Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c84f6a3988 |
19
src/main/kotlin/space/luminic/finance/api/GoalController.kt
Normal file
19
src/main/kotlin/space/luminic/finance/api/GoalController.kt
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package space.luminic.finance.api
|
||||||
|
|
||||||
|
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 space.luminic.finance.dtos.GoalDTO
|
||||||
|
import space.luminic.finance.mappers.GoalMapper.toDto
|
||||||
|
import space.luminic.finance.services.GoalService
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/spaces/{spaceId}/goals")
|
||||||
|
class GoalController(private val goalService: GoalService) {
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
fun findAll(@PathVariable spaceId: Int): List<GoalDTO> {
|
||||||
|
return goalService.findAllBySpaceId(spaceId).map { it.toDto() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package space.luminic.finance.api
|
|
||||||
|
|
||||||
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 space.luminic.finance.dtos.TargetDTO
|
|
||||||
import space.luminic.finance.mappers.TargetMapper.toDto
|
|
||||||
import space.luminic.finance.services.TargetService
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/spaces/{spaceId}/targets")
|
|
||||||
class TargetController(private val targetService: TargetService) {
|
|
||||||
|
|
||||||
@GetMapping
|
|
||||||
fun findAll(@PathVariable spaceId: Int): List<TargetDTO> {
|
|
||||||
return targetService.findAllBySpaceId(spaceId).map { it.toDto() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +1,36 @@
|
|||||||
package space.luminic.finance.dtos
|
package space.luminic.finance.dtos
|
||||||
|
|
||||||
import space.luminic.finance.models.Target
|
import space.luminic.finance.models.Goal
|
||||||
import space.luminic.finance.models.Target.TargetType
|
import space.luminic.finance.models.Goal.GoalType
|
||||||
import space.luminic.finance.models.Transaction
|
import space.luminic.finance.models.Transaction
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
data class TargetDTO(
|
data class GoalDTO(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val type: TargetType,
|
val type: GoalType,
|
||||||
val name: String,
|
val name: String,
|
||||||
val description: String? = null,
|
val description: String? = null,
|
||||||
val amount: BigDecimal,
|
val amount: BigDecimal,
|
||||||
val currentAmount: BigDecimal,
|
|
||||||
val date: LocalDate,
|
val date: LocalDate,
|
||||||
val components: List<Target.TargetComponent>,
|
val components: List<Goal.GoalComponent>,
|
||||||
val transactions: List<Transaction>,
|
val transactions: List<Transaction>,
|
||||||
val createdBy: UserDTO,
|
val createdBy: UserDTO,
|
||||||
val createdAt: Instant,
|
val createdAt: Instant,
|
||||||
val updatedBy: UserDTO? = null,
|
val updatedBy: UserDTO? = null,
|
||||||
val updatedAt: Instant? = null,
|
val updatedAt: Instant? = null,
|
||||||
) {
|
) {
|
||||||
data class CreateTargetDTO(
|
data class CreateGoalDTO(
|
||||||
val type: TargetType,
|
val type: GoalType,
|
||||||
val name: String,
|
val name: String,
|
||||||
val description: String?,
|
val description: String?,
|
||||||
val amount: BigDecimal,
|
val amount: BigDecimal,
|
||||||
val date: LocalDate
|
val date: LocalDate
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UpdateTargetDTO(
|
data class UpdateGoalDTO(
|
||||||
val type: TargetType,
|
val type: GoalType,
|
||||||
val name: String,
|
val name: String,
|
||||||
val description: String?,
|
val description: String?,
|
||||||
val amount: BigDecimal,
|
val amount: BigDecimal,
|
||||||
@@ -1,26 +1,22 @@
|
|||||||
package space.luminic.finance.mappers
|
package space.luminic.finance.mappers
|
||||||
|
|
||||||
import space.luminic.finance.dtos.TargetDTO
|
import space.luminic.finance.dtos.GoalDTO
|
||||||
import space.luminic.finance.mappers.UserMapper.toDto
|
import space.luminic.finance.mappers.UserMapper.toDto
|
||||||
import space.luminic.finance.models.Target
|
import space.luminic.finance.models.Goal
|
||||||
|
|
||||||
object TargetMapper {
|
object GoalMapper {
|
||||||
|
|
||||||
fun Target.toDto() = TargetDTO(
|
fun Goal.toDto() = GoalDTO(
|
||||||
id = this.id ?: throw IllegalArgumentException("Target id is not provided"),
|
id = this.id ?: throw IllegalArgumentException("Goal id is not provided"),
|
||||||
type = this.type,
|
type = this.type,
|
||||||
name = this.name,
|
name = this.name,
|
||||||
description = this.description,
|
|
||||||
amount = this.amount,
|
amount = this.amount,
|
||||||
currentAmount = this.currentAmount,
|
|
||||||
date = this.untilDate,
|
date = this.untilDate,
|
||||||
components = this.components,
|
components = this.components,
|
||||||
transactions = this.transactions,
|
transactions = this.transactions,
|
||||||
createdBy = (this.createdBy ?: throw IllegalArgumentException("created by not provided")).toDto(),
|
createdBy = (this.createdBy ?: throw IllegalArgumentException("created by not provided")).toDto(),
|
||||||
createdAt = this.createdAt ?: throw IllegalArgumentException("created at not provided"),
|
createdAt = this.createdAt ?: throw IllegalArgumentException("created at not provided"),
|
||||||
updatedBy = this.updatedBy?.toDto(),
|
updatedBy = this.updatedBy?.toDto() ,
|
||||||
updatedAt = this.updatedAt,
|
updatedAt = this.updatedAt
|
||||||
|
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -8,30 +8,30 @@ import java.math.BigDecimal
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
data class Target(
|
data class Goal(
|
||||||
var id: Int? = null,
|
var id: Int? = null,
|
||||||
val space: Space? = null,
|
val space: Space? = null,
|
||||||
val type: TargetType,
|
val type: GoalType,
|
||||||
val name: String,
|
val name: String,
|
||||||
val description: String? = null,
|
val description: String? = null,
|
||||||
val amount: BigDecimal,
|
val amount: BigDecimal,
|
||||||
val components: List<TargetComponent> = emptyList(),
|
val components: List<GoalComponent> = emptyList(),
|
||||||
val transactions: List<Transaction> = emptyList(),
|
val transactions: List<Transaction> = emptyList(),
|
||||||
val untilDate: LocalDate,
|
val untilDate: LocalDate,
|
||||||
var createdBy: User? = null,
|
@CreatedBy var createdBy: User? = null,
|
||||||
|
|
||||||
var createdAt: Instant? = null,
|
@CreatedDate var createdAt: Instant? = null,
|
||||||
var updatedBy: User? = null,
|
@LastModifiedBy var updatedBy: User? = null,
|
||||||
|
|
||||||
var updatedAt: Instant? = null,
|
@LastModifiedDate var updatedAt: Instant? = null,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
var currentAmount: BigDecimal = {
|
var currentAmount: BigDecimal = {
|
||||||
this.transactions.sumOf { it.amount }
|
this.transactions.sumOf { it.amount }
|
||||||
} as BigDecimal
|
} as BigDecimal
|
||||||
|
|
||||||
|
|
||||||
data class TargetComponent(
|
data class GoalComponent(
|
||||||
val id: Int? = null,
|
val id: Int? = null,
|
||||||
val name: String,
|
val name: String,
|
||||||
val amount: BigDecimal,
|
val amount: BigDecimal,
|
||||||
@@ -39,9 +39,8 @@ data class Target(
|
|||||||
val date: LocalDate = LocalDate.now(),
|
val date: LocalDate = LocalDate.now(),
|
||||||
)
|
)
|
||||||
|
|
||||||
enum class TargetType(val displayName: String, val icon: String) {
|
enum class GoalType(val displayName: String, val icon: String) {
|
||||||
AUTO("Авто", "🏎️"),
|
AUTO("Авто", "🏎️"),
|
||||||
LEISURE("Досуг", "💃"),
|
|
||||||
VACATION("Отпуск", "🏖️"),
|
VACATION("Отпуск", "🏖️"),
|
||||||
GOODS("Покупка", "🛍️"),
|
GOODS("Покупка", "🛍️"),
|
||||||
OTHER("Прочее", "💸")
|
OTHER("Прочее", "💸")
|
||||||
20
src/main/kotlin/space/luminic/finance/repos/GoalRepo.kt
Normal file
20
src/main/kotlin/space/luminic/finance/repos/GoalRepo.kt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package space.luminic.finance.repos
|
||||||
|
|
||||||
|
import space.luminic.finance.models.Goal
|
||||||
|
|
||||||
|
interface GoalRepo {
|
||||||
|
|
||||||
|
fun findAllBySpaceId(spaceId: Int) : List<Goal>
|
||||||
|
fun findBySpaceIdAndId(spaceId: Int, id: Int) : Goal?
|
||||||
|
fun create(goal: Goal, createdById: Int): Int
|
||||||
|
fun update(goal: Goal, updatedById: Int)
|
||||||
|
fun delete(spaceId: Int, id: Int)
|
||||||
|
fun getComponents(spaceId: Int, goalId: Int): List<Goal.GoalComponent>
|
||||||
|
fun getComponent(spaceId: Int, goalId: Int, id: Int): Goal.GoalComponent?
|
||||||
|
fun createComponent(goalId: Int, component: Goal.GoalComponent, createdById: Int): Int
|
||||||
|
fun updateComponent(goalId: Int, componentId: Int, component: Goal.GoalComponent, updatedById: Int)
|
||||||
|
fun deleteComponent(goalId: Int, componentId: Int)
|
||||||
|
|
||||||
|
fun assignTransaction(goalId: Int, transactionId: Int)
|
||||||
|
fun refuseTransaction(goalId: Int, transactionId: Int)
|
||||||
|
}
|
||||||
@@ -3,17 +3,17 @@ package space.luminic.finance.repos
|
|||||||
import org.springframework.jdbc.core.RowMapper
|
import org.springframework.jdbc.core.RowMapper
|
||||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
|
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
import space.luminic.finance.models.Target
|
import space.luminic.finance.models.Goal
|
||||||
import space.luminic.finance.models.User
|
import space.luminic.finance.models.User
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
class TargetRepoImpl(
|
class GoalRepoImpl(
|
||||||
private val jdbcTemplate: NamedParameterJdbcTemplate
|
private val jdbcTemplate: NamedParameterJdbcTemplate
|
||||||
) : TargetRepo {
|
) : GoalRepo {
|
||||||
private val targetRowMapper = RowMapper { rs, _ ->
|
private val goalRowMapper = RowMapper { rs, _ ->
|
||||||
Target(
|
Goal(
|
||||||
id = rs.getInt("g_id"),
|
id = rs.getInt("g_id"),
|
||||||
type = Target.TargetType.valueOf(rs.getString("g_type")),
|
type = Goal.GoalType.valueOf(rs.getString("g_type")),
|
||||||
name = rs.getString("g_name"),
|
name = rs.getString("g_name"),
|
||||||
description = rs.getString("g_description"),
|
description = rs.getString("g_description"),
|
||||||
amount = rs.getBigDecimal("g_amount"),
|
amount = rs.getBigDecimal("g_amount"),
|
||||||
@@ -30,7 +30,7 @@ class TargetRepoImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val componentRowMapper = RowMapper { rs, _ ->
|
private val componentRowMapper = RowMapper { rs, _ ->
|
||||||
Target.TargetComponent(
|
Goal.GoalComponent(
|
||||||
id = rs.getInt("gc_id"),
|
id = rs.getInt("gc_id"),
|
||||||
name = rs.getString("gc_name"),
|
name = rs.getString("gc_name"),
|
||||||
amount = rs.getBigDecimal("gc_amount"),
|
amount = rs.getBigDecimal("gc_amount"),
|
||||||
@@ -39,7 +39,7 @@ class TargetRepoImpl(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findAllBySpaceId(spaceId: Int): List<Target> {
|
override fun findAllBySpaceId(spaceId: Int): List<Goal> {
|
||||||
val sql = """
|
val sql = """
|
||||||
select
|
select
|
||||||
g.id as g_id,
|
g.id as g_id,
|
||||||
@@ -51,7 +51,7 @@ class TargetRepoImpl(
|
|||||||
created_by.username as created_by_username,
|
created_by.username as created_by_username,
|
||||||
created_by.first_name as created_by_first_name,
|
created_by.first_name as created_by_first_name,
|
||||||
g.created_at as g_created_at
|
g.created_at as g_created_at
|
||||||
from finance.targets g
|
from finance.goals g
|
||||||
join finance.users created_by on g.created_by_id = created_by.id
|
join finance.users created_by on g.created_by_id = created_by.id
|
||||||
where g.space_id = :spaceId
|
where g.space_id = :spaceId
|
||||||
|
|
||||||
@@ -60,10 +60,10 @@ class TargetRepoImpl(
|
|||||||
val params = mapOf(
|
val params = mapOf(
|
||||||
"space_id" to spaceId,
|
"space_id" to spaceId,
|
||||||
)
|
)
|
||||||
return jdbcTemplate.query(sql, params, targetRowMapper)
|
return jdbcTemplate.query(sql, params, goalRowMapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findBySpaceIdAndId(spaceId: Int, id: Int): Target? {
|
override fun findBySpaceIdAndId(spaceId: Int, id: Int): Goal? {
|
||||||
val sql = """
|
val sql = """
|
||||||
select
|
select
|
||||||
g.id as g_id,
|
g.id as g_id,
|
||||||
@@ -75,7 +75,7 @@ class TargetRepoImpl(
|
|||||||
created_by.username as created_by_username,
|
created_by.username as created_by_username,
|
||||||
created_by.first_name as created_by_first_name,
|
created_by.first_name as created_by_first_name,
|
||||||
g.created_at as g_created_at
|
g.created_at as g_created_at
|
||||||
from finance.targets g
|
from finance.goals g
|
||||||
join finance.users created_by on g.created_by_id = created_by.id
|
join finance.users created_by on g.created_by_id = created_by.id
|
||||||
where g.space_id = :spaceId and g.id = :id
|
where g.space_id = :spaceId and g.id = :id
|
||||||
|
|
||||||
@@ -85,12 +85,12 @@ class TargetRepoImpl(
|
|||||||
"space_id" to spaceId,
|
"space_id" to spaceId,
|
||||||
"id" to id,
|
"id" to id,
|
||||||
)
|
)
|
||||||
return jdbcTemplate.query(sql, params, targetRowMapper).firstOrNull()
|
return jdbcTemplate.query(sql, params, goalRowMapper).firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun create(target: Target, createdById: Int): Int {
|
override fun create(goal: Goal, createdById: Int): Int {
|
||||||
val sql = """
|
val sql = """
|
||||||
insert into finance.targets(
|
insert into finance.goals(
|
||||||
type,
|
type,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
@@ -108,19 +108,19 @@ class TargetRepoImpl(
|
|||||||
returning id
|
returning id
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
val params = mapOf(
|
val params = mapOf(
|
||||||
"type" to target.type,
|
"type" to goal.type,
|
||||||
"name" to target.name,
|
"name" to goal.name,
|
||||||
"description" to target.description,
|
"description" to goal.description,
|
||||||
"amount" to target.amount,
|
"amount" to goal.amount,
|
||||||
"until_date" to target.untilDate,
|
"until_date" to goal.untilDate,
|
||||||
"created_by_id" to createdById
|
"created_by_id" to createdById
|
||||||
)
|
)
|
||||||
return jdbcTemplate.queryForObject(sql, params, Int::class.java)!!
|
return jdbcTemplate.queryForObject(sql, params, Int::class.java)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun update(target: Target, updatedById: Int) {
|
override fun update(goal: Goal, updatedById: Int) {
|
||||||
val sql = """
|
val sql = """
|
||||||
update finance.targets set
|
update finance.goals set
|
||||||
type = :type,
|
type = :type,
|
||||||
name = :name,
|
name = :name,
|
||||||
description = :description,
|
description = :description,
|
||||||
@@ -131,12 +131,12 @@ class TargetRepoImpl(
|
|||||||
where id = :id
|
where id = :id
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
val params = mapOf(
|
val params = mapOf(
|
||||||
"id" to target.id,
|
"id" to goal.id,
|
||||||
"type" to target.type.name,
|
"type" to goal.type.name,
|
||||||
"name" to target.name,
|
"name" to goal.name,
|
||||||
"description" to target.description,
|
"description" to goal.description,
|
||||||
"amount" to target.amount,
|
"amount" to goal.amount,
|
||||||
"until_date" to target.untilDate,
|
"until_date" to goal.untilDate,
|
||||||
"updated_by_id" to updatedById
|
"updated_by_id" to updatedById
|
||||||
|
|
||||||
)
|
)
|
||||||
@@ -145,7 +145,7 @@ class TargetRepoImpl(
|
|||||||
|
|
||||||
override fun delete(spaceId: Int, id: Int) {
|
override fun delete(spaceId: Int, id: Int) {
|
||||||
val sql = """
|
val sql = """
|
||||||
delete from finance.targets where id = :id
|
delete from finance.goals where id = :id
|
||||||
|
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
val params = mapOf(
|
val params = mapOf(
|
||||||
@@ -156,8 +156,8 @@ class TargetRepoImpl(
|
|||||||
|
|
||||||
override fun getComponents(
|
override fun getComponents(
|
||||||
spaceId: Int,
|
spaceId: Int,
|
||||||
targetId: Int
|
goalId: Int
|
||||||
): List<Target.TargetComponent> {
|
): List<Goal.GoalComponent> {
|
||||||
val sql = """
|
val sql = """
|
||||||
select
|
select
|
||||||
gc.id as gc_id,
|
gc.id as gc_id,
|
||||||
@@ -165,11 +165,11 @@ class TargetRepoImpl(
|
|||||||
gc.amount as gc_amount,
|
gc.amount as gc_amount,
|
||||||
gc.is_done as gc_is_done,
|
gc.is_done as gc_is_done,
|
||||||
gc.date as gc_date
|
gc.date as gc_date
|
||||||
from finance.targets_components gc
|
from finance.goals_components gc
|
||||||
where gc.target_id = :target_id
|
where gc.goal_id = :goal_id
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
val params = mapOf(
|
val params = mapOf(
|
||||||
"target_id" to targetId
|
"goal_id" to goalId
|
||||||
)
|
)
|
||||||
return jdbcTemplate.query(sql, params, componentRowMapper)
|
return jdbcTemplate.query(sql, params, componentRowMapper)
|
||||||
|
|
||||||
@@ -177,9 +177,9 @@ class TargetRepoImpl(
|
|||||||
|
|
||||||
override fun getComponent(
|
override fun getComponent(
|
||||||
spaceId: Int,
|
spaceId: Int,
|
||||||
targetId: Int,
|
goalId: Int,
|
||||||
id: Int
|
id: Int
|
||||||
): Target.TargetComponent? {
|
): Goal.GoalComponent? {
|
||||||
val sql = """
|
val sql = """
|
||||||
select
|
select
|
||||||
gc.id as gc_id,
|
gc.id as gc_id,
|
||||||
@@ -187,27 +187,27 @@ class TargetRepoImpl(
|
|||||||
gc.amount as gc_amount,
|
gc.amount as gc_amount,
|
||||||
gc.is_done as gc_is_done,
|
gc.is_done as gc_is_done,
|
||||||
gc.date as gc_date
|
gc.date as gc_date
|
||||||
from finance.targets_components gc
|
from finance.goals_components gc
|
||||||
where gc.target_id = :target_id and gc.id = :id
|
where gc.goal_id = :goal_id and gc.id = :id
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
val params = mapOf(
|
val params = mapOf(
|
||||||
"target_id" to targetId,
|
"goal_id" to goalId,
|
||||||
"id" to id
|
"id" to id
|
||||||
)
|
)
|
||||||
return jdbcTemplate.query(sql, params, componentRowMapper).firstOrNull()
|
return jdbcTemplate.query(sql, params, componentRowMapper).firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createComponent(targetId: Int, component: Target.TargetComponent, createdById: Int): Int {
|
override fun createComponent(goalId: Int, component: Goal.GoalComponent, createdById: Int): Int {
|
||||||
val sql = """
|
val sql = """
|
||||||
insert into finance.targets_components(
|
insert into finance.goals_components(
|
||||||
target_id,
|
goal_id,
|
||||||
name,
|
name,
|
||||||
amount,
|
amount,
|
||||||
is_done,
|
is_done,
|
||||||
date,
|
date,
|
||||||
created_by_id
|
created_by_id
|
||||||
) values (
|
) values (
|
||||||
:target_id,
|
:goal_id,
|
||||||
:name,
|
:name,
|
||||||
:amount,
|
:amount,
|
||||||
:is_done,
|
:is_done,
|
||||||
@@ -216,7 +216,7 @@ class TargetRepoImpl(
|
|||||||
returning id
|
returning id
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
val params = mapOf(
|
val params = mapOf(
|
||||||
"target_id" to targetId,
|
"goal_id" to goalId,
|
||||||
"name" to component.name,
|
"name" to component.name,
|
||||||
"amount" to component.amount,
|
"amount" to component.amount,
|
||||||
"is_done" to component.isDone,
|
"is_done" to component.isDone,
|
||||||
@@ -226,17 +226,17 @@ class TargetRepoImpl(
|
|||||||
return jdbcTemplate.queryForObject(sql, params, Int::class.java)!!
|
return jdbcTemplate.queryForObject(sql, params, Int::class.java)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateComponent(targetId: Int, componentId: Int, component: Target.TargetComponent, updatedById: Int) {
|
override fun updateComponent(goalId: Int, componentId: Int, component: Goal.GoalComponent, updatedById: Int) {
|
||||||
val sql = """
|
val sql = """
|
||||||
update finance.targets_components set
|
update finance.goals_components set
|
||||||
name = :name,
|
name = :name,
|
||||||
amount = :amount,
|
amount = :amount,
|
||||||
is_done = :is_done,
|
is_done = :is_done,
|
||||||
updated_by_id = :updated_by_id
|
updated_by_id = :updated_by_id
|
||||||
where target_id = :targetId and id = :componentId
|
where goal_id = :goalId and id = :componentId
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
val params = mapOf(
|
val params = mapOf(
|
||||||
"targetId" to targetId,
|
"goalId" to goalId,
|
||||||
"componentId" to componentId,
|
"componentId" to componentId,
|
||||||
"name" to component.name,
|
"name" to component.name,
|
||||||
"amount" to component.amount,
|
"amount" to component.amount,
|
||||||
@@ -247,35 +247,35 @@ class TargetRepoImpl(
|
|||||||
jdbcTemplate.update(sql, params)
|
jdbcTemplate.update(sql, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteComponent(targetId: Int, componentId: Int) {
|
override fun deleteComponent(goalId: Int, componentId: Int) {
|
||||||
val sql = """
|
val sql = """
|
||||||
delete from finance.targets_components where target_id = :targetId and id = :componentId
|
delete from finance.goals_components where goal_id = :goalId and id = :componentId
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
val params = mapOf(
|
val params = mapOf(
|
||||||
"targetId" to targetId,
|
"goalId" to goalId,
|
||||||
"componentId" to componentId
|
"componentId" to componentId
|
||||||
)
|
)
|
||||||
jdbcTemplate.update(sql, params)
|
jdbcTemplate.update(sql, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun assignTransaction(targetId: Int, transactionId: Int) {
|
override fun assignTransaction(goalId: Int, transactionId: Int) {
|
||||||
val sql = """
|
val sql = """
|
||||||
insert into finance.targets_transactions(target_id, transactions_id)
|
insert into finance.goals_transactions(goal_id, transactions_id)
|
||||||
values (:targetId, :transaction_id)
|
values (:goal_id, :transaction_id)
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
val params = mapOf(
|
val params = mapOf(
|
||||||
"targetId" to targetId,
|
"goal_id" to goalId,
|
||||||
"transaction_id" to transactionId
|
"transaction_id" to transactionId
|
||||||
)
|
)
|
||||||
jdbcTemplate.update(sql, params)
|
jdbcTemplate.update(sql, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun refuseTransaction(targetId: Int, transactionId: Int) {
|
override fun refuseTransaction(goalId: Int, transactionId: Int) {
|
||||||
val sql = """
|
val sql = """
|
||||||
delete from finance.targets_transactions where target_id = :goalId and transactions_id = :transactionId
|
delete from finance.goals_transactions where goal_id = :goalId and transactions_id = :transactionId
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
val params = mapOf(
|
val params = mapOf(
|
||||||
"target_id" to targetId,
|
"goal_id" to goalId,
|
||||||
"transaction_id" to transactionId
|
"transaction_id" to transactionId
|
||||||
)
|
)
|
||||||
jdbcTemplate.update(sql, params)
|
jdbcTemplate.update(sql, params)
|
||||||
@@ -38,7 +38,8 @@ class SpaceRepoImpl(
|
|||||||
owner = User(
|
owner = User(
|
||||||
rs.getInt("s_owner_id"),
|
rs.getInt("s_owner_id"),
|
||||||
rs.getString("s_owner_username"),
|
rs.getString("s_owner_username"),
|
||||||
rs.getString("s_owner_firstname")
|
rs.getString("s_owner_firstname"),
|
||||||
|
tgId = rs.getLong("s_owner_tg_id"),
|
||||||
),
|
),
|
||||||
participant = User(rs.getInt("sp_uid"), rs.getString("sp_username"), rs.getString("sp_first_name")),
|
participant = User(rs.getInt("sp_uid"), rs.getString("sp_username"), rs.getString("sp_first_name")),
|
||||||
createdAt = rs.getTimestamp("s_created_at").toInstant(),
|
createdAt = rs.getTimestamp("s_created_at").toInstant(),
|
||||||
@@ -93,6 +94,7 @@ class SpaceRepoImpl(
|
|||||||
s.owner_id as s_owner_id,
|
s.owner_id as s_owner_id,
|
||||||
ou.username as s_owner_username,
|
ou.username as s_owner_username,
|
||||||
ou.first_name as s_owner_firstname,
|
ou.first_name as s_owner_firstname,
|
||||||
|
ou.tg_id as s_owner_tg_id,
|
||||||
sp.participants_id as sp_uid,
|
sp.participants_id as sp_uid,
|
||||||
u.username as sp_username,
|
u.username as sp_username,
|
||||||
u.first_name as sp_first_name,
|
u.first_name as sp_first_name,
|
||||||
@@ -114,7 +116,7 @@ class SpaceRepoImpl(
|
|||||||
where (s.owner_id = :user_id
|
where (s.owner_id = :user_id
|
||||||
or sp.participants_id = :user_id)
|
or sp.participants_id = :user_id)
|
||||||
and s.is_deleted = false
|
and s.is_deleted = false
|
||||||
group by s.id, ou.username, ou.first_name, sp.participants_id, u.username, u.first_name, cau.username, cau.first_name,
|
group by s.id, ou.username, ou.first_name, ou.tg_id, sp.participants_id, u.username, u.first_name, cau.username, cau.first_name,
|
||||||
uau.username, uau.first_name;
|
uau.username, uau.first_name;
|
||||||
""".trimMargin()
|
""".trimMargin()
|
||||||
val params = mapOf(
|
val params = mapOf(
|
||||||
@@ -133,6 +135,7 @@ class SpaceRepoImpl(
|
|||||||
s.owner_id as s_owner_id,
|
s.owner_id as s_owner_id,
|
||||||
ou.username as s_owner_username,
|
ou.username as s_owner_username,
|
||||||
ou.first_name as s_owner_firstname,
|
ou.first_name as s_owner_firstname,
|
||||||
|
ou.tg_id as s_owner_tg_id,
|
||||||
sp.participants_id as sp_uid,
|
sp.participants_id as sp_uid,
|
||||||
u.username as sp_username,
|
u.username as sp_username,
|
||||||
u.first_name as sp_first_name,
|
u.first_name as sp_first_name,
|
||||||
@@ -154,7 +157,7 @@ from finance.spaces s
|
|||||||
where (s.owner_id = :user_id
|
where (s.owner_id = :user_id
|
||||||
or sp.participants_id = :user_id)
|
or sp.participants_id = :user_id)
|
||||||
and s.is_deleted = false and s.id = :spaceId
|
and s.is_deleted = false and s.id = :spaceId
|
||||||
group by s.id, ou.username, ou.first_name, sp.participants_id, u.username, u.first_name, cau.username, cau.first_name,
|
group by s.id, ou.username, ou.first_name, ou.tg_id, sp.participants_id, u.username, u.first_name, cau.username, cau.first_name,
|
||||||
uau.username, uau.first_name;
|
uau.username, uau.first_name;
|
||||||
""".trimMargin()
|
""".trimMargin()
|
||||||
val params = mapOf(
|
val params = mapOf(
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
package space.luminic.finance.repos
|
|
||||||
|
|
||||||
import space.luminic.finance.models.Target
|
|
||||||
|
|
||||||
interface TargetRepo {
|
|
||||||
|
|
||||||
fun findAllBySpaceId(spaceId: Int) : List<Target>
|
|
||||||
fun findBySpaceIdAndId(spaceId: Int, id: Int) : Target?
|
|
||||||
fun create(target: Target, createdById: Int): Int
|
|
||||||
fun update(target: Target, updatedById: Int)
|
|
||||||
fun delete(spaceId: Int, id: Int)
|
|
||||||
fun getComponents(spaceId: Int, targetId: Int): List<Target.TargetComponent>
|
|
||||||
fun getComponent(spaceId: Int, targetId: Int, id: Int): Target.TargetComponent?
|
|
||||||
fun createComponent(targetId: Int, component: Target.TargetComponent, createdById: Int): Int
|
|
||||||
fun updateComponent(targetId: Int, componentId: Int, component: Target.TargetComponent, updatedById: Int)
|
|
||||||
fun deleteComponent(targetId: Int, componentId: Int)
|
|
||||||
|
|
||||||
fun assignTransaction(targetId: Int, transactionId: Int)
|
|
||||||
fun refuseTransaction(targetId: Int, transactionId: Int)
|
|
||||||
}
|
|
||||||
@@ -7,8 +7,6 @@ import space.luminic.finance.models.Category
|
|||||||
import space.luminic.finance.models.Transaction
|
import space.luminic.finance.models.Transaction
|
||||||
import space.luminic.finance.models.User
|
import space.luminic.finance.models.User
|
||||||
import space.luminic.finance.services.TransactionService
|
import space.luminic.finance.services.TransactionService
|
||||||
import java.time.LocalDate
|
|
||||||
import java.time.LocalDateTime
|
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
class TransactionRepoImpl(
|
class TransactionRepoImpl(
|
||||||
@@ -111,9 +109,6 @@ class TransactionRepoImpl(
|
|||||||
filters.dateFrom?.let {
|
filters.dateFrom?.let {
|
||||||
sql += " AND t.date >= :dateFrom"
|
sql += " AND t.date >= :dateFrom"
|
||||||
params.put("dateFrom", it)
|
params.put("dateFrom", it)
|
||||||
} ?: {
|
|
||||||
sql += " AND t.date >= :dateFrom"
|
|
||||||
params.put("dateFrom", LocalDate.now().minusMonths(1))
|
|
||||||
}
|
}
|
||||||
filters.dateTo?.let {
|
filters.dateTo?.let {
|
||||||
sql += " AND t.date <= :dateTo"
|
sql += " AND t.date <= :dateTo"
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package space.luminic.finance.services
|
||||||
|
|
||||||
|
import space.luminic.finance.dtos.GoalDTO
|
||||||
|
import space.luminic.finance.models.Goal
|
||||||
|
|
||||||
|
interface GoalService {
|
||||||
|
fun findAllBySpaceId(spaceId: Int): List<Goal>
|
||||||
|
fun findBySpaceIdAndId(spaceId: Int, id: Int): Goal
|
||||||
|
fun create(spaceId: Int,goal: GoalDTO.CreateGoalDTO): Int
|
||||||
|
fun update(spaceId: Int, goalId: Int, goal: GoalDTO.UpdateGoalDTO)
|
||||||
|
fun delete(spaceId: Int, id: Int)
|
||||||
|
|
||||||
|
fun getComponents(spaceId: Int, goalId: Int): List<Goal.GoalComponent>
|
||||||
|
fun getComponent(spaceId: Int, goalId: Int, id: Int): Goal.GoalComponent?
|
||||||
|
fun createComponent(spaceId: Int, goalId: Int, component: Goal.GoalComponent): Int
|
||||||
|
fun updateComponent(spaceId: Int, goalId: Int, component: Goal.GoalComponent)
|
||||||
|
fun deleteComponent(spaceId: Int, goalId: Int, id: Int)
|
||||||
|
|
||||||
|
fun assignTransaction(spaceId: Int, goalId: Int, transactionId: Int)
|
||||||
|
fun refuseTransaction(spaceId: Int,goalId: Int, transactionId: Int)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package space.luminic.finance.services
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import space.luminic.finance.dtos.GoalDTO
|
||||||
|
import space.luminic.finance.models.Goal
|
||||||
|
import space.luminic.finance.models.NotFoundException
|
||||||
|
import space.luminic.finance.repos.GoalRepo
|
||||||
|
import space.luminic.finance.repos.SpaceRepo
|
||||||
|
import space.luminic.finance.repos.TransactionRepo
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class GoalServiceImpl(
|
||||||
|
private val goalRepo: GoalRepo,
|
||||||
|
private val spaceRepo: SpaceRepo,
|
||||||
|
private val authService: AuthService,
|
||||||
|
private val transactionRepo: TransactionRepo
|
||||||
|
) : GoalService {
|
||||||
|
override fun findAllBySpaceId(spaceId: Int): List<Goal> {
|
||||||
|
val userId = authService.getSecurityUserId()
|
||||||
|
spaceRepo.findSpaceById(spaceId, userId)
|
||||||
|
return goalRepo.findAllBySpaceId(spaceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findBySpaceIdAndId(spaceId: Int, id: Int): Goal {
|
||||||
|
val userId = authService.getSecurityUserId()
|
||||||
|
spaceRepo.findSpaceById(spaceId, userId)
|
||||||
|
return goalRepo.findBySpaceIdAndId(spaceId, userId) ?: throw NotFoundException("Goal $id not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun create(spaceId: Int, goal: GoalDTO.CreateGoalDTO): Int {
|
||||||
|
val userId = authService.getSecurityUserId()
|
||||||
|
spaceRepo.findSpaceById(spaceId, userId)
|
||||||
|
val creatingGoal = Goal(
|
||||||
|
type = goal.type,
|
||||||
|
name = goal.name,
|
||||||
|
amount = goal.amount,
|
||||||
|
untilDate = goal.date
|
||||||
|
)
|
||||||
|
return goalRepo.create(creatingGoal, userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun update(spaceId: Int, goalId: Int, goal: GoalDTO.UpdateGoalDTO) {
|
||||||
|
val userId = authService.getSecurityUserId()
|
||||||
|
spaceRepo.findSpaceById(spaceId, userId)
|
||||||
|
val existingGoal =
|
||||||
|
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||||
|
val updatedGoal = existingGoal.copy(
|
||||||
|
type = goal.type,
|
||||||
|
name = goal.name,
|
||||||
|
description = goal.description,
|
||||||
|
amount = goal.amount,
|
||||||
|
untilDate = goal.date
|
||||||
|
)
|
||||||
|
goalRepo.update(updatedGoal, userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun delete(spaceId: Int, id: Int) {
|
||||||
|
goalRepo.delete(spaceId, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getComponents(
|
||||||
|
spaceId: Int,
|
||||||
|
goalId: Int
|
||||||
|
): List<Goal.GoalComponent> {
|
||||||
|
val userId = authService.getSecurityUserId()
|
||||||
|
spaceRepo.findSpaceById(spaceId, userId)
|
||||||
|
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||||
|
return goalRepo.getComponents(spaceId, goalId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getComponent(
|
||||||
|
spaceId: Int,
|
||||||
|
goalId: Int,
|
||||||
|
id: Int
|
||||||
|
): Goal.GoalComponent? {
|
||||||
|
val userId = authService.getSecurityUserId()
|
||||||
|
spaceRepo.findSpaceById(spaceId, userId)
|
||||||
|
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||||
|
return goalRepo.getComponent(spaceId, goalId, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createComponent(
|
||||||
|
spaceId: Int,
|
||||||
|
goalId: Int,
|
||||||
|
component: Goal.GoalComponent
|
||||||
|
): Int {
|
||||||
|
val userId = authService.getSecurityUserId()
|
||||||
|
spaceRepo.findSpaceById(spaceId, userId)
|
||||||
|
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||||
|
return goalRepo.createComponent(goalId, component, userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateComponent(
|
||||||
|
spaceId: Int,
|
||||||
|
goalId: Int,
|
||||||
|
component: Goal.GoalComponent
|
||||||
|
) {
|
||||||
|
val userId = authService.getSecurityUserId()
|
||||||
|
spaceRepo.findSpaceById(spaceId, userId)
|
||||||
|
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||||
|
val existingComponent = goalRepo.getComponent(spaceId, goalId, component.id!!)
|
||||||
|
?: throw NotFoundException("Component $goalId not found")
|
||||||
|
val updatedComponent = existingComponent.copy(
|
||||||
|
name = component.name,
|
||||||
|
amount = component.amount,
|
||||||
|
isDone = component.isDone,
|
||||||
|
date = component.date
|
||||||
|
)
|
||||||
|
goalRepo.updateComponent(goalId, updatedComponent.id!!, updatedComponent, userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deleteComponent(spaceId: Int, goalId: Int, id: Int) {
|
||||||
|
val userId = authService.getSecurityUserId()
|
||||||
|
spaceRepo.findSpaceById(spaceId, userId)
|
||||||
|
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||||
|
goalRepo.getComponent(spaceId, goalId, id) ?: throw NotFoundException("Component $goalId not found")
|
||||||
|
goalRepo.deleteComponent(goalId, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun assignTransaction(spaceId: Int, goalId: Int, transactionId: Int) {
|
||||||
|
val userId = authService.getSecurityUserId()
|
||||||
|
spaceRepo.findSpaceById(spaceId, userId)
|
||||||
|
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||||
|
transactionRepo.findBySpaceIdAndId(spaceId, transactionId) ?: throw NotFoundException(
|
||||||
|
"Transaction $transactionId not found"
|
||||||
|
)
|
||||||
|
goalRepo.assignTransaction(goalId, transactionId)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun refuseTransaction(spaceId: Int, goalId: Int, transactionId: Int) {
|
||||||
|
val userId = authService.getSecurityUserId()
|
||||||
|
spaceRepo.findSpaceById(spaceId, userId)
|
||||||
|
goalRepo.findBySpaceIdAndId(spaceId, goalId) ?: throw NotFoundException("Goal $goalId not found")
|
||||||
|
transactionRepo.findBySpaceIdAndId(spaceId, transactionId) ?: throw NotFoundException(
|
||||||
|
"Transaction $transactionId not found"
|
||||||
|
)
|
||||||
|
goalRepo.refuseTransaction(goalId, transactionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package space.luminic.finance.services
|
||||||
|
|
||||||
|
import com.github.kotlintelegrambot.entities.ReplyMarkup
|
||||||
|
import com.github.kotlintelegrambot.entities.inputmedia.MediaGroup
|
||||||
|
import space.luminic.finance.models.Space
|
||||||
|
import space.luminic.finance.models.Transaction
|
||||||
|
|
||||||
|
interface NotificationService {
|
||||||
|
fun sendDailyReminder()
|
||||||
|
fun sendTXNotification(action: TxActionType, space: Space, userId: Int, tx: Transaction, tx2: Transaction? = null)
|
||||||
|
fun sendTextMessage(chatId: Long, message: String, replyMarkup: ReplyMarkup? = null)
|
||||||
|
fun sendMediaGroup(chatId: Long, group: MediaGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class TxActionType {
|
||||||
|
CREATE,
|
||||||
|
UPDATE,
|
||||||
|
DELETE,
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
package space.luminic.finance.services
|
||||||
|
|
||||||
|
import com.github.kotlintelegrambot.Bot
|
||||||
|
import com.github.kotlintelegrambot.entities.ChatId
|
||||||
|
import com.github.kotlintelegrambot.entities.InlineKeyboardMarkup
|
||||||
|
import com.github.kotlintelegrambot.entities.ReplyMarkup
|
||||||
|
import com.github.kotlintelegrambot.entities.inputmedia.MediaGroup
|
||||||
|
import com.github.kotlintelegrambot.entities.keyboard.InlineKeyboardButton
|
||||||
|
import com.github.kotlintelegrambot.entities.keyboard.WebAppInfo
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.context.annotation.Lazy
|
||||||
|
import space.luminic.finance.models.Space
|
||||||
|
import space.luminic.finance.models.Transaction
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class NotificationServiceImpl(private val userService: UserService, private val bot: Bot,) : NotificationService {
|
||||||
|
private val logger = LoggerFactory.getLogger(this.javaClass)
|
||||||
|
|
||||||
|
|
||||||
|
private fun createWebAppButton(spaceId: Int? = null, txId: Int? = null): InlineKeyboardMarkup =
|
||||||
|
spaceId?.let { spaceId ->
|
||||||
|
txId?.let { txId ->
|
||||||
|
InlineKeyboardMarkup.create(
|
||||||
|
listOf(
|
||||||
|
InlineKeyboardButton.WebApp(
|
||||||
|
"Открыть в WebApp",
|
||||||
|
WebAppInfo("https://app.luminic.space/transactions/${txId}/edit?mode=from_bot&space=${spaceId}")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} ?: InlineKeyboardMarkup.create(
|
||||||
|
listOf(
|
||||||
|
InlineKeyboardButton.WebApp(
|
||||||
|
"Открыть в WebApp",
|
||||||
|
WebAppInfo("https://app.luminic.space/transactions?mode=from_bot&space=${spaceId}")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} ?: InlineKeyboardMarkup.create(
|
||||||
|
listOf(
|
||||||
|
InlineKeyboardButton.WebApp(
|
||||||
|
"Открыть в WebApp",
|
||||||
|
WebAppInfo("https://app.luminic.space/transactions")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
override fun sendDailyReminder() {
|
||||||
|
val text = "🤑 Время заполнять траты!"
|
||||||
|
val users = userService.getUsers()
|
||||||
|
|
||||||
|
for (user in users) {
|
||||||
|
user.tgId?.let {
|
||||||
|
sendTextMessage(it, text, createWebAppButton())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendTXNotification(
|
||||||
|
action: TxActionType,
|
||||||
|
space: Space,
|
||||||
|
userId: Int,
|
||||||
|
tx: Transaction,
|
||||||
|
tx2: Transaction?
|
||||||
|
) {
|
||||||
|
val user = userService.getById(userId)
|
||||||
|
when (action) {
|
||||||
|
TxActionType.CREATE -> {
|
||||||
|
val text = "${user.firstName} создал транзакцию ${tx.comment} c суммой ${tx.amount} и датой ${tx.date}"
|
||||||
|
space.owner.tgId?.let { sendTextMessage(it, text, createWebAppButton(space.id, tx.id)) }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TxActionType.UPDATE -> {
|
||||||
|
tx2?.let { tx2 ->
|
||||||
|
val changes = mutableListOf<String>()
|
||||||
|
if (tx.type != tx2.type) {
|
||||||
|
changes.add("Тип: ${tx.type.name} → ${tx2.type.name}")
|
||||||
|
}
|
||||||
|
if (tx.kind != tx2.kind) {
|
||||||
|
changes.add("Вид: ${tx.kind.name} → ${tx2.kind.name}")
|
||||||
|
}
|
||||||
|
if (tx.category != tx2.category) {
|
||||||
|
tx.category?.let { oldCategory ->
|
||||||
|
tx2.category?.let { newCategory ->
|
||||||
|
if (oldCategory.id != newCategory.id) {
|
||||||
|
changes.add("Категория: ${oldCategory.name} → ${newCategory.name}")
|
||||||
|
}
|
||||||
|
} ?: changes.add("Удалена категория. Прежняя: ${oldCategory.name}")
|
||||||
|
} ?: {
|
||||||
|
tx2.category?.let { newCategory ->
|
||||||
|
changes.add("Установлена новая категория ${newCategory.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tx.comment != tx2.comment) {
|
||||||
|
changes.add("Комментарий: ${tx.comment} → ${tx2.comment}")
|
||||||
|
}
|
||||||
|
if (tx.amount != tx2.amount) {
|
||||||
|
changes.add("Сумма: ${tx.amount} → ${tx2.amount}")
|
||||||
|
}
|
||||||
|
if (tx.date.toEpochDay() != tx2.date.toEpochDay()) {
|
||||||
|
changes.add(
|
||||||
|
"Сумма: ${
|
||||||
|
tx.date.format(DateTimeFormatter.ofPattern("dd.MM.yyyy"))
|
||||||
|
} → ${
|
||||||
|
tx2.date.format(
|
||||||
|
DateTimeFormatter.ofPattern("dd.MM.yyyy")
|
||||||
|
)
|
||||||
|
}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
var text = "${user.firstName} обновил транзакцию ${tx.comment}\n\n"
|
||||||
|
text += changes.joinToString("\n") { it }
|
||||||
|
space.owner.tgId?.let { sendTextMessage(it, text, createWebAppButton(space.id, tx.id)) }
|
||||||
|
|
||||||
|
} ?: logger.warn("No tx2 provided when update")
|
||||||
|
}
|
||||||
|
|
||||||
|
TxActionType.DELETE -> {
|
||||||
|
val text = "${user.firstName} удалил транзакцию ${tx.comment} c суммой ${tx.amount} и датой ${tx.date}"
|
||||||
|
space.owner.tgId?.let { sendTextMessage(it, text, createWebAppButton(space.id, tx.id)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun sendTextMessage(
|
||||||
|
chatId: Long,
|
||||||
|
message: String,
|
||||||
|
replyMarkup: ReplyMarkup?
|
||||||
|
) {
|
||||||
|
bot.sendMessage(ChatId.fromId(chatId), message, replyMarkup = replyMarkup)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendMediaGroup(
|
||||||
|
chatId: Long,
|
||||||
|
group: MediaGroup
|
||||||
|
) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -110,7 +110,6 @@ class RecurrentOperationServiceImpl(
|
|||||||
type = if (it.category?.type == Category.CategoryType.EXPENSE) Transaction.TransactionType.EXPENSE else Transaction.TransactionType.INCOME,
|
type = if (it.category?.type == Category.CategoryType.EXPENSE) Transaction.TransactionType.EXPENSE else Transaction.TransactionType.INCOME,
|
||||||
category = updatedOperation.category,
|
category = updatedOperation.category,
|
||||||
comment = operation.name,
|
comment = operation.name,
|
||||||
amount = updatedOperation.amount,
|
|
||||||
date = LocalDate.of(
|
date = LocalDate.of(
|
||||||
it.date.year,
|
it.date.year,
|
||||||
it.date.monthValue,
|
it.date.monthValue,
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import org.springframework.stereotype.Service
|
|||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
@Service
|
@Service
|
||||||
class Scheduler(
|
class Scheduler(
|
||||||
private val recurrentOperationService: RecurrentOperationService
|
private val recurrentOperationService: RecurrentOperationService,
|
||||||
|
private val notificationService: NotificationService
|
||||||
) {
|
) {
|
||||||
private val log = LoggerFactory.getLogger(Scheduler::class.java)
|
private val log = LoggerFactory.getLogger(Scheduler::class.java)
|
||||||
|
|
||||||
@@ -17,4 +18,10 @@ class Scheduler(
|
|||||||
log.info("Creating recurrent after 13 month")
|
log.info("Creating recurrent after 13 month")
|
||||||
recurrentOperationService.createRecurrentTransactions()
|
recurrentOperationService.createRecurrentTransactions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Scheduled(cron = "0 30 19 * * *")
|
||||||
|
fun sendDailyReminders() {
|
||||||
|
log.info("Sending daily reminders")
|
||||||
|
notificationService.sendDailyReminder()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package space.luminic.finance.services
|
|
||||||
|
|
||||||
import space.luminic.finance.dtos.TargetDTO
|
|
||||||
import space.luminic.finance.models.Target
|
|
||||||
|
|
||||||
interface TargetService {
|
|
||||||
fun findAllBySpaceId(spaceId: Int): List<Target>
|
|
||||||
fun findBySpaceIdAndId(spaceId: Int, id: Int): Target
|
|
||||||
fun create(spaceId: Int,target: TargetDTO.CreateTargetDTO): Int
|
|
||||||
fun update(spaceId: Int, targetId: Int, target: TargetDTO.UpdateTargetDTO)
|
|
||||||
fun delete(spaceId: Int, id: Int)
|
|
||||||
|
|
||||||
fun getComponents(spaceId: Int, targetId: Int): List<Target.TargetComponent>
|
|
||||||
fun getComponent(spaceId: Int, targetId: Int, id: Int): Target.TargetComponent?
|
|
||||||
fun createComponent(spaceId: Int, targetId: Int, component: Target.TargetComponent): Int
|
|
||||||
fun updateComponent(spaceId: Int, targetId: Int, component: Target.TargetComponent)
|
|
||||||
fun deleteComponent(spaceId: Int, targetId: Int, id: Int)
|
|
||||||
|
|
||||||
fun assignTransaction(spaceId: Int, targetId: Int, transactionId: Int)
|
|
||||||
fun refuseTransaction(spaceId: Int,targetId: Int, transactionId: Int)
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
package space.luminic.finance.services
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import space.luminic.finance.dtos.TargetDTO
|
|
||||||
import space.luminic.finance.models.Target
|
|
||||||
import space.luminic.finance.models.NotFoundException
|
|
||||||
import space.luminic.finance.repos.TargetRepo
|
|
||||||
import space.luminic.finance.repos.SpaceRepo
|
|
||||||
import space.luminic.finance.repos.TransactionRepo
|
|
||||||
|
|
||||||
@Service
|
|
||||||
class TargetServiceImpl(
|
|
||||||
private val targetRepo: TargetRepo,
|
|
||||||
private val spaceRepo: SpaceRepo,
|
|
||||||
private val authService: AuthService,
|
|
||||||
private val transactionRepo: TransactionRepo
|
|
||||||
) : TargetService {
|
|
||||||
override fun findAllBySpaceId(spaceId: Int): List<Target> {
|
|
||||||
val userId = authService.getSecurityUserId()
|
|
||||||
spaceRepo.findSpaceById(spaceId, userId)
|
|
||||||
return targetRepo.findAllBySpaceId(spaceId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun findBySpaceIdAndId(spaceId: Int, id: Int): Target {
|
|
||||||
val userId = authService.getSecurityUserId()
|
|
||||||
spaceRepo.findSpaceById(spaceId, userId)
|
|
||||||
return targetRepo.findBySpaceIdAndId(spaceId, userId) ?: throw NotFoundException("Goal $id not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun create(spaceId: Int, target: TargetDTO.CreateTargetDTO): Int {
|
|
||||||
val userId = authService.getSecurityUserId()
|
|
||||||
spaceRepo.findSpaceById(spaceId, userId)
|
|
||||||
val creatingTarget = Target(
|
|
||||||
type = target.type,
|
|
||||||
name = target.name,
|
|
||||||
amount = target.amount,
|
|
||||||
untilDate = target.date
|
|
||||||
)
|
|
||||||
return targetRepo.create(creatingTarget, userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun update(spaceId: Int, targetId: Int, target: TargetDTO.UpdateTargetDTO) {
|
|
||||||
val userId = authService.getSecurityUserId()
|
|
||||||
spaceRepo.findSpaceById(spaceId, userId)
|
|
||||||
val existingGoal =
|
|
||||||
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Goal $targetId not found")
|
|
||||||
val updatedGoal = existingGoal.copy(
|
|
||||||
type = target.type,
|
|
||||||
name = target.name,
|
|
||||||
description = target.description,
|
|
||||||
amount = target.amount,
|
|
||||||
untilDate = target.date
|
|
||||||
)
|
|
||||||
targetRepo.update(updatedGoal, userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun delete(spaceId: Int, id: Int) {
|
|
||||||
targetRepo.delete(spaceId, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getComponents(
|
|
||||||
spaceId: Int,
|
|
||||||
targetId: Int
|
|
||||||
): List<Target.TargetComponent> {
|
|
||||||
val userId = authService.getSecurityUserId()
|
|
||||||
spaceRepo.findSpaceById(spaceId, userId)
|
|
||||||
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Goal $targetId not found")
|
|
||||||
return targetRepo.getComponents(spaceId, targetId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getComponent(
|
|
||||||
spaceId: Int,
|
|
||||||
targetId: Int,
|
|
||||||
id: Int
|
|
||||||
): Target.TargetComponent? {
|
|
||||||
val userId = authService.getSecurityUserId()
|
|
||||||
spaceRepo.findSpaceById(spaceId, userId)
|
|
||||||
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Target $targetId not found")
|
|
||||||
return targetRepo.getComponent(spaceId, targetId, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createComponent(
|
|
||||||
spaceId: Int,
|
|
||||||
targetId: Int,
|
|
||||||
component: Target.TargetComponent
|
|
||||||
): Int {
|
|
||||||
val userId = authService.getSecurityUserId()
|
|
||||||
spaceRepo.findSpaceById(spaceId, userId)
|
|
||||||
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Target $targetId not found")
|
|
||||||
return targetRepo.createComponent(targetId, component, userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun updateComponent(
|
|
||||||
spaceId: Int,
|
|
||||||
targetId: Int,
|
|
||||||
component: Target.TargetComponent
|
|
||||||
) {
|
|
||||||
val userId = authService.getSecurityUserId()
|
|
||||||
spaceRepo.findSpaceById(spaceId, userId)
|
|
||||||
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Target $targetId not found")
|
|
||||||
val existingComponent = targetRepo.getComponent(spaceId, targetId, component.id!!)
|
|
||||||
?: throw NotFoundException("Component $targetId not found")
|
|
||||||
val updatedComponent = existingComponent.copy(
|
|
||||||
name = component.name,
|
|
||||||
amount = component.amount,
|
|
||||||
isDone = component.isDone,
|
|
||||||
date = component.date
|
|
||||||
)
|
|
||||||
targetRepo.updateComponent(targetId, updatedComponent.id!!, updatedComponent, userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun deleteComponent(spaceId: Int, targetId: Int, id: Int) {
|
|
||||||
val userId = authService.getSecurityUserId()
|
|
||||||
spaceRepo.findSpaceById(spaceId, userId)
|
|
||||||
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Target $targetId not found")
|
|
||||||
targetRepo.getComponent(spaceId, targetId, id) ?: throw NotFoundException("Component $targetId not found")
|
|
||||||
targetRepo.deleteComponent(targetId, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun assignTransaction(spaceId: Int, targetId: Int, transactionId: Int) {
|
|
||||||
val userId = authService.getSecurityUserId()
|
|
||||||
spaceRepo.findSpaceById(spaceId, userId)
|
|
||||||
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Target $targetId not found")
|
|
||||||
transactionRepo.findBySpaceIdAndId(spaceId, transactionId) ?: throw NotFoundException(
|
|
||||||
"Transaction $transactionId not found"
|
|
||||||
)
|
|
||||||
targetRepo.assignTransaction(targetId, transactionId)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun refuseTransaction(spaceId: Int, targetId: Int, transactionId: Int) {
|
|
||||||
val userId = authService.getSecurityUserId()
|
|
||||||
spaceRepo.findSpaceById(spaceId, userId)
|
|
||||||
targetRepo.findBySpaceIdAndId(spaceId, targetId) ?: throw NotFoundException("Target $targetId not found")
|
|
||||||
transactionRepo.findBySpaceIdAndId(spaceId, transactionId) ?: throw NotFoundException(
|
|
||||||
"Transaction $transactionId not found"
|
|
||||||
)
|
|
||||||
targetRepo.refuseTransaction(targetId, transactionId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,17 @@
|
|||||||
package space.luminic.finance.services
|
package space.luminic.finance.services
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import space.luminic.finance.dtos.TransactionDTO
|
import space.luminic.finance.dtos.TransactionDTO
|
||||||
import space.luminic.finance.models.NotFoundException
|
import space.luminic.finance.models.NotFoundException
|
||||||
import space.luminic.finance.models.Transaction
|
import space.luminic.finance.models.Transaction
|
||||||
import space.luminic.finance.repos.TransactionRepo
|
import space.luminic.finance.repos.TransactionRepo
|
||||||
import space.luminic.finance.services.gpt.CategorizeService
|
import space.luminic.finance.services.gpt.CategorizeService
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
class TransactionServiceImpl(
|
class TransactionServiceImpl(
|
||||||
@@ -14,7 +20,11 @@ class TransactionServiceImpl(
|
|||||||
private val transactionRepo: TransactionRepo,
|
private val transactionRepo: TransactionRepo,
|
||||||
private val authService: AuthService,
|
private val authService: AuthService,
|
||||||
private val categorizeService: CategorizeService,
|
private val categorizeService: CategorizeService,
|
||||||
|
private val notificationService: NotificationService,
|
||||||
) : TransactionService {
|
) : TransactionService {
|
||||||
|
private val logger = LoggerFactory.getLogger(this.javaClass)
|
||||||
|
private val serviceScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||||
|
|
||||||
override fun getTransactions(
|
override fun getTransactions(
|
||||||
spaceId: Int,
|
spaceId: Int,
|
||||||
filter: TransactionService.TransactionsFilter,
|
filter: TransactionService.TransactionsFilter,
|
||||||
@@ -53,7 +63,17 @@ class TransactionServiceImpl(
|
|||||||
date = transaction.date,
|
date = transaction.date,
|
||||||
recurrentId = transaction.recurrentId,
|
recurrentId = transaction.recurrentId,
|
||||||
)
|
)
|
||||||
return transactionRepo.create(transaction, userId)
|
val createdTx = transactionRepo.create(transaction, userId)
|
||||||
|
serviceScope.launch {
|
||||||
|
runCatching {
|
||||||
|
if (space.owner.id != userId) {
|
||||||
|
notificationService.sendTXNotification(TxActionType.CREATE, space, userId, transaction)
|
||||||
|
}
|
||||||
|
}.onFailure {
|
||||||
|
logger.error("Error while creating transaction", it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return createdTx
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun batchCreate(spaceId: Int, transactions: List<TransactionDTO.CreateTransactionDTO>, createdById: Int?) {
|
override fun batchCreate(spaceId: Int, transactions: List<TransactionDTO.CreateTransactionDTO>, createdById: Int?) {
|
||||||
@@ -85,6 +105,7 @@ class TransactionServiceImpl(
|
|||||||
transactionId: Int,
|
transactionId: Int,
|
||||||
transaction: TransactionDTO.UpdateTransactionDTO
|
transaction: TransactionDTO.UpdateTransactionDTO
|
||||||
): Int {
|
): Int {
|
||||||
|
val userId = authService.getSecurityUserId()
|
||||||
val space = spaceService.getSpace(spaceId, null)
|
val space = spaceService.getSpace(spaceId, null)
|
||||||
val existingTransaction = getTransaction(space.id!!, transactionId)
|
val existingTransaction = getTransaction(space.id!!, transactionId)
|
||||||
val newCategory = transaction.categoryId?.let { categoryService.getCategory(spaceId, it) }
|
val newCategory = transaction.categoryId?.let { categoryService.getCategory(spaceId, it) }
|
||||||
@@ -109,14 +130,34 @@ class TransactionServiceImpl(
|
|||||||
if ((existingTransaction.category == null && updatedTransaction.category != null) || (existingTransaction.category?.id != updatedTransaction.category?.id)) {
|
if ((existingTransaction.category == null && updatedTransaction.category != null) || (existingTransaction.category?.id != updatedTransaction.category?.id)) {
|
||||||
categorizeService.notifyThatCategorySelected(updatedTransaction)
|
categorizeService.notifyThatCategorySelected(updatedTransaction)
|
||||||
}
|
}
|
||||||
|
val updatedTx = transactionRepo.update(updatedTransaction)
|
||||||
|
serviceScope.launch {
|
||||||
|
runCatching {
|
||||||
|
|
||||||
return transactionRepo.update(updatedTransaction)
|
notificationService.sendTXNotification(
|
||||||
|
TxActionType.UPDATE,
|
||||||
|
space,
|
||||||
|
userId,
|
||||||
|
existingTransaction,
|
||||||
|
updatedTransaction
|
||||||
|
)
|
||||||
|
}.onFailure {
|
||||||
|
logger.error("Error while send transaction update notification", it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updatedTx
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteTransaction(spaceId: Int, transactionId: Int) {
|
override fun deleteTransaction(spaceId: Int, transactionId: Int) {
|
||||||
|
val userId = authService.getSecurityUserId()
|
||||||
val space = spaceService.getSpace(spaceId, null)
|
val space = spaceService.getSpace(spaceId, null)
|
||||||
getTransaction(space.id!!, transactionId)
|
val tx = getTransaction(space.id!!, transactionId)
|
||||||
transactionRepo.delete(transactionId)
|
transactionRepo.delete(transactionId)
|
||||||
|
serviceScope.launch {
|
||||||
|
runCatching {
|
||||||
|
notificationService.sendTXNotification(TxActionType.DELETE, space, userId, tx)
|
||||||
|
}.onFailure { logger.error("Error while transaction delete notification", it) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteByRecurrentId(spaceId: Int, recurrentId: Int) {
|
override fun deleteByRecurrentId(spaceId: Int, recurrentId: Int) {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class CategorizeService(
|
|||||||
listOf(
|
listOf(
|
||||||
InlineKeyboardButton.WebApp(
|
InlineKeyboardButton.WebApp(
|
||||||
"Открыть в WebApp",
|
"Открыть в WebApp",
|
||||||
WebAppInfo("https://app.luminic.space/transactions/${tx.id}/edit?mode=from_bot")
|
WebAppInfo("https://app.luminic.space/transactions/${tx.id}/edit")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -85,7 +85,7 @@ class CategorizeService(
|
|||||||
listOf(
|
listOf(
|
||||||
InlineKeyboardButton.WebApp(
|
InlineKeyboardButton.WebApp(
|
||||||
"Открыть в WebApp",
|
"Открыть в WebApp",
|
||||||
WebAppInfo("https://app.luminic.space/transactions/${tx.id}/edit?mode=from_bot")
|
WebAppInfo("https://app.luminic.space/transactions/${tx.id}/edit")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@@ -131,7 +131,7 @@ class CategorizeService(
|
|||||||
listOf(
|
listOf(
|
||||||
InlineKeyboardButton.WebApp(
|
InlineKeyboardButton.WebApp(
|
||||||
"Открыть в WebApp",
|
"Открыть в WebApp",
|
||||||
WebAppInfo("https://app.luminic.space/transactions/${tx.id}/edit?mode=from_bot")
|
WebAppInfo("https://app.luminic.space/transactions/${tx.id}/edit")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import com.github.kotlintelegrambot.logging.LogLevel
|
|||||||
import org.springframework.beans.factory.annotation.Qualifier
|
import org.springframework.beans.factory.annotation.Qualifier
|
||||||
import org.springframework.beans.factory.annotation.Value
|
import org.springframework.beans.factory.annotation.Value
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Lazy
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import org.springframework.transaction.annotation.Transactional
|
import org.springframework.transaction.annotation.Transactional
|
||||||
import space.luminic.finance.dtos.TransactionDTO
|
import space.luminic.finance.dtos.TransactionDTO
|
||||||
@@ -30,10 +31,11 @@ import java.time.LocalDate
|
|||||||
@Service
|
@Service
|
||||||
class BotService(
|
class BotService(
|
||||||
@Value("\${telegram.bot.token}") private val botToken: String,
|
@Value("\${telegram.bot.token}") private val botToken: String,
|
||||||
|
@Value("\${spring.profiles.active}") private val profile: String,
|
||||||
private val userService: UserService,
|
private val userService: UserService,
|
||||||
@Qualifier("spaceServiceTelegram") private val spaceService: SpaceService,
|
@Qualifier("spaceServiceTelegram") private val spaceService: SpaceService,
|
||||||
private val botRepo: BotRepo,
|
private val botRepo: BotRepo,
|
||||||
@Qualifier("transactionsServiceTelegram") private val transactionService: TransactionService
|
@Lazy @Qualifier("transactionsServiceTelegram") private val transactionService: TransactionService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
@@ -113,13 +115,13 @@ class BotService(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return InlineKeyboardMarkup.Companion.create(keyboard)
|
return InlineKeyboardMarkup.create(keyboard)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
fun bot(): Bot {
|
fun bot(): Bot {
|
||||||
val bot = com.github.kotlintelegrambot.bot {
|
val bot = com.github.kotlintelegrambot.bot {
|
||||||
logLevel = LogLevel.None
|
logLevel = if (profile == "proc") LogLevel.None else LogLevel.All()
|
||||||
token = botToken
|
token = botToken
|
||||||
dispatch {
|
dispatch {
|
||||||
message(Filter.Text) {
|
message(Filter.Text) {
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ import space.luminic.finance.models.Space
|
|||||||
|
|
||||||
interface SpaceService {
|
interface SpaceService {
|
||||||
fun getSpaces(userId: Int): List<Space>
|
fun getSpaces(userId: Int): List<Space>
|
||||||
fun getSpace(spaceId: Int, userId: Int): Space?
|
fun getSpace(spaceId: Int, userId: Int): Space
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ class SpaceServiceImpl(
|
|||||||
return spaces
|
return spaces
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSpace(spaceId: Int, userId: Int): Space? {
|
override fun getSpace(spaceId: Int, userId: Int): Space {
|
||||||
val space =
|
val space =
|
||||||
spaceRepo.findSpaceById(spaceId, userId) ?: throw NotFoundException("Space with id $spaceId not found")
|
spaceRepo.findSpaceById(spaceId, userId) ?: throw NotFoundException("Space with id $spaceId not found")
|
||||||
return space
|
return space
|
||||||
|
|||||||
@@ -1,18 +1,29 @@
|
|||||||
package space.luminic.finance.services.telegram
|
package space.luminic.finance.services.telegram
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.beans.factory.annotation.Qualifier
|
import org.springframework.beans.factory.annotation.Qualifier
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import space.luminic.finance.dtos.TransactionDTO
|
import space.luminic.finance.dtos.TransactionDTO
|
||||||
import space.luminic.finance.models.Transaction
|
import space.luminic.finance.models.Transaction
|
||||||
import space.luminic.finance.repos.TransactionRepo
|
import space.luminic.finance.repos.TransactionRepo
|
||||||
import space.luminic.finance.services.CategoryServiceImpl
|
import space.luminic.finance.services.CategoryServiceImpl
|
||||||
|
import space.luminic.finance.services.NotificationService
|
||||||
|
import space.luminic.finance.services.TxActionType
|
||||||
|
|
||||||
@Service("transactionsServiceTelegram")
|
@Service("transactionsServiceTelegram")
|
||||||
class TransactionsServiceImpl(
|
class TransactionsServiceImpl(
|
||||||
private val transactionRepo: TransactionRepo,
|
private val transactionRepo: TransactionRepo,
|
||||||
@Qualifier("spaceServiceTelegram") private val spaceService: SpaceService,
|
@Qualifier("spaceServiceTelegram") private val spaceService: SpaceService,
|
||||||
private val categoryService: CategoryServiceImpl
|
private val categoryService: CategoryServiceImpl,
|
||||||
): TransactionService {
|
private val notificationService: NotificationService
|
||||||
|
) : TransactionService {
|
||||||
|
private val logger = LoggerFactory.getLogger(this.javaClass)
|
||||||
|
private val serviceScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
|
||||||
|
|
||||||
|
|
||||||
override fun createTransaction(
|
override fun createTransaction(
|
||||||
spaceId: Int,
|
spaceId: Int,
|
||||||
@@ -35,9 +46,17 @@ class TransactionsServiceImpl(
|
|||||||
tgChatId = chatId,
|
tgChatId = chatId,
|
||||||
tgMessageId = messageId,
|
tgMessageId = messageId,
|
||||||
)
|
)
|
||||||
|
serviceScope.launch {
|
||||||
|
runCatching {
|
||||||
|
if (space.owner.id != userId) {
|
||||||
|
notificationService.sendTXNotification(TxActionType.CREATE, space, userId, transaction)
|
||||||
|
}
|
||||||
|
}.onFailure {
|
||||||
|
logger.error("Error while transaction notification", it)
|
||||||
|
}
|
||||||
|
}
|
||||||
return transactionRepo.create(transaction, userId)
|
return transactionRepo.create(transaction, userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
DROP table if exists finance.goals cascade;
|
|
||||||
DROP table if exists finance.goals_components cascade;
|
|
||||||
DROP table if exists finance.goals_transactions cascade;
|
|
||||||
|
|
||||||
DROP table if exists finance.targets cascade;
|
|
||||||
DROP table if exists finance.targets_components cascade;
|
|
||||||
DROP table if exists finance.targets_transactions cascade;
|
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE finance.targets
|
|
||||||
(
|
|
||||||
id integer generated by default as identity not null,
|
|
||||||
space_id INTEGER,
|
|
||||||
type SMALLINT,
|
|
||||||
name VARCHAR(255),
|
|
||||||
description VARCHAR(255),
|
|
||||||
amount DECIMAL,
|
|
||||||
until_date date,
|
|
||||||
created_by_id INTEGER,
|
|
||||||
created_at TIMESTAMP WITHOUT TIME ZONE,
|
|
||||||
updated_by_id INTEGER,
|
|
||||||
updated_at TIMESTAMP WITHOUT TIME ZONE,
|
|
||||||
CONSTRAINT pk_targets PRIMARY KEY (id)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE finance.targets
|
|
||||||
ADD CONSTRAINT FK_TARGETS_ON_CREATEDBY FOREIGN KEY (created_by_id) REFERENCES finance.users (id);
|
|
||||||
|
|
||||||
ALTER TABLE finance.targets
|
|
||||||
ADD CONSTRAINT FK_TARGETS_ON_SPACE FOREIGN KEY (space_id) REFERENCES finance.spaces (id);
|
|
||||||
|
|
||||||
ALTER TABLE finance.targets
|
|
||||||
ADD CONSTRAINT FK_TARGETS_ON_UPDATEDBY FOREIGN KEY (updated_by_id) REFERENCES finance.users (id);
|
|
||||||
|
|
||||||
CREATE TABLE finance.targets_transactions
|
|
||||||
(
|
|
||||||
target_id INTEGER NOT NULL,
|
|
||||||
transactions_id INTEGER NOT NULL
|
|
||||||
);
|
|
||||||
ALTER TABLE finance.targets_transactions
|
|
||||||
ADD CONSTRAINT fk_targettx_on_target FOREIGN KEY (target_id) REFERENCES finance.targets (id);
|
|
||||||
|
|
||||||
ALTER TABLE finance.targets_transactions
|
|
||||||
ADD CONSTRAINT fk_targettx_on_tx FOREIGN KEY (transactions_id) REFERENCES finance.transactions (id);
|
|
||||||
|
|
||||||
create table if not exists finance.targets_components
|
|
||||||
(
|
|
||||||
id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
||||||
target_id INTEGER NOT NULL,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
amount NUMERIC NOT NULL,
|
|
||||||
is_done BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
date TIMESTAMP WITH TIME ZONE NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
alter table finance.targets_components
|
|
||||||
add constraint fk_target_on_components foreign key (target_id) references finance.targets (id);
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user