优化基础框架
This commit is contained in:
@@ -0,0 +1,181 @@
|
|||||||
|
@file:OptIn(ExperimentalUuidApi::class)
|
||||||
|
|
||||||
|
package com.bbit.ticket.dao.system
|
||||||
|
|
||||||
|
import com.bbit.ticket.database.system.SysDictItemTable
|
||||||
|
import com.bbit.ticket.database.system.SysDictTypeTable
|
||||||
|
import com.bbit.ticket.entity.system.CreateDictItemRequest
|
||||||
|
import com.bbit.ticket.entity.system.CreateDictTypeRequest
|
||||||
|
import com.bbit.ticket.entity.system.DictItem
|
||||||
|
import com.bbit.ticket.entity.system.DictTypeItem
|
||||||
|
import com.bbit.ticket.entity.system.UpdateDictItemRequest
|
||||||
|
import com.bbit.ticket.entity.system.UpdateDictTypeRequest
|
||||||
|
import com.bbit.ticket.entity.common.BizException
|
||||||
|
import com.bbit.ticket.entity.common.ErrorCode
|
||||||
|
import com.bbit.ticket.entity.common.PageResult
|
||||||
|
import com.bbit.ticket.entity.common.statusLabel
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import org.jetbrains.exposed.v1.core.Op
|
||||||
|
import org.jetbrains.exposed.v1.core.ResultRow
|
||||||
|
import org.jetbrains.exposed.v1.core.and
|
||||||
|
import org.jetbrains.exposed.v1.core.eq
|
||||||
|
import org.jetbrains.exposed.v1.core.isNull
|
||||||
|
import org.jetbrains.exposed.v1.core.like
|
||||||
|
import org.jetbrains.exposed.v1.core.or
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.insert
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.update
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
object DictDao {
|
||||||
|
fun listTypes(page: Int, pageSize: Int, keyword: String?): PageResult<DictTypeItem> {
|
||||||
|
val where = buildTypeWhere(keyword)
|
||||||
|
val total = SysDictTypeTable.selectAll().where { where }.count()
|
||||||
|
val rows = SysDictTypeTable.selectAll().where { where }
|
||||||
|
.orderBy(SysDictTypeTable.createdAt)
|
||||||
|
.limit(pageSize)
|
||||||
|
.offset(pageOffset(page, pageSize))
|
||||||
|
.toList()
|
||||||
|
return PageResult(
|
||||||
|
items = rows.map { it.toDictTypeItem() },
|
||||||
|
page = page,
|
||||||
|
pageSize = pageSize,
|
||||||
|
total = total,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun typeCodeExists(code: String): Boolean =
|
||||||
|
SysDictTypeTable.selectAll().where {
|
||||||
|
(SysDictTypeTable.code eq code) and SysDictTypeTable.deletedAt.isNull()
|
||||||
|
}.any()
|
||||||
|
|
||||||
|
fun createType(request: CreateDictTypeRequest): String {
|
||||||
|
val inserted = SysDictTypeTable.insert {
|
||||||
|
it[SysDictTypeTable.code] = request.code.trim()
|
||||||
|
it[SysDictTypeTable.name] = request.name.trim()
|
||||||
|
it[SysDictTypeTable.status] = request.status
|
||||||
|
it[SysDictTypeTable.remark] = request.remark.trimToNull()
|
||||||
|
it[SysDictTypeTable.createdAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
return inserted[SysDictTypeTable.id].toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateType(id: Uuid, request: UpdateDictTypeRequest) {
|
||||||
|
SysDictTypeTable.update({ SysDictTypeTable.id eq id }) {
|
||||||
|
it[SysDictTypeTable.name] = request.name.trim()
|
||||||
|
it[SysDictTypeTable.status] = request.status
|
||||||
|
it[SysDictTypeTable.remark] = request.remark.trimToNull()
|
||||||
|
it[SysDictTypeTable.updatedAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requireType(id: Uuid): ResultRow =
|
||||||
|
SysDictTypeTable.selectAll().where {
|
||||||
|
(SysDictTypeTable.id eq id) and SysDictTypeTable.deletedAt.isNull()
|
||||||
|
}.singleOrNull() ?: throw BizException(
|
||||||
|
ErrorCode.DICT_TYPE_NOT_FOUND.code,
|
||||||
|
ErrorCode.DICT_TYPE_NOT_FOUND.message,
|
||||||
|
HttpStatusCode.NotFound,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun typeHasItems(id: Uuid): Boolean =
|
||||||
|
SysDictItemTable.selectAll().where {
|
||||||
|
(SysDictItemTable.typeId eq id) and SysDictItemTable.deletedAt.isNull()
|
||||||
|
}.any()
|
||||||
|
|
||||||
|
fun softDeleteType(id: Uuid) {
|
||||||
|
SysDictTypeTable.update({ SysDictTypeTable.id eq id }) {
|
||||||
|
it[SysDictTypeTable.deletedAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun listItems(page: Int, pageSize: Int, typeId: Uuid?): PageResult<DictItem> {
|
||||||
|
var where: Op<Boolean> = SysDictItemTable.deletedAt.isNull()
|
||||||
|
if (typeId != null) where = where and (SysDictItemTable.typeId eq typeId)
|
||||||
|
val total = SysDictItemTable.selectAll().where { where }.count()
|
||||||
|
val rows = SysDictItemTable.selectAll().where { where }
|
||||||
|
.orderBy(SysDictItemTable.sort)
|
||||||
|
.limit(pageSize)
|
||||||
|
.offset(pageOffset(page, pageSize))
|
||||||
|
.toList()
|
||||||
|
return PageResult(
|
||||||
|
items = rows.map { it.toDictItem() },
|
||||||
|
page = page,
|
||||||
|
pageSize = pageSize,
|
||||||
|
total = total,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createItem(request: CreateDictItemRequest, typeId: Uuid): String {
|
||||||
|
val inserted = SysDictItemTable.insert {
|
||||||
|
it[SysDictItemTable.typeId] = typeId
|
||||||
|
it[SysDictItemTable.label] = request.label.trim()
|
||||||
|
it[SysDictItemTable.value] = request.value.trim()
|
||||||
|
it[SysDictItemTable.color] = request.color.trimToNull()
|
||||||
|
it[SysDictItemTable.sort] = request.sort
|
||||||
|
it[SysDictItemTable.status] = request.status
|
||||||
|
it[SysDictItemTable.remark] = request.remark.trimToNull()
|
||||||
|
it[SysDictItemTable.createdAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
return inserted[SysDictItemTable.id].toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requireItem(id: Uuid): ResultRow =
|
||||||
|
SysDictItemTable.selectAll().where {
|
||||||
|
(SysDictItemTable.id eq id) and SysDictItemTable.deletedAt.isNull()
|
||||||
|
}.singleOrNull() ?: throw BizException(
|
||||||
|
ErrorCode.DICT_ITEM_NOT_FOUND.code,
|
||||||
|
ErrorCode.DICT_ITEM_NOT_FOUND.message,
|
||||||
|
HttpStatusCode.NotFound,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun updateItem(id: Uuid, request: UpdateDictItemRequest, typeId: Uuid) {
|
||||||
|
SysDictItemTable.update({ SysDictItemTable.id eq id }) {
|
||||||
|
it[SysDictItemTable.typeId] = typeId
|
||||||
|
it[SysDictItemTable.label] = request.label.trim()
|
||||||
|
it[SysDictItemTable.value] = request.value.trim()
|
||||||
|
it[SysDictItemTable.color] = request.color.trimToNull()
|
||||||
|
it[SysDictItemTable.sort] = request.sort
|
||||||
|
it[SysDictItemTable.status] = request.status
|
||||||
|
it[SysDictItemTable.remark] = request.remark.trimToNull()
|
||||||
|
it[SysDictItemTable.updatedAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun softDeleteItem(id: Uuid) {
|
||||||
|
SysDictItemTable.update({ SysDictItemTable.id eq id }) {
|
||||||
|
it[SysDictItemTable.deletedAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildTypeWhere(keyword: String?): Op<Boolean> {
|
||||||
|
var where: Op<Boolean> = SysDictTypeTable.deletedAt.isNull()
|
||||||
|
if (!keyword.isNullOrBlank()) {
|
||||||
|
where = where and ((SysDictTypeTable.code like "%$keyword%") or (SysDictTypeTable.name like "%$keyword%"))
|
||||||
|
}
|
||||||
|
return where
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ResultRow.toDictTypeItem() = DictTypeItem(
|
||||||
|
id = this[SysDictTypeTable.id].toString(),
|
||||||
|
code = this[SysDictTypeTable.code],
|
||||||
|
name = this[SysDictTypeTable.name],
|
||||||
|
status = this[SysDictTypeTable.status],
|
||||||
|
statusLabel = statusLabel(this[SysDictTypeTable.status]),
|
||||||
|
remark = this[SysDictTypeTable.remark],
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun ResultRow.toDictItem() = DictItem(
|
||||||
|
id = this[SysDictItemTable.id].toString(),
|
||||||
|
typeId = this[SysDictItemTable.typeId].toString(),
|
||||||
|
label = this[SysDictItemTable.label],
|
||||||
|
value = this[SysDictItemTable.value],
|
||||||
|
color = this[SysDictItemTable.color],
|
||||||
|
sort = this[SysDictItemTable.sort],
|
||||||
|
status = this[SysDictItemTable.status],
|
||||||
|
statusLabel = statusLabel(this[SysDictItemTable.status]),
|
||||||
|
remark = this[SysDictItemTable.remark],
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
@file:OptIn(ExperimentalUuidApi::class)
|
||||||
|
|
||||||
|
package com.bbit.ticket.dao.system
|
||||||
|
|
||||||
|
import com.bbit.ticket.database.system.SysApiAccessLogTable
|
||||||
|
import com.bbit.ticket.database.system.SysOperationLogTable
|
||||||
|
import com.bbit.ticket.entity.system.ApiAccessLogItem
|
||||||
|
import com.bbit.ticket.entity.system.OperationLogItem
|
||||||
|
import com.bbit.ticket.entity.common.PageResult
|
||||||
|
import com.bbit.ticket.utils.CurrentUser
|
||||||
|
import com.bbit.ticket.utils.formatDateTime
|
||||||
|
import com.bbit.ticket.utils.traceIdOrNull
|
||||||
|
import io.ktor.http.formUrlEncode
|
||||||
|
import io.ktor.server.application.ApplicationCall
|
||||||
|
import io.ktor.server.request.httpMethod
|
||||||
|
import io.ktor.server.request.path
|
||||||
|
import org.jetbrains.exposed.v1.core.Op
|
||||||
|
import org.jetbrains.exposed.v1.core.SortOrder
|
||||||
|
import org.jetbrains.exposed.v1.core.and
|
||||||
|
import org.jetbrains.exposed.v1.core.eq
|
||||||
|
import org.jetbrains.exposed.v1.core.like
|
||||||
|
import org.jetbrains.exposed.v1.core.or
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.insert
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
|
||||||
|
object LogDao {
|
||||||
|
fun operationLogs(page: Int, pageSize: Int, keyword: String?, status: String?): PageResult<OperationLogItem> {
|
||||||
|
var where: Op<Boolean> = Op.TRUE
|
||||||
|
if (!keyword.isNullOrBlank()) {
|
||||||
|
where = where and ((SysOperationLogTable.username like "%$keyword%") or (SysOperationLogTable.requestPath like "%$keyword%"))
|
||||||
|
}
|
||||||
|
if (!status.isNullOrBlank()) {
|
||||||
|
where = where and (SysOperationLogTable.status eq status)
|
||||||
|
}
|
||||||
|
val total = SysOperationLogTable.selectAll().where { where }.count()
|
||||||
|
val rows = SysOperationLogTable.selectAll().where { where }
|
||||||
|
.orderBy(SysOperationLogTable.createdAt, SortOrder.DESC)
|
||||||
|
.limit(pageSize)
|
||||||
|
.offset(pageOffset(page, pageSize))
|
||||||
|
.toList()
|
||||||
|
return PageResult(
|
||||||
|
items = rows.map { row ->
|
||||||
|
OperationLogItem(
|
||||||
|
id = row[SysOperationLogTable.id].toString(),
|
||||||
|
traceId = row[SysOperationLogTable.traceId],
|
||||||
|
username = row[SysOperationLogTable.username],
|
||||||
|
operationType = row[SysOperationLogTable.operationType],
|
||||||
|
operationName = row[SysOperationLogTable.operationName],
|
||||||
|
httpMethod = row[SysOperationLogTable.httpMethod],
|
||||||
|
requestPath = row[SysOperationLogTable.requestPath],
|
||||||
|
status = row[SysOperationLogTable.status],
|
||||||
|
errorMessage = row[SysOperationLogTable.errorMessage],
|
||||||
|
costMs = row[SysOperationLogTable.costMs],
|
||||||
|
createdAt = formatDateTime(row[SysOperationLogTable.createdAt]) ?: "",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
page = page,
|
||||||
|
pageSize = pageSize,
|
||||||
|
total = total,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun apiAccessLogs(page: Int, pageSize: Int, keyword: String?, status: String?): PageResult<ApiAccessLogItem> {
|
||||||
|
var where: Op<Boolean> = Op.TRUE
|
||||||
|
if (!keyword.isNullOrBlank()) {
|
||||||
|
where = where and ((SysApiAccessLogTable.appName like "%$keyword%") or (SysApiAccessLogTable.requestPath like "%$keyword%"))
|
||||||
|
}
|
||||||
|
if (!status.isNullOrBlank()) {
|
||||||
|
where = where and (SysApiAccessLogTable.status eq status)
|
||||||
|
}
|
||||||
|
val total = SysApiAccessLogTable.selectAll().where { where }.count()
|
||||||
|
val rows = SysApiAccessLogTable.selectAll().where { where }
|
||||||
|
.orderBy(SysApiAccessLogTable.createdAt, SortOrder.DESC)
|
||||||
|
.limit(pageSize)
|
||||||
|
.offset(pageOffset(page, pageSize))
|
||||||
|
.toList()
|
||||||
|
return PageResult(
|
||||||
|
items = rows.map { row ->
|
||||||
|
ApiAccessLogItem(
|
||||||
|
id = row[SysApiAccessLogTable.id].toString(),
|
||||||
|
traceId = row[SysApiAccessLogTable.traceId],
|
||||||
|
appKey = row[SysApiAccessLogTable.appKey],
|
||||||
|
appName = row[SysApiAccessLogTable.appName],
|
||||||
|
httpMethod = row[SysApiAccessLogTable.httpMethod],
|
||||||
|
requestPath = row[SysApiAccessLogTable.requestPath],
|
||||||
|
responseCode = row[SysApiAccessLogTable.responseCode],
|
||||||
|
status = row[SysApiAccessLogTable.status],
|
||||||
|
errorMessage = row[SysApiAccessLogTable.errorMessage],
|
||||||
|
costMs = row[SysApiAccessLogTable.costMs],
|
||||||
|
createdAt = formatDateTime(row[SysApiAccessLogTable.createdAt]) ?: "",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
page = page,
|
||||||
|
pageSize = pageSize,
|
||||||
|
total = total,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveOperationLog(
|
||||||
|
call: ApplicationCall,
|
||||||
|
currentUser: CurrentUser?,
|
||||||
|
operationType: String,
|
||||||
|
operationName: String,
|
||||||
|
status: String,
|
||||||
|
errorMessage: String?,
|
||||||
|
costMs: Long,
|
||||||
|
) {
|
||||||
|
SysOperationLogTable.insert {
|
||||||
|
it[traceId] = call.traceIdOrNull()
|
||||||
|
it[userId] = currentUser?.id
|
||||||
|
it[SysOperationLogTable.username] = currentUser?.username
|
||||||
|
it[orgId] = currentUser?.orgId
|
||||||
|
it[SysOperationLogTable.operationType] = operationType
|
||||||
|
it[SysOperationLogTable.operationName] = operationName
|
||||||
|
it[httpMethod] = call.request.httpMethod.value
|
||||||
|
it[requestPath] = call.request.path().take(255)
|
||||||
|
it[requestParams] = call.request.queryParameters.formUrlEncode().take(1000)
|
||||||
|
it[ip] = call.request.local.remoteHost.take(64)
|
||||||
|
it[userAgent] = call.request.headers["User-Agent"]?.take(255)
|
||||||
|
it[SysOperationLogTable.status] = status
|
||||||
|
it[SysOperationLogTable.errorMessage] = errorMessage
|
||||||
|
it[SysOperationLogTable.costMs] = costMs
|
||||||
|
it[createdAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
@file:OptIn(ExperimentalUuidApi::class)
|
||||||
|
|
||||||
|
package com.bbit.ticket.dao.system
|
||||||
|
|
||||||
|
import com.bbit.ticket.database.system.SysMenuTable
|
||||||
|
import com.bbit.ticket.database.system.SysRoleMenuTable
|
||||||
|
import com.bbit.ticket.entity.system.CreateMenuRequest
|
||||||
|
import com.bbit.ticket.entity.system.MenuFlat
|
||||||
|
import com.bbit.ticket.entity.system.MenuNode
|
||||||
|
import com.bbit.ticket.entity.system.MenuTreeNode
|
||||||
|
import com.bbit.ticket.entity.system.UpdateMenuRequest
|
||||||
|
import com.bbit.ticket.entity.common.BizException
|
||||||
|
import com.bbit.ticket.entity.common.ErrorCode
|
||||||
|
import com.bbit.ticket.entity.common.menuTypeLabel
|
||||||
|
import com.bbit.ticket.entity.common.statusLabel
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import org.jetbrains.exposed.v1.core.ResultRow
|
||||||
|
import org.jetbrains.exposed.v1.core.and
|
||||||
|
import org.jetbrains.exposed.v1.core.eq
|
||||||
|
import org.jetbrains.exposed.v1.core.inList
|
||||||
|
import org.jetbrains.exposed.v1.core.isNull
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.insert
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.update
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
object MenuDao {
|
||||||
|
fun tree(): List<MenuTreeNode> = buildTree(loadAllFlatMenus())
|
||||||
|
|
||||||
|
fun requireActive(id: Uuid): ResultRow =
|
||||||
|
SysMenuTable.selectAll().where {
|
||||||
|
(SysMenuTable.id eq id) and SysMenuTable.deletedAt.isNull()
|
||||||
|
}.singleOrNull() ?: throw BizException(
|
||||||
|
ErrorCode.MENU_NOT_FOUND.code,
|
||||||
|
ErrorCode.MENU_NOT_FOUND.message,
|
||||||
|
HttpStatusCode.NotFound,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun create(request: CreateMenuRequest, parentId: Uuid?): String {
|
||||||
|
val inserted = SysMenuTable.insert {
|
||||||
|
it[SysMenuTable.parentId] = parentId
|
||||||
|
it[SysMenuTable.type] = request.type
|
||||||
|
it[SysMenuTable.title] = request.title.trim()
|
||||||
|
it[SysMenuTable.name] = request.name.trimToNull()
|
||||||
|
it[SysMenuTable.path] = request.path.trimToNull()
|
||||||
|
it[SysMenuTable.component] = request.component.trimToNull()
|
||||||
|
it[SysMenuTable.icon] = request.icon.trimToNull()
|
||||||
|
it[SysMenuTable.permission] = request.permission.trimToNull()
|
||||||
|
it[SysMenuTable.sort] = request.sort
|
||||||
|
it[SysMenuTable.visible] = request.visible
|
||||||
|
it[SysMenuTable.keepAlive] = request.keepAlive
|
||||||
|
it[SysMenuTable.builtIn] = false
|
||||||
|
it[SysMenuTable.status] = request.status
|
||||||
|
it[SysMenuTable.createdAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
return inserted[SysMenuTable.id].toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(id: Uuid, request: UpdateMenuRequest, parentId: Uuid?) {
|
||||||
|
SysMenuTable.update({ SysMenuTable.id eq id }) {
|
||||||
|
it[SysMenuTable.parentId] = parentId
|
||||||
|
it[SysMenuTable.type] = request.type
|
||||||
|
it[SysMenuTable.title] = request.title.trim()
|
||||||
|
it[SysMenuTable.name] = request.name.trimToNull()
|
||||||
|
it[SysMenuTable.path] = request.path.trimToNull()
|
||||||
|
it[SysMenuTable.component] = request.component.trimToNull()
|
||||||
|
it[SysMenuTable.icon] = request.icon.trimToNull()
|
||||||
|
it[SysMenuTable.permission] = request.permission.trimToNull()
|
||||||
|
it[SysMenuTable.sort] = request.sort
|
||||||
|
it[SysMenuTable.visible] = request.visible
|
||||||
|
it[SysMenuTable.keepAlive] = request.keepAlive
|
||||||
|
it[SysMenuTable.status] = request.status
|
||||||
|
it[SysMenuTable.updatedAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasChildren(id: Uuid): Boolean =
|
||||||
|
SysMenuTable.selectAll().where {
|
||||||
|
(SysMenuTable.parentId eq id) and SysMenuTable.deletedAt.isNull()
|
||||||
|
}.any()
|
||||||
|
|
||||||
|
fun isReferenced(id: Uuid): Boolean =
|
||||||
|
SysRoleMenuTable.selectAll().where { SysRoleMenuTable.menuId eq id }.any()
|
||||||
|
|
||||||
|
fun softDelete(id: Uuid) {
|
||||||
|
SysMenuTable.update({ SysMenuTable.id eq id }) {
|
||||||
|
it[SysMenuTable.deletedAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enabledMenusForRoleIds(roleIds: List<Uuid>): List<MenuFlat> {
|
||||||
|
if (roleIds.isEmpty()) return emptyList()
|
||||||
|
val rows = (SysRoleMenuTable innerJoin SysMenuTable)
|
||||||
|
.selectAll()
|
||||||
|
.where {
|
||||||
|
(SysRoleMenuTable.roleId inList roleIds) and
|
||||||
|
SysMenuTable.deletedAt.isNull() and
|
||||||
|
(SysMenuTable.status eq "ENABLED")
|
||||||
|
}
|
||||||
|
.distinct()
|
||||||
|
.toList()
|
||||||
|
return rows.map { it.toMenuFlat() }.sortedWith(compareBy<MenuFlat> { it.sort }.thenBy { it.id.toString() })
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enabledMenusForSuperAdmin(): List<MenuFlat> =
|
||||||
|
SysMenuTable.selectAll()
|
||||||
|
.where { SysMenuTable.deletedAt.isNull() and (SysMenuTable.status eq "ENABLED") }
|
||||||
|
.toList()
|
||||||
|
.map { it.toMenuFlat() }
|
||||||
|
.sortedWith(compareBy<MenuFlat> { it.sort }.thenBy { it.id.toString() })
|
||||||
|
|
||||||
|
fun buildAuthTree(flatMenus: List<MenuFlat>): List<MenuNode> {
|
||||||
|
val parentMap = flatMenus.groupBy { it.parentId }
|
||||||
|
fun build(parentId: Uuid?): List<MenuNode> =
|
||||||
|
(parentMap[parentId] ?: emptyList()).map { menu ->
|
||||||
|
MenuNode(
|
||||||
|
id = menu.id.toString(),
|
||||||
|
parentId = menu.parentId?.toString(),
|
||||||
|
type = menu.type,
|
||||||
|
title = menu.title,
|
||||||
|
name = menu.name,
|
||||||
|
path = menu.path,
|
||||||
|
component = menu.component,
|
||||||
|
icon = menu.icon,
|
||||||
|
permission = menu.permission,
|
||||||
|
sort = menu.sort,
|
||||||
|
visible = menu.visible,
|
||||||
|
keepAlive = menu.keepAlive,
|
||||||
|
children = build(menu.id),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return build(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadAllFlatMenus(): List<MenuFlat> =
|
||||||
|
SysMenuTable.selectAll()
|
||||||
|
.where { SysMenuTable.deletedAt.isNull() }
|
||||||
|
.toList()
|
||||||
|
.map { it.toMenuFlat() }
|
||||||
|
|
||||||
|
private fun buildTree(items: List<MenuFlat>): List<MenuTreeNode> {
|
||||||
|
val grouped = items.groupBy { it.parentId }
|
||||||
|
fun children(parentId: Uuid?): List<MenuTreeNode> =
|
||||||
|
(grouped[parentId] ?: emptyList()).sortedBy { it.sort }.map { menu ->
|
||||||
|
MenuTreeNode(
|
||||||
|
id = menu.id.toString(),
|
||||||
|
parentId = menu.parentId?.toString(),
|
||||||
|
type = menu.type,
|
||||||
|
typeLabel = menuTypeLabel(menu.type),
|
||||||
|
title = menu.title,
|
||||||
|
name = menu.name,
|
||||||
|
path = menu.path,
|
||||||
|
component = menu.component,
|
||||||
|
icon = menu.icon,
|
||||||
|
permission = menu.permission,
|
||||||
|
sort = menu.sort,
|
||||||
|
visible = menu.visible,
|
||||||
|
keepAlive = menu.keepAlive,
|
||||||
|
builtIn = menu.builtIn,
|
||||||
|
status = menu.status,
|
||||||
|
statusLabel = statusLabel(menu.status),
|
||||||
|
children = children(menu.id),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return children(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ResultRow.toMenuFlat() = MenuFlat(
|
||||||
|
id = this[SysMenuTable.id],
|
||||||
|
parentId = this[SysMenuTable.parentId],
|
||||||
|
type = this[SysMenuTable.type],
|
||||||
|
title = this[SysMenuTable.title],
|
||||||
|
name = this[SysMenuTable.name],
|
||||||
|
path = this[SysMenuTable.path],
|
||||||
|
component = this[SysMenuTable.component],
|
||||||
|
icon = this[SysMenuTable.icon],
|
||||||
|
permission = this[SysMenuTable.permission],
|
||||||
|
sort = this[SysMenuTable.sort],
|
||||||
|
visible = this[SysMenuTable.visible],
|
||||||
|
keepAlive = this[SysMenuTable.keepAlive],
|
||||||
|
builtIn = this[SysMenuTable.builtIn],
|
||||||
|
status = this[SysMenuTable.status],
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
@file:OptIn(ExperimentalUuidApi::class)
|
||||||
|
|
||||||
|
package com.bbit.ticket.dao.system
|
||||||
|
|
||||||
|
import com.bbit.ticket.database.system.SysOrgTable
|
||||||
|
import com.bbit.ticket.database.system.SysUserTable
|
||||||
|
import com.bbit.ticket.entity.system.CreateOrgRequest
|
||||||
|
import com.bbit.ticket.entity.system.OrgTreeNode
|
||||||
|
import com.bbit.ticket.entity.system.UpdateOrgRequest
|
||||||
|
import com.bbit.ticket.entity.common.BizException
|
||||||
|
import com.bbit.ticket.entity.common.ErrorCode
|
||||||
|
import com.bbit.ticket.entity.common.statusLabel
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import org.jetbrains.exposed.v1.core.ResultRow
|
||||||
|
import org.jetbrains.exposed.v1.core.and
|
||||||
|
import org.jetbrains.exposed.v1.core.eq
|
||||||
|
import org.jetbrains.exposed.v1.core.isNull
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.insert
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.update
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
object OrgDao {
|
||||||
|
fun tree(): List<OrgTreeNode> {
|
||||||
|
val rows = SysOrgTable.selectAll()
|
||||||
|
.where { SysOrgTable.deletedAt.isNull() }
|
||||||
|
.orderBy(SysOrgTable.sort)
|
||||||
|
.toList()
|
||||||
|
val nodes = rows.map(::toFlatNode)
|
||||||
|
return buildTree(nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun codeExists(code: String): Boolean =
|
||||||
|
SysOrgTable.selectAll().where {
|
||||||
|
(SysOrgTable.code eq code) and SysOrgTable.deletedAt.isNull()
|
||||||
|
}.any()
|
||||||
|
|
||||||
|
fun requireActive(id: Uuid): ResultRow =
|
||||||
|
SysOrgTable.selectAll().where {
|
||||||
|
(SysOrgTable.id eq id) and SysOrgTable.deletedAt.isNull()
|
||||||
|
}.singleOrNull() ?: throw BizException(
|
||||||
|
ErrorCode.ORG_NOT_FOUND.code,
|
||||||
|
ErrorCode.ORG_NOT_FOUND.message,
|
||||||
|
HttpStatusCode.NotFound,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun create(request: CreateOrgRequest, parentId: Uuid?): String {
|
||||||
|
val inserted = SysOrgTable.insert {
|
||||||
|
it[SysOrgTable.parentId] = parentId
|
||||||
|
it[SysOrgTable.name] = request.name.trim()
|
||||||
|
it[SysOrgTable.code] = request.code.trim()
|
||||||
|
it[SysOrgTable.sort] = request.sort
|
||||||
|
it[SysOrgTable.status] = request.status
|
||||||
|
it[SysOrgTable.createdAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
return inserted[SysOrgTable.id].toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(id: Uuid, request: UpdateOrgRequest, parentId: Uuid?) {
|
||||||
|
SysOrgTable.update({ SysOrgTable.id eq id }) {
|
||||||
|
it[SysOrgTable.parentId] = parentId
|
||||||
|
it[SysOrgTable.name] = request.name.trim()
|
||||||
|
it[SysOrgTable.sort] = request.sort
|
||||||
|
it[SysOrgTable.status] = request.status
|
||||||
|
it[SysOrgTable.updatedAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasChildren(id: Uuid): Boolean =
|
||||||
|
SysOrgTable.selectAll().where {
|
||||||
|
(SysOrgTable.parentId eq id) and SysOrgTable.deletedAt.isNull()
|
||||||
|
}.any()
|
||||||
|
|
||||||
|
fun hasUsers(id: Uuid): Boolean =
|
||||||
|
SysUserTable.selectAll().where {
|
||||||
|
(SysUserTable.orgId eq id) and SysUserTable.deletedAt.isNull()
|
||||||
|
}.any()
|
||||||
|
|
||||||
|
fun softDelete(id: Uuid) {
|
||||||
|
SysOrgTable.update({ SysOrgTable.id eq id }) {
|
||||||
|
it[SysOrgTable.deletedAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toFlatNode(row: ResultRow) = OrgNodeFlat(
|
||||||
|
id = row[SysOrgTable.id],
|
||||||
|
parentId = row[SysOrgTable.parentId],
|
||||||
|
name = row[SysOrgTable.name],
|
||||||
|
code = row[SysOrgTable.code],
|
||||||
|
sort = row[SysOrgTable.sort],
|
||||||
|
status = row[SysOrgTable.status],
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun buildTree(nodes: List<OrgNodeFlat>): List<OrgTreeNode> {
|
||||||
|
val byParent = nodes.groupBy { it.parentId }
|
||||||
|
fun children(parentId: Uuid?): List<OrgTreeNode> =
|
||||||
|
(byParent[parentId] ?: emptyList()).sortedBy { it.sort }.map { item ->
|
||||||
|
OrgTreeNode(
|
||||||
|
id = item.id.toString(),
|
||||||
|
parentId = item.parentId?.toString(),
|
||||||
|
name = item.name,
|
||||||
|
code = item.code,
|
||||||
|
sort = item.sort,
|
||||||
|
status = item.status,
|
||||||
|
statusLabel = statusLabel(item.status),
|
||||||
|
children = children(item.id),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return children(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class OrgNodeFlat(
|
||||||
|
val id: Uuid,
|
||||||
|
val parentId: Uuid?,
|
||||||
|
val name: String,
|
||||||
|
val code: String,
|
||||||
|
val sort: Int,
|
||||||
|
val status: String,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
package com.bbit.ticket.dao.system
|
||||||
|
|
||||||
|
import com.bbit.ticket.database.system.SysMenuTable
|
||||||
|
import com.bbit.ticket.database.system.SysRoleMenuTable
|
||||||
|
import com.bbit.ticket.database.system.SysRoleTable
|
||||||
|
import com.bbit.ticket.database.system.SysUserRoleTable
|
||||||
|
import com.bbit.ticket.entity.system.CreateRoleRequest
|
||||||
|
import com.bbit.ticket.entity.system.RoleDetail
|
||||||
|
import com.bbit.ticket.entity.system.RoleItem
|
||||||
|
import com.bbit.ticket.entity.system.UpdateRoleRequest
|
||||||
|
import com.bbit.ticket.entity.common.BizException
|
||||||
|
import com.bbit.ticket.entity.common.ErrorCode
|
||||||
|
import com.bbit.ticket.entity.common.PageResult
|
||||||
|
import com.bbit.ticket.entity.common.dataScopeLabel
|
||||||
|
import com.bbit.ticket.entity.common.statusLabel
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import org.jetbrains.exposed.v1.core.Op
|
||||||
|
import org.jetbrains.exposed.v1.core.ResultRow
|
||||||
|
import org.jetbrains.exposed.v1.core.and
|
||||||
|
import org.jetbrains.exposed.v1.core.eq
|
||||||
|
import org.jetbrains.exposed.v1.core.inList
|
||||||
|
import org.jetbrains.exposed.v1.core.isNull
|
||||||
|
import org.jetbrains.exposed.v1.core.like
|
||||||
|
import org.jetbrains.exposed.v1.core.or
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.deleteWhere
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.insert
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.update
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
object RoleDao {
|
||||||
|
fun list(page: Int, pageSize: Int, keyword: String?, status: String?): PageResult<RoleItem> {
|
||||||
|
val where = buildWhere(keyword, status)
|
||||||
|
val total = SysRoleTable.selectAll().where { where }.count()
|
||||||
|
val rows = SysRoleTable.selectAll().where { where }
|
||||||
|
.orderBy(SysRoleTable.createdAt)
|
||||||
|
.limit(pageSize)
|
||||||
|
.offset(pageOffset(page, pageSize))
|
||||||
|
.toList()
|
||||||
|
return PageResult(
|
||||||
|
items = rows.map { it.toRoleItem() },
|
||||||
|
page = page,
|
||||||
|
pageSize = pageSize,
|
||||||
|
total = total,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun codeExists(code: String): Boolean =
|
||||||
|
SysRoleTable.selectAll().where {
|
||||||
|
(SysRoleTable.code eq code) and SysRoleTable.deletedAt.isNull()
|
||||||
|
}.any()
|
||||||
|
|
||||||
|
fun create(request: CreateRoleRequest): String {
|
||||||
|
val inserted = SysRoleTable.insert {
|
||||||
|
it[SysRoleTable.name] = request.name.trim()
|
||||||
|
it[SysRoleTable.code] = request.code.trim()
|
||||||
|
it[SysRoleTable.description] = request.description.trimToNull()
|
||||||
|
it[SysRoleTable.status] = request.status
|
||||||
|
it[SysRoleTable.dataScope] = request.dataScope
|
||||||
|
it[SysRoleTable.createdAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
return inserted[SysRoleTable.id].toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requireActive(id: Uuid): ResultRow =
|
||||||
|
SysRoleTable.selectAll().where {
|
||||||
|
(SysRoleTable.id eq id) and SysRoleTable.deletedAt.isNull()
|
||||||
|
}.singleOrNull() ?: throw BizException(
|
||||||
|
ErrorCode.ROLE_NOT_FOUND.code,
|
||||||
|
ErrorCode.ROLE_NOT_FOUND.message,
|
||||||
|
HttpStatusCode.NotFound,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun detail(id: Uuid): RoleDetail {
|
||||||
|
val role = requireActive(id)
|
||||||
|
val menuIds = SysRoleMenuTable.selectAll().where { SysRoleMenuTable.roleId eq id }
|
||||||
|
.map { it[SysRoleMenuTable.menuId].toString() }
|
||||||
|
return role.toRoleDetail(menuIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(id: Uuid, request: UpdateRoleRequest) {
|
||||||
|
SysRoleTable.update({ SysRoleTable.id eq id }) {
|
||||||
|
it[SysRoleTable.name] = request.name.trim()
|
||||||
|
it[SysRoleTable.description] = request.description.trimToNull()
|
||||||
|
it[SysRoleTable.status] = request.status
|
||||||
|
it[SysRoleTable.dataScope] = request.dataScope
|
||||||
|
it[SysRoleTable.updatedAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun inUse(id: Uuid): Boolean =
|
||||||
|
SysUserRoleTable.selectAll().where { SysUserRoleTable.roleId eq id }.any()
|
||||||
|
|
||||||
|
fun softDelete(id: Uuid) {
|
||||||
|
SysRoleTable.update({ SysRoleTable.id eq id }) {
|
||||||
|
it[SysRoleTable.deletedAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
SysRoleMenuTable.deleteWhere { roleId eq id }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun countEnabledMenus(menuIds: List<Uuid>): Long =
|
||||||
|
SysMenuTable.selectAll().where {
|
||||||
|
(SysMenuTable.id inList menuIds) and
|
||||||
|
SysMenuTable.deletedAt.isNull() and
|
||||||
|
(SysMenuTable.status eq "ENABLED")
|
||||||
|
}.count()
|
||||||
|
|
||||||
|
fun replaceMenus(id: Uuid, menuIds: List<Uuid>) {
|
||||||
|
SysRoleMenuTable.deleteWhere { roleId eq id }
|
||||||
|
menuIds.forEach { menuId ->
|
||||||
|
SysRoleMenuTable.insertIgnore {
|
||||||
|
it[roleId] = id
|
||||||
|
it[SysRoleMenuTable.menuId] = menuId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildWhere(keyword: String?, status: String?): Op<Boolean> {
|
||||||
|
var where: Op<Boolean> = SysRoleTable.deletedAt.isNull()
|
||||||
|
if (!keyword.isNullOrBlank()) {
|
||||||
|
where = where and ((SysRoleTable.name like "%$keyword%") or (SysRoleTable.code like "%$keyword%"))
|
||||||
|
}
|
||||||
|
if (!status.isNullOrBlank()) {
|
||||||
|
where = where and (SysRoleTable.status eq status)
|
||||||
|
}
|
||||||
|
return where
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ResultRow.toRoleItem() = RoleItem(
|
||||||
|
id = this[SysRoleTable.id].toString(),
|
||||||
|
name = this[SysRoleTable.name],
|
||||||
|
code = this[SysRoleTable.code],
|
||||||
|
description = this[SysRoleTable.description],
|
||||||
|
status = this[SysRoleTable.status],
|
||||||
|
statusLabel = statusLabel(this[SysRoleTable.status]),
|
||||||
|
dataScope = this[SysRoleTable.dataScope],
|
||||||
|
dataScopeLabel = dataScopeLabel(this[SysRoleTable.dataScope]),
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun ResultRow.toRoleDetail(menuIds: List<String>) = RoleDetail(
|
||||||
|
id = this[SysRoleTable.id].toString(),
|
||||||
|
name = this[SysRoleTable.name],
|
||||||
|
code = this[SysRoleTable.code],
|
||||||
|
description = this[SysRoleTable.description],
|
||||||
|
status = this[SysRoleTable.status],
|
||||||
|
statusLabel = statusLabel(this[SysRoleTable.status]),
|
||||||
|
dataScope = this[SysRoleTable.dataScope],
|
||||||
|
dataScopeLabel = dataScopeLabel(this[SysRoleTable.dataScope]),
|
||||||
|
menuIds = menuIds,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.bbit.ticket.dao.system
|
||||||
|
|
||||||
|
internal fun pageOffset(page: Int, pageSize: Int): Long = ((page - 1) * pageSize).toLong()
|
||||||
|
|
||||||
|
internal fun String?.trimToNull(): String? = this?.trim()?.takeIf { it.isNotEmpty() }
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
package com.bbit.ticket.dao.system
|
||||||
|
|
||||||
|
import com.bbit.ticket.database.system.SysOrgTable
|
||||||
|
import com.bbit.ticket.database.system.SysRoleTable
|
||||||
|
import com.bbit.ticket.database.system.SysUserRoleTable
|
||||||
|
import com.bbit.ticket.database.system.SysUserTable
|
||||||
|
import com.bbit.ticket.entity.system.CreateUserRequest
|
||||||
|
import com.bbit.ticket.entity.system.UserDetailResponse
|
||||||
|
import com.bbit.ticket.entity.system.UserListItem
|
||||||
|
import com.bbit.ticket.entity.system.UpdateUserRequest
|
||||||
|
import com.bbit.ticket.entity.common.BizException
|
||||||
|
import com.bbit.ticket.entity.common.ErrorCode
|
||||||
|
import com.bbit.ticket.entity.common.PageResult
|
||||||
|
import com.bbit.ticket.entity.common.statusLabel
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import org.jetbrains.exposed.v1.core.Op
|
||||||
|
import org.jetbrains.exposed.v1.core.ResultRow
|
||||||
|
import org.jetbrains.exposed.v1.core.and
|
||||||
|
import org.jetbrains.exposed.v1.core.eq
|
||||||
|
import org.jetbrains.exposed.v1.core.inList
|
||||||
|
import org.jetbrains.exposed.v1.core.isNull
|
||||||
|
import org.jetbrains.exposed.v1.core.like
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.Query
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.deleteWhere
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.insert
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.selectAll
|
||||||
|
import org.jetbrains.exposed.v1.jdbc.update
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
|
object UserDao {
|
||||||
|
fun list(
|
||||||
|
page: Int,
|
||||||
|
pageSize: Int,
|
||||||
|
username: String?,
|
||||||
|
nickname: String?,
|
||||||
|
status: String?,
|
||||||
|
orgId: Uuid?,
|
||||||
|
): PageResult<UserListItem> {
|
||||||
|
val where = buildWhere(username, nickname, status, orgId)
|
||||||
|
val total = SysUserTable.selectAll().where { where }.count()
|
||||||
|
val rows = SysUserTable.selectAll()
|
||||||
|
.where { where }
|
||||||
|
.orderBy(SysUserTable.createdAt)
|
||||||
|
.limit(pageSize)
|
||||||
|
.offset(pageOffset(page, pageSize))
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
val roleMap = findRoleCodesByUserIds(rows.map { it[SysUserTable.id] })
|
||||||
|
return PageResult(
|
||||||
|
items = rows.map { row -> row.toUserListItem(roleMap[row[SysUserTable.id]] ?: emptyList()) },
|
||||||
|
page = page,
|
||||||
|
pageSize = pageSize,
|
||||||
|
total = total,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findByUsername(username: String): ResultRow? =
|
||||||
|
activeUsers().where { SysUserTable.username eq username }.singleOrNull()
|
||||||
|
|
||||||
|
fun requireActive(id: Uuid): ResultRow =
|
||||||
|
activeUsers().where { SysUserTable.id eq id }.singleOrNull()
|
||||||
|
?: throw BizException(
|
||||||
|
ErrorCode.USER_NOT_FOUND.code,
|
||||||
|
ErrorCode.USER_NOT_FOUND.message,
|
||||||
|
HttpStatusCode.NotFound,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun create(request: CreateUserRequest, passwordHash: String, orgId: Uuid?): String {
|
||||||
|
val now = OffsetDateTime.now()
|
||||||
|
val row = SysUserTable.insert {
|
||||||
|
it[SysUserTable.username] = request.username.trim()
|
||||||
|
it[SysUserTable.passwordHash] = passwordHash
|
||||||
|
it[SysUserTable.nickname] = request.nickname.trimToNull()
|
||||||
|
it[SysUserTable.realName] = request.realName.trimToNull()
|
||||||
|
it[SysUserTable.phone] = request.phone.trimToNull()
|
||||||
|
it[SysUserTable.email] = request.email.trimToNull()
|
||||||
|
it[SysUserTable.avatar] = request.avatar.trimToNull()
|
||||||
|
it[SysUserTable.orgId] = orgId
|
||||||
|
it[SysUserTable.status] = request.status
|
||||||
|
it[SysUserTable.tokenVersion] = 1
|
||||||
|
it[SysUserTable.createdAt] = now
|
||||||
|
}
|
||||||
|
return row[SysUserTable.id].toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun detail(id: Uuid): UserDetailResponse {
|
||||||
|
val user = requireActive(id)
|
||||||
|
val roleIds = SysUserRoleTable.selectAll()
|
||||||
|
.where { SysUserRoleTable.userId eq id }
|
||||||
|
.map { it[SysUserRoleTable.roleId].toString() }
|
||||||
|
return user.toUserDetail(roleIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateProfile(id: Uuid, request: UpdateUserRequest, orgId: Uuid?) {
|
||||||
|
SysUserTable.update({ SysUserTable.id eq id }) {
|
||||||
|
it[SysUserTable.nickname] = request.nickname.trimToNull()
|
||||||
|
it[SysUserTable.realName] = request.realName.trimToNull()
|
||||||
|
it[SysUserTable.phone] = request.phone.trimToNull()
|
||||||
|
it[SysUserTable.email] = request.email.trimToNull()
|
||||||
|
it[SysUserTable.avatar] = request.avatar.trimToNull()
|
||||||
|
it[SysUserTable.orgId] = orgId
|
||||||
|
it[SysUserTable.updatedAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun softDelete(id: Uuid) {
|
||||||
|
SysUserTable.update({ SysUserTable.id eq id }) {
|
||||||
|
it[SysUserTable.deletedAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
SysUserRoleTable.deleteWhere { userId eq id }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateStatus(id: Uuid, status: String) {
|
||||||
|
SysUserTable.update({ SysUserTable.id eq id }) {
|
||||||
|
it[SysUserTable.status] = status
|
||||||
|
it[SysUserTable.updatedAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updatePassword(id: Uuid, passwordHash: String, nextTokenVersion: Int) {
|
||||||
|
SysUserTable.update({ SysUserTable.id eq id }) {
|
||||||
|
it[SysUserTable.passwordHash] = passwordHash
|
||||||
|
it[SysUserTable.tokenVersion] = nextTokenVersion
|
||||||
|
it[SysUserTable.updatedAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun replaceRoles(id: Uuid, roleIds: List<Uuid>) {
|
||||||
|
SysUserRoleTable.deleteWhere { userId eq id }
|
||||||
|
roleIds.forEach { roleId ->
|
||||||
|
SysUserRoleTable.insertIgnore {
|
||||||
|
it[userId] = id
|
||||||
|
it[SysUserRoleTable.roleId] = roleId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun countEnabledRoles(roleIds: List<Uuid>): Long =
|
||||||
|
SysRoleTable.selectAll().where {
|
||||||
|
(SysRoleTable.id inList roleIds) and
|
||||||
|
(SysRoleTable.status eq "ENABLED") and
|
||||||
|
SysRoleTable.deletedAt.isNull()
|
||||||
|
}.count()
|
||||||
|
|
||||||
|
fun orgExists(orgId: Uuid): Boolean =
|
||||||
|
SysOrgTable.selectAll().where {
|
||||||
|
(SysOrgTable.id eq orgId) and SysOrgTable.deletedAt.isNull()
|
||||||
|
}.any()
|
||||||
|
|
||||||
|
fun findEnabledRoleCodes(userId: Uuid): List<String> =
|
||||||
|
(SysUserRoleTable innerJoin SysRoleTable)
|
||||||
|
.selectAll()
|
||||||
|
.where {
|
||||||
|
(SysUserRoleTable.userId eq userId) and
|
||||||
|
SysRoleTable.deletedAt.isNull() and
|
||||||
|
(SysRoleTable.status eq "ENABLED")
|
||||||
|
}
|
||||||
|
.map { it[SysRoleTable.code] }
|
||||||
|
|
||||||
|
fun findEnabledRoleIds(userId: Uuid): List<Uuid> =
|
||||||
|
(SysUserRoleTable innerJoin SysRoleTable)
|
||||||
|
.selectAll()
|
||||||
|
.where {
|
||||||
|
(SysUserRoleTable.userId eq userId) and
|
||||||
|
SysRoleTable.deletedAt.isNull() and
|
||||||
|
(SysRoleTable.status eq "ENABLED")
|
||||||
|
}
|
||||||
|
.map { it[SysRoleTable.id] }
|
||||||
|
|
||||||
|
fun updateLoginInfo(userId: Uuid, loginIp: String?) {
|
||||||
|
SysUserTable.update({ SysUserTable.id eq userId }) {
|
||||||
|
it[SysUserTable.lastLoginAt] = OffsetDateTime.now()
|
||||||
|
it[SysUserTable.lastLoginIp] = loginIp
|
||||||
|
it[SysUserTable.updatedAt] = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun activeUsers(): Query =
|
||||||
|
SysUserTable.selectAll().where { SysUserTable.deletedAt.isNull() }
|
||||||
|
|
||||||
|
private fun buildWhere(username: String?, nickname: String?, status: String?, orgId: Uuid?): Op<Boolean> {
|
||||||
|
var where: Op<Boolean> = SysUserTable.deletedAt.isNull()
|
||||||
|
if (!username.isNullOrBlank()) {
|
||||||
|
where = where and (SysUserTable.username like "%$username%")
|
||||||
|
}
|
||||||
|
if (!nickname.isNullOrBlank()) {
|
||||||
|
where = where and (SysUserTable.nickname like "%$nickname%")
|
||||||
|
}
|
||||||
|
if (!status.isNullOrBlank()) {
|
||||||
|
where = where and (SysUserTable.status eq status)
|
||||||
|
}
|
||||||
|
if (orgId != null) {
|
||||||
|
where = where and (SysUserTable.orgId eq orgId)
|
||||||
|
}
|
||||||
|
return where
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findRoleCodesByUserIds(userIds: List<Uuid>): Map<Uuid, List<String>> {
|
||||||
|
if (userIds.isEmpty()) return emptyMap()
|
||||||
|
return (SysUserRoleTable innerJoin SysRoleTable).selectAll()
|
||||||
|
.where {
|
||||||
|
(SysUserRoleTable.userId inList userIds) and
|
||||||
|
SysRoleTable.deletedAt.isNull()
|
||||||
|
}
|
||||||
|
.groupBy { it[SysUserRoleTable.userId] }
|
||||||
|
.mapValues { entry -> entry.value.map { row -> row[SysRoleTable.code] }.distinct() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ResultRow.toUserListItem(roleCodes: List<String>) = UserListItem(
|
||||||
|
id = this[SysUserTable.id].toString(),
|
||||||
|
username = this[SysUserTable.username],
|
||||||
|
nickname = this[SysUserTable.nickname],
|
||||||
|
realName = this[SysUserTable.realName],
|
||||||
|
orgId = this[SysUserTable.orgId]?.toString(),
|
||||||
|
status = this[SysUserTable.status],
|
||||||
|
statusLabel = statusLabel(this[SysUserTable.status]),
|
||||||
|
roleCodes = roleCodes,
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun ResultRow.toUserDetail(roleIds: List<String>) = UserDetailResponse(
|
||||||
|
id = this[SysUserTable.id].toString(),
|
||||||
|
username = this[SysUserTable.username],
|
||||||
|
nickname = this[SysUserTable.nickname],
|
||||||
|
realName = this[SysUserTable.realName],
|
||||||
|
phone = this[SysUserTable.phone],
|
||||||
|
email = this[SysUserTable.email],
|
||||||
|
avatar = this[SysUserTable.avatar],
|
||||||
|
orgId = this[SysUserTable.orgId]?.toString(),
|
||||||
|
status = this[SysUserTable.status],
|
||||||
|
statusLabel = statusLabel(this[SysUserTable.status]),
|
||||||
|
roleIds = roleIds,
|
||||||
|
)
|
||||||
|
}
|
||||||
+3
-1
@@ -1,6 +1,9 @@
|
|||||||
|
@file:OptIn(ExperimentalUuidApi::class)
|
||||||
|
|
||||||
package com.bbit.ticket.entity.system
|
package com.bbit.ticket.entity.system
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class LoginRequest(
|
data class LoginRequest(
|
||||||
@@ -48,4 +51,3 @@ data class MenuNode(
|
|||||||
val keepAlive: Boolean,
|
val keepAlive: Boolean,
|
||||||
val children: List<MenuNode> = emptyList(),
|
val children: List<MenuNode> = emptyList(),
|
||||||
)
|
)
|
||||||
|
|
||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
package com.bbit.ticket.entity.system
|
package com.bbit.ticket.entity.system
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
package com.bbit.ticket.entity.system
|
package com.bbit.ticket.entity.system
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
package com.bbit.ticket.entity.system
|
package com.bbit.ticket.entity.system
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
-1
@@ -1,5 +1,4 @@
|
|||||||
package com.bbit.ticket.entity.system
|
package com.bbit.ticket.entity.system
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@@ -2,30 +2,19 @@
|
|||||||
|
|
||||||
package com.bbit.ticket.service.system
|
package com.bbit.ticket.service.system
|
||||||
|
|
||||||
|
import com.bbit.ticket.dao.system.MenuDao
|
||||||
|
import com.bbit.ticket.dao.system.UserDao
|
||||||
|
import com.bbit.ticket.database.system.SysUserTable
|
||||||
import com.bbit.ticket.entity.common.BizException
|
import com.bbit.ticket.entity.common.BizException
|
||||||
import com.bbit.ticket.entity.common.ErrorCode
|
import com.bbit.ticket.entity.common.ErrorCode
|
||||||
import com.bbit.ticket.database.system.SysMenuTable
|
|
||||||
import com.bbit.ticket.database.system.SysRoleMenuTable
|
|
||||||
import com.bbit.ticket.database.system.SysRoleTable
|
|
||||||
import com.bbit.ticket.database.system.SysUserRoleTable
|
|
||||||
import com.bbit.ticket.database.system.SysUserTable
|
|
||||||
import com.bbit.ticket.entity.system.CurrentUserProfile
|
import com.bbit.ticket.entity.system.CurrentUserProfile
|
||||||
import com.bbit.ticket.entity.system.LoginRequest
|
import com.bbit.ticket.entity.system.LoginRequest
|
||||||
import com.bbit.ticket.entity.system.LoginResponse
|
import com.bbit.ticket.entity.system.LoginResponse
|
||||||
import com.bbit.ticket.entity.system.MeResponse
|
import com.bbit.ticket.entity.system.MeResponse
|
||||||
import com.bbit.ticket.entity.system.MenuNode
|
|
||||||
import com.bbit.ticket.plugins.dbQuery
|
import com.bbit.ticket.plugins.dbQuery
|
||||||
import com.bbit.ticket.utils.CurrentUser
|
import com.bbit.ticket.utils.CurrentUser
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import org.jetbrains.exposed.v1.core.and
|
|
||||||
import org.jetbrains.exposed.v1.core.eq
|
|
||||||
import org.jetbrains.exposed.v1.core.inList
|
|
||||||
import org.jetbrains.exposed.v1.core.isNull
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.update
|
|
||||||
import java.time.OffsetDateTime
|
|
||||||
import kotlin.uuid.ExperimentalUuidApi
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
import kotlin.uuid.Uuid
|
|
||||||
|
|
||||||
object AuthService {
|
object AuthService {
|
||||||
suspend fun login(request: LoginRequest, loginIp: String?): LoginResponse {
|
suspend fun login(request: LoginRequest, loginIp: String?): LoginResponse {
|
||||||
@@ -34,17 +23,13 @@ object AuthService {
|
|||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "用户名和密码不能为空", HttpStatusCode.BadRequest)
|
throw BizException(ErrorCode.BAD_REQUEST.code, "用户名和密码不能为空", HttpStatusCode.BadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
val user = dbQuery {
|
val user = dbQuery { UserDao.findByUsername(username) } ?: throw BizException(
|
||||||
SysUserTable.selectAll()
|
|
||||||
.where { (SysUserTable.username eq username) and SysUserTable.deletedAt.isNull() }
|
|
||||||
.singleOrNull()
|
|
||||||
} ?: throw BizException(
|
|
||||||
ErrorCode.USERNAME_OR_PASSWORD_INVALID.code,
|
ErrorCode.USERNAME_OR_PASSWORD_INVALID.code,
|
||||||
ErrorCode.USERNAME_OR_PASSWORD_INVALID.message,
|
ErrorCode.USERNAME_OR_PASSWORD_INVALID.message,
|
||||||
HttpStatusCode.BadRequest,
|
HttpStatusCode.BadRequest,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!PasswordService.matches(request.password, user[SysUserTable.passwordHash])) {
|
if (!PasswordService.matches(request.password, user[com.bbit.ticket.database.system.SysUserTable.passwordHash])) {
|
||||||
throw BizException(
|
throw BizException(
|
||||||
ErrorCode.USERNAME_OR_PASSWORD_INVALID.code,
|
ErrorCode.USERNAME_OR_PASSWORD_INVALID.code,
|
||||||
ErrorCode.USERNAME_OR_PASSWORD_INVALID.message,
|
ErrorCode.USERNAME_OR_PASSWORD_INVALID.message,
|
||||||
@@ -52,50 +37,31 @@ object AuthService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user[SysUserTable.status] != "ENABLED") {
|
if (user[com.bbit.ticket.database.system.SysUserTable.status] != "ENABLED") {
|
||||||
throw BizException(ErrorCode.USER_DISABLED.code, ErrorCode.USER_DISABLED.message, HttpStatusCode.BadRequest)
|
throw BizException(ErrorCode.USER_DISABLED.code, ErrorCode.USER_DISABLED.message, HttpStatusCode.BadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
val userId = user[SysUserTable.id]
|
val userId = user[com.bbit.ticket.database.system.SysUserTable.id]
|
||||||
val roleCodes = dbQuery {
|
val roleCodes = dbQuery { UserDao.findEnabledRoleCodes(userId) }
|
||||||
(SysUserRoleTable innerJoin SysRoleTable)
|
|
||||||
.selectAll()
|
|
||||||
.where {
|
|
||||||
(SysUserRoleTable.userId eq userId) and
|
|
||||||
SysRoleTable.deletedAt.isNull() and
|
|
||||||
(SysRoleTable.status eq "ENABLED")
|
|
||||||
}
|
|
||||||
.map { it[SysRoleTable.code] }
|
|
||||||
}
|
|
||||||
|
|
||||||
val (accessToken, expiresIn) = JwtService.issueAccessToken(
|
val (accessToken, expiresIn) = JwtService.issueAccessToken(
|
||||||
userId = userId.toString(),
|
userId = userId.toString(),
|
||||||
username = user[SysUserTable.username],
|
username = user[com.bbit.ticket.database.system.SysUserTable.username],
|
||||||
orgId = user[SysUserTable.orgId]?.toString(),
|
orgId = user[com.bbit.ticket.database.system.SysUserTable.orgId]?.toString(),
|
||||||
roles = roleCodes,
|
roles = roleCodes,
|
||||||
tokenVersion = user[SysUserTable.tokenVersion],
|
tokenVersion = user[com.bbit.ticket.database.system.SysUserTable.tokenVersion],
|
||||||
)
|
)
|
||||||
|
|
||||||
dbQuery {
|
dbQuery { UserDao.updateLoginInfo(userId, loginIp) }
|
||||||
SysUserTable.update({ SysUserTable.id eq userId }) {
|
|
||||||
it[lastLoginAt] = OffsetDateTime.now()
|
|
||||||
it[lastLoginIp] = loginIp
|
|
||||||
it[updatedAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return LoginResponse(accessToken = accessToken, expiresIn = expiresIn)
|
return LoginResponse(accessToken = accessToken, expiresIn = expiresIn)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun me(currentUser: CurrentUser): MeResponse {
|
suspend fun me(currentUser: CurrentUser): MeResponse {
|
||||||
val userRow = dbQuery {
|
val userRow = dbQuery { UserDao.requireActive(currentUser.id) }
|
||||||
SysUserTable.selectAll()
|
|
||||||
.where { (SysUserTable.id eq currentUser.id) and SysUserTable.deletedAt.isNull() }
|
|
||||||
.single()
|
|
||||||
}
|
|
||||||
|
|
||||||
val allMenus = loadMenusForUser(currentUser)
|
val allMenus = loadMenusForUser(currentUser)
|
||||||
val menuTree = buildMenuTree(allMenus)
|
val menuTree = MenuDao.buildAuthTree(allMenus)
|
||||||
val permissions = allMenus.mapNotNull { it.permission }.toSet()
|
val permissions = allMenus.mapNotNull { it.permission }.toSet()
|
||||||
|
|
||||||
return MeResponse(
|
return MeResponse(
|
||||||
@@ -112,97 +78,11 @@ object AuthService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loadMenusForUser(currentUser: CurrentUser): List<MenuFlat> {
|
private suspend fun loadMenusForUser(currentUser: CurrentUser) = dbQuery {
|
||||||
val rows = if (currentUser.isSuperAdmin) {
|
if (currentUser.isSuperAdmin) {
|
||||||
dbQuery {
|
MenuDao.enabledMenusForSuperAdmin()
|
||||||
SysMenuTable.selectAll()
|
|
||||||
.where { SysMenuTable.deletedAt.isNull() and (SysMenuTable.status eq "ENABLED") }
|
|
||||||
.toList()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
val roleIds = dbQuery {
|
MenuDao.enabledMenusForRoleIds(UserDao.findEnabledRoleIds(currentUser.id))
|
||||||
(SysUserRoleTable innerJoin SysRoleTable)
|
|
||||||
.selectAll()
|
|
||||||
.where {
|
|
||||||
(SysUserRoleTable.userId eq currentUser.id) and
|
|
||||||
SysRoleTable.deletedAt.isNull() and
|
|
||||||
(SysRoleTable.status eq "ENABLED")
|
|
||||||
}
|
|
||||||
.map { it[SysRoleTable.id] }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (roleIds.isEmpty()) {
|
|
||||||
emptyList()
|
|
||||||
} else {
|
|
||||||
dbQuery {
|
|
||||||
(SysRoleMenuTable innerJoin SysMenuTable)
|
|
||||||
.selectAll()
|
|
||||||
.where {
|
|
||||||
(SysRoleMenuTable.roleId inList roleIds) and
|
|
||||||
SysMenuTable.deletedAt.isNull() and
|
|
||||||
(SysMenuTable.status eq "ENABLED")
|
|
||||||
}
|
|
||||||
.distinct()
|
|
||||||
.toList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows.map { row ->
|
|
||||||
MenuFlat(
|
|
||||||
id = row[SysMenuTable.id],
|
|
||||||
parentId = row[SysMenuTable.parentId],
|
|
||||||
type = row[SysMenuTable.type],
|
|
||||||
title = row[SysMenuTable.title],
|
|
||||||
name = row[SysMenuTable.name],
|
|
||||||
path = row[SysMenuTable.path],
|
|
||||||
component = row[SysMenuTable.component],
|
|
||||||
icon = row[SysMenuTable.icon],
|
|
||||||
permission = row[SysMenuTable.permission],
|
|
||||||
sort = row[SysMenuTable.sort],
|
|
||||||
visible = row[SysMenuTable.visible],
|
|
||||||
keepAlive = row[SysMenuTable.keepAlive],
|
|
||||||
)
|
|
||||||
}.sortedWith(compareBy<MenuFlat> { it.sort }.thenBy { it.id.toString() })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildMenuTree(flatMenus: List<MenuFlat>): List<MenuNode> {
|
|
||||||
val parentMap = flatMenus.groupBy { it.parentId }
|
|
||||||
|
|
||||||
fun build(parentId: Uuid?): List<MenuNode> =
|
|
||||||
(parentMap[parentId] ?: emptyList()).map { menu ->
|
|
||||||
MenuNode(
|
|
||||||
id = menu.id.toString(),
|
|
||||||
parentId = menu.parentId?.toString(),
|
|
||||||
type = menu.type,
|
|
||||||
title = menu.title,
|
|
||||||
name = menu.name,
|
|
||||||
path = menu.path,
|
|
||||||
component = menu.component,
|
|
||||||
icon = menu.icon,
|
|
||||||
permission = menu.permission,
|
|
||||||
sort = menu.sort,
|
|
||||||
visible = menu.visible,
|
|
||||||
keepAlive = menu.keepAlive,
|
|
||||||
children = build(menu.id),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return build(null)
|
|
||||||
}
|
|
||||||
private data class MenuFlat(
|
|
||||||
val id: Uuid,
|
|
||||||
val parentId: Uuid?,
|
|
||||||
val type: String,
|
|
||||||
val title: String,
|
|
||||||
val name: String?,
|
|
||||||
val path: String?,
|
|
||||||
val component: String?,
|
|
||||||
val icon: String?,
|
|
||||||
val permission: String?,
|
|
||||||
val sort: Int,
|
|
||||||
val visible: Boolean,
|
|
||||||
val keepAlive: Boolean,
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,7 @@
|
|||||||
|
|
||||||
package com.bbit.ticket.service.system
|
package com.bbit.ticket.service.system
|
||||||
|
|
||||||
import com.bbit.ticket.database.system.SysDictItemTable
|
import com.bbit.ticket.dao.system.DictDao
|
||||||
import com.bbit.ticket.database.system.SysDictItemTable.color
|
|
||||||
import com.bbit.ticket.database.system.SysDictItemTable.label
|
|
||||||
import com.bbit.ticket.database.system.SysDictTypeTable
|
|
||||||
import com.bbit.ticket.entity.system.CreateDictItemRequest
|
import com.bbit.ticket.entity.system.CreateDictItemRequest
|
||||||
import com.bbit.ticket.entity.system.CreateDictTypeRequest
|
import com.bbit.ticket.entity.system.CreateDictTypeRequest
|
||||||
import com.bbit.ticket.entity.system.DictItem
|
import com.bbit.ticket.entity.system.DictItem
|
||||||
@@ -15,172 +12,54 @@ import com.bbit.ticket.entity.system.UpdateDictTypeRequest
|
|||||||
import com.bbit.ticket.entity.common.BizException
|
import com.bbit.ticket.entity.common.BizException
|
||||||
import com.bbit.ticket.entity.common.ErrorCode
|
import com.bbit.ticket.entity.common.ErrorCode
|
||||||
import com.bbit.ticket.entity.common.PageResult
|
import com.bbit.ticket.entity.common.PageResult
|
||||||
import com.bbit.ticket.entity.common.statusLabel
|
|
||||||
import com.bbit.ticket.plugins.dbQuery
|
import com.bbit.ticket.plugins.dbQuery
|
||||||
import com.bbit.ticket.utils.parseUuid
|
import com.bbit.ticket.utils.parseUuid
|
||||||
import io.ktor.http.HttpStatusCode
|
|
||||||
import org.jetbrains.exposed.v1.core.ResultRow
|
|
||||||
import org.jetbrains.exposed.v1.core.and
|
|
||||||
import org.jetbrains.exposed.v1.core.eq
|
|
||||||
import org.jetbrains.exposed.v1.core.isNull
|
|
||||||
import org.jetbrains.exposed.v1.core.like
|
|
||||||
import org.jetbrains.exposed.v1.core.or
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.insert
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.update
|
|
||||||
import java.time.OffsetDateTime
|
|
||||||
import kotlin.uuid.ExperimentalUuidApi
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
import kotlin.uuid.Uuid
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
object DictService {
|
object DictService {
|
||||||
suspend fun listTypes(page: Int, pageSize: Int, keyword: String?): PageResult<DictTypeItem> = dbQuery {
|
suspend fun listTypes(page: Int, pageSize: Int, keyword: String?): PageResult<DictTypeItem> =
|
||||||
var where = SysDictTypeTable.deletedAt.isNull()
|
dbQuery { DictDao.listTypes(page, pageSize, keyword) }
|
||||||
if (!keyword.isNullOrBlank()) {
|
|
||||||
where = where and ((SysDictTypeTable.code like "%$keyword%") or (SysDictTypeTable.name like "%$keyword%"))
|
|
||||||
}
|
|
||||||
val total = SysDictTypeTable.selectAll().where { where }.count()
|
|
||||||
val rows = SysDictTypeTable.selectAll().where { where }
|
|
||||||
.orderBy(SysDictTypeTable.createdAt)
|
|
||||||
.limit(pageSize)
|
|
||||||
.offset(((page - 1) * pageSize).toLong())
|
|
||||||
.toList()
|
|
||||||
PageResult(
|
|
||||||
items = rows.map {
|
|
||||||
DictTypeItem(
|
|
||||||
id = it[SysDictTypeTable.id].toString(),
|
|
||||||
code = it[SysDictTypeTable.code],
|
|
||||||
name = it[SysDictTypeTable.name],
|
|
||||||
status = it[SysDictTypeTable.status],
|
|
||||||
statusLabel = statusLabel(it[SysDictTypeTable.status]),
|
|
||||||
remark = it[SysDictTypeTable.remark],
|
|
||||||
)
|
|
||||||
},
|
|
||||||
page = page,
|
|
||||||
pageSize = pageSize,
|
|
||||||
total = total,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun createType(request: CreateDictTypeRequest): String = dbQuery {
|
suspend fun createType(request: CreateDictTypeRequest): String = dbQuery {
|
||||||
if (request.code.trim().isBlank() || request.name.trim().isBlank()) {
|
if (request.code.trim().isBlank() || request.name.trim().isBlank()) {
|
||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "字典类型编码和名称不能为空")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "字典类型编码和名称不能为空")
|
||||||
}
|
}
|
||||||
val exists = SysDictTypeTable.selectAll().where {
|
if (DictDao.typeCodeExists(request.code.trim())) {
|
||||||
(SysDictTypeTable.code eq request.code.trim()) and SysDictTypeTable.deletedAt.isNull()
|
throw BizException(ErrorCode.DATA_CONFLICT.code, "字典类型编码已存在")
|
||||||
}.any()
|
|
||||||
if (exists) throw BizException(ErrorCode.DATA_CONFLICT.code, "字典类型编码已存在")
|
|
||||||
val inserted = SysDictTypeTable.insert {
|
|
||||||
it[code] = request.code.trim()
|
|
||||||
it[name] = request.name.trim()
|
|
||||||
it[status] = request.status
|
|
||||||
it[remark] = request.remark?.trim()
|
|
||||||
it[createdAt] = OffsetDateTime.now()
|
|
||||||
}
|
}
|
||||||
inserted[SysDictTypeTable.id].toString()
|
DictDao.createType(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateType(id: Uuid, request: UpdateDictTypeRequest) = dbQuery {
|
suspend fun updateType(id: Uuid, request: UpdateDictTypeRequest) = dbQuery {
|
||||||
requireType(id)
|
DictDao.requireType(id)
|
||||||
SysDictTypeTable.update({ SysDictTypeTable.id eq id }) {
|
DictDao.updateType(id, request)
|
||||||
it[name] = request.name.trim()
|
|
||||||
it[status] = request.status
|
|
||||||
it[remark] = request.remark?.trim()
|
|
||||||
it[updatedAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteType(id: Uuid) = dbQuery {
|
suspend fun deleteType(id: Uuid) = dbQuery {
|
||||||
requireType(id)
|
DictDao.requireType(id)
|
||||||
val hasItems = SysDictItemTable.selectAll().where {
|
if (DictDao.typeHasItems(id)) throw BizException(ErrorCode.BAD_REQUEST.code, "字典类型下存在字典项,不能删除")
|
||||||
(SysDictItemTable.typeId eq id) and SysDictItemTable.deletedAt.isNull()
|
DictDao.softDeleteType(id)
|
||||||
}.any()
|
|
||||||
if (hasItems) throw BizException(ErrorCode.BAD_REQUEST.code, "字典类型下存在字典项,不能删除")
|
|
||||||
SysDictTypeTable.update({ SysDictTypeTable.id eq id }) {
|
|
||||||
it[deletedAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun listItems(page: Int, pageSize: Int, typeId: Uuid?): PageResult<DictItem> = dbQuery {
|
suspend fun listItems(page: Int, pageSize: Int, typeId: Uuid?): PageResult<DictItem> =
|
||||||
var where = SysDictItemTable.deletedAt.isNull()
|
dbQuery { DictDao.listItems(page, pageSize, typeId) }
|
||||||
if (typeId != null) where = where and (SysDictItemTable.typeId eq typeId)
|
|
||||||
val total = SysDictItemTable.selectAll().where { where }.count()
|
|
||||||
val rows = SysDictItemTable.selectAll().where { where }
|
|
||||||
.orderBy(SysDictItemTable.sort)
|
|
||||||
.limit(pageSize)
|
|
||||||
.offset(((page - 1) * pageSize).toLong())
|
|
||||||
.toList()
|
|
||||||
PageResult(
|
|
||||||
items = rows.map {
|
|
||||||
DictItem(
|
|
||||||
id = it[SysDictItemTable.id].toString(),
|
|
||||||
typeId = it[SysDictItemTable.typeId].toString(),
|
|
||||||
label = it[label],
|
|
||||||
value = it[SysDictItemTable.value],
|
|
||||||
color = it[color],
|
|
||||||
sort = it[SysDictItemTable.sort],
|
|
||||||
status = it[SysDictItemTable.status],
|
|
||||||
statusLabel = statusLabel(it[SysDictItemTable.status]),
|
|
||||||
remark = it[SysDictItemTable.remark],
|
|
||||||
)
|
|
||||||
},
|
|
||||||
page = page,
|
|
||||||
pageSize = pageSize,
|
|
||||||
total = total,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun createItem(request: CreateDictItemRequest): String = dbQuery {
|
suspend fun createItem(request: CreateDictItemRequest): String = dbQuery {
|
||||||
val typeId = parseUuid(request.typeId, "typeId")
|
val typeId = parseUuid(request.typeId, "typeId")
|
||||||
requireType(typeId)
|
DictDao.requireType(typeId)
|
||||||
val inserted = SysDictItemTable.insert {
|
DictDao.createItem(request, typeId)
|
||||||
it[SysDictItemTable.typeId] = typeId
|
|
||||||
it[label] = request.label.trim()
|
|
||||||
it[value] = request.value.trim()
|
|
||||||
it[color] = request.color?.trim()
|
|
||||||
it[sort] = request.sort
|
|
||||||
it[status] = request.status
|
|
||||||
it[remark] = request.remark?.trim()
|
|
||||||
it[createdAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
inserted[SysDictItemTable.id].toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateItem(id: Uuid, request: UpdateDictItemRequest) = dbQuery {
|
suspend fun updateItem(id: Uuid, request: UpdateDictItemRequest) = dbQuery {
|
||||||
requireItem(id)
|
DictDao.requireItem(id)
|
||||||
val typeId = parseUuid(request.typeId, "typeId")
|
val typeId = parseUuid(request.typeId, "typeId")
|
||||||
requireType(typeId)
|
DictDao.requireType(typeId)
|
||||||
SysDictItemTable.update({ SysDictItemTable.id eq id }) {
|
DictDao.updateItem(id, request, typeId)
|
||||||
it[SysDictItemTable.typeId] = typeId
|
|
||||||
it[label] = request.label.trim()
|
|
||||||
it[value] = request.value.trim()
|
|
||||||
it[color] = request.color?.trim()
|
|
||||||
it[sort] = request.sort
|
|
||||||
it[status] = request.status
|
|
||||||
it[remark] = request.remark?.trim()
|
|
||||||
it[updatedAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteItem(id: Uuid) = dbQuery {
|
suspend fun deleteItem(id: Uuid) = dbQuery {
|
||||||
requireItem(id)
|
DictDao.requireItem(id)
|
||||||
SysDictItemTable.update({ SysDictItemTable.id eq id }) {
|
DictDao.softDeleteItem(id)
|
||||||
it[deletedAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun requireType(id: Uuid): ResultRow =
|
|
||||||
SysDictTypeTable.selectAll().where { (SysDictTypeTable.id eq id) and SysDictTypeTable.deletedAt.isNull() }.singleOrNull()
|
|
||||||
?: throw BizException(
|
|
||||||
ErrorCode.DICT_TYPE_NOT_FOUND.code,
|
|
||||||
ErrorCode.DICT_TYPE_NOT_FOUND.message,
|
|
||||||
HttpStatusCode.NotFound
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun requireItem(id: Uuid): ResultRow =
|
|
||||||
SysDictItemTable.selectAll().where { (SysDictItemTable.id eq id) and SysDictItemTable.deletedAt.isNull() }.singleOrNull()
|
|
||||||
?: throw BizException(
|
|
||||||
ErrorCode.DICT_ITEM_NOT_FOUND.code,
|
|
||||||
ErrorCode.DICT_ITEM_NOT_FOUND.message,
|
|
||||||
HttpStatusCode.NotFound
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,88 +2,17 @@
|
|||||||
|
|
||||||
package com.bbit.ticket.service.system
|
package com.bbit.ticket.service.system
|
||||||
|
|
||||||
import com.bbit.ticket.database.system.SysApiAccessLogTable
|
import com.bbit.ticket.dao.system.LogDao
|
||||||
import com.bbit.ticket.database.system.SysOperationLogTable
|
|
||||||
import com.bbit.ticket.entity.system.ApiAccessLogItem
|
import com.bbit.ticket.entity.system.ApiAccessLogItem
|
||||||
import com.bbit.ticket.entity.system.OperationLogItem
|
import com.bbit.ticket.entity.system.OperationLogItem
|
||||||
import com.bbit.ticket.entity.common.PageResult
|
import com.bbit.ticket.entity.common.PageResult
|
||||||
import com.bbit.ticket.utils.formatDateTime
|
|
||||||
import com.bbit.ticket.plugins.dbQuery
|
import com.bbit.ticket.plugins.dbQuery
|
||||||
import org.jetbrains.exposed.v1.core.Op
|
|
||||||
import org.jetbrains.exposed.v1.core.SortOrder
|
|
||||||
import org.jetbrains.exposed.v1.core.and
|
|
||||||
import org.jetbrains.exposed.v1.core.eq
|
|
||||||
import org.jetbrains.exposed.v1.core.like
|
|
||||||
import org.jetbrains.exposed.v1.core.or
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
|
||||||
import kotlin.uuid.ExperimentalUuidApi
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
|
||||||
object LogsQueryService {
|
object LogsQueryService {
|
||||||
suspend fun operationLogs(page: Int, pageSize: Int, keyword: String?, status: String?): PageResult<OperationLogItem> = dbQuery {
|
suspend fun operationLogs(page: Int, pageSize: Int, keyword: String?, status: String?): PageResult<OperationLogItem> =
|
||||||
var where: Op<Boolean> = Op.TRUE
|
dbQuery { LogDao.operationLogs(page, pageSize, keyword, status) }
|
||||||
if (!keyword.isNullOrBlank()) {
|
|
||||||
where = where and ((SysOperationLogTable.username like "%$keyword%") or (SysOperationLogTable.requestPath like "%$keyword%"))
|
|
||||||
}
|
|
||||||
if (!status.isNullOrBlank()) where = where and (SysOperationLogTable.status eq status)
|
|
||||||
val total = SysOperationLogTable.selectAll().where { where }.count()
|
|
||||||
val rows = SysOperationLogTable.selectAll().where { where }
|
|
||||||
.orderBy(SysOperationLogTable.createdAt, SortOrder.DESC)
|
|
||||||
.limit(pageSize)
|
|
||||||
.offset(((page - 1) * pageSize).toLong())
|
|
||||||
.toList()
|
|
||||||
PageResult(
|
|
||||||
items = rows.map {
|
|
||||||
OperationLogItem(
|
|
||||||
id = it[SysOperationLogTable.id].toString(),
|
|
||||||
traceId = it[SysOperationLogTable.traceId],
|
|
||||||
username = it[SysOperationLogTable.username],
|
|
||||||
operationType = it[SysOperationLogTable.operationType],
|
|
||||||
operationName = it[SysOperationLogTable.operationName],
|
|
||||||
httpMethod = it[SysOperationLogTable.httpMethod],
|
|
||||||
requestPath = it[SysOperationLogTable.requestPath],
|
|
||||||
status = it[SysOperationLogTable.status],
|
|
||||||
errorMessage = it[SysOperationLogTable.errorMessage],
|
|
||||||
costMs = it[SysOperationLogTable.costMs],
|
|
||||||
createdAt = formatDateTime(it[SysOperationLogTable.createdAt]) ?: "",
|
|
||||||
)
|
|
||||||
},
|
|
||||||
page = page,
|
|
||||||
pageSize = pageSize,
|
|
||||||
total = total,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun apiAccessLogs(page: Int, pageSize: Int, keyword: String?, status: String?): PageResult<ApiAccessLogItem> = dbQuery {
|
suspend fun apiAccessLogs(page: Int, pageSize: Int, keyword: String?, status: String?): PageResult<ApiAccessLogItem> =
|
||||||
var where: Op<Boolean> = Op.TRUE
|
dbQuery { LogDao.apiAccessLogs(page, pageSize, keyword, status) }
|
||||||
if (!keyword.isNullOrBlank()) {
|
}
|
||||||
where = where and ((SysApiAccessLogTable.appName like "%$keyword%") or (SysApiAccessLogTable.requestPath like "%$keyword%"))
|
|
||||||
}
|
|
||||||
if (!status.isNullOrBlank()) where = where and (SysApiAccessLogTable.status eq status)
|
|
||||||
val total = SysApiAccessLogTable.selectAll().where { where }.count()
|
|
||||||
val rows = SysApiAccessLogTable.selectAll().where { where }
|
|
||||||
.orderBy(SysApiAccessLogTable.createdAt, SortOrder.DESC)
|
|
||||||
.limit(pageSize)
|
|
||||||
.offset(((page - 1) * pageSize).toLong())
|
|
||||||
.toList()
|
|
||||||
PageResult(
|
|
||||||
items = rows.map {
|
|
||||||
ApiAccessLogItem(
|
|
||||||
id = it[SysApiAccessLogTable.id].toString(),
|
|
||||||
traceId = it[SysApiAccessLogTable.traceId],
|
|
||||||
appKey = it[SysApiAccessLogTable.appKey],
|
|
||||||
appName = it[SysApiAccessLogTable.appName],
|
|
||||||
httpMethod = it[SysApiAccessLogTable.httpMethod],
|
|
||||||
requestPath = it[SysApiAccessLogTable.requestPath],
|
|
||||||
responseCode = it[SysApiAccessLogTable.responseCode],
|
|
||||||
status = it[SysApiAccessLogTable.status],
|
|
||||||
errorMessage = it[SysApiAccessLogTable.errorMessage],
|
|
||||||
costMs = it[SysApiAccessLogTable.costMs],
|
|
||||||
createdAt = formatDateTime(it[SysApiAccessLogTable.createdAt]) ?: "",
|
|
||||||
)
|
|
||||||
},
|
|
||||||
page = page,
|
|
||||||
pageSize = pageSize,
|
|
||||||
total = total,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,160 +2,49 @@
|
|||||||
|
|
||||||
package com.bbit.ticket.service.system
|
package com.bbit.ticket.service.system
|
||||||
|
|
||||||
import com.bbit.ticket.database.system.SysMenuTable
|
import com.bbit.ticket.dao.system.MenuDao
|
||||||
import com.bbit.ticket.database.system.SysMenuTable.builtIn
|
|
||||||
import com.bbit.ticket.database.system.SysMenuTable.component
|
|
||||||
import com.bbit.ticket.database.system.SysMenuTable.icon
|
|
||||||
import com.bbit.ticket.database.system.SysMenuTable.keepAlive
|
|
||||||
import com.bbit.ticket.database.system.SysMenuTable.path
|
|
||||||
import com.bbit.ticket.database.system.SysMenuTable.permission
|
|
||||||
import com.bbit.ticket.database.system.SysMenuTable.visible
|
|
||||||
import com.bbit.ticket.database.system.SysRoleMenuTable
|
|
||||||
import com.bbit.ticket.entity.system.CreateMenuRequest
|
import com.bbit.ticket.entity.system.CreateMenuRequest
|
||||||
import com.bbit.ticket.entity.system.MenuFlat
|
|
||||||
import com.bbit.ticket.entity.system.MenuTreeNode
|
import com.bbit.ticket.entity.system.MenuTreeNode
|
||||||
import com.bbit.ticket.entity.system.UpdateMenuRequest
|
import com.bbit.ticket.entity.system.UpdateMenuRequest
|
||||||
import com.bbit.ticket.entity.common.BizException
|
import com.bbit.ticket.entity.common.BizException
|
||||||
import com.bbit.ticket.entity.common.ErrorCode
|
import com.bbit.ticket.entity.common.ErrorCode
|
||||||
import com.bbit.ticket.entity.common.menuTypeLabel
|
|
||||||
import com.bbit.ticket.entity.common.statusLabel
|
|
||||||
import com.bbit.ticket.plugins.dbQuery
|
import com.bbit.ticket.plugins.dbQuery
|
||||||
import com.bbit.ticket.utils.parseUuid
|
import com.bbit.ticket.utils.parseUuid
|
||||||
import io.ktor.http.HttpStatusCode
|
|
||||||
import org.jetbrains.exposed.v1.core.ResultRow
|
|
||||||
import org.jetbrains.exposed.v1.core.and
|
|
||||||
import org.jetbrains.exposed.v1.core.eq
|
|
||||||
import org.jetbrains.exposed.v1.core.isNull
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.insert
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.update
|
|
||||||
import java.time.OffsetDateTime
|
|
||||||
import kotlin.uuid.ExperimentalUuidApi
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
import kotlin.uuid.Uuid
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
object MenuService {
|
object MenuService {
|
||||||
suspend fun tree(): List<MenuTreeNode> = dbQuery {
|
suspend fun tree(): List<MenuTreeNode> = dbQuery { MenuDao.tree() }
|
||||||
val rows = SysMenuTable.selectAll().where { SysMenuTable.deletedAt.isNull() }.toList()
|
|
||||||
val flat = rows.map {
|
|
||||||
MenuFlat(
|
|
||||||
id = it[SysMenuTable.id],
|
|
||||||
parentId = it[SysMenuTable.parentId],
|
|
||||||
type = it[SysMenuTable.type],
|
|
||||||
title = it[SysMenuTable.title],
|
|
||||||
name = it[SysMenuTable.name],
|
|
||||||
path = it[path],
|
|
||||||
component = it[component],
|
|
||||||
icon = it[icon],
|
|
||||||
permission = it[permission],
|
|
||||||
sort = it[SysMenuTable.sort],
|
|
||||||
visible = it[visible],
|
|
||||||
keepAlive = it[keepAlive],
|
|
||||||
builtIn = it[builtIn],
|
|
||||||
status = it[SysMenuTable.status],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
buildTree(flat)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun create(request: CreateMenuRequest): String = dbQuery {
|
suspend fun create(request: CreateMenuRequest): String = dbQuery {
|
||||||
validateMenuType(request.type)
|
validateMenuType(request.type)
|
||||||
val parentId = request.parentId?.let { parseUuid(it, "parentId") }
|
val parentId = request.parentId?.let { parseUuid(it, "parentId") }
|
||||||
if (parentId != null) requireMenu(parentId)
|
if (parentId != null) MenuDao.requireActive(parentId)
|
||||||
val inserted = SysMenuTable.insert {
|
MenuDao.create(request, parentId)
|
||||||
it[SysMenuTable.parentId] = parentId
|
|
||||||
it[SysMenuTable.type] = request.type
|
|
||||||
it[title] = request.title.trim()
|
|
||||||
it[name] = request.name?.trim()
|
|
||||||
it[path] = request.path?.trim()
|
|
||||||
it[component] = request.component?.trim()
|
|
||||||
it[icon] = request.icon?.trim()
|
|
||||||
it[permission] = request.permission?.trim()
|
|
||||||
it[sort] = request.sort
|
|
||||||
it[visible] = request.visible
|
|
||||||
it[keepAlive] = request.keepAlive
|
|
||||||
it[builtIn] = false
|
|
||||||
it[status] = request.status
|
|
||||||
it[createdAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
inserted[SysMenuTable.id].toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun update(id: Uuid, request: UpdateMenuRequest) = dbQuery {
|
suspend fun update(id: Uuid, request: UpdateMenuRequest) = dbQuery {
|
||||||
requireMenu(id)
|
MenuDao.requireActive(id)
|
||||||
validateMenuType(request.type)
|
validateMenuType(request.type)
|
||||||
val parentId = request.parentId?.let { parseUuid(it, "parentId") }
|
val parentId = request.parentId?.let { parseUuid(it, "parentId") }
|
||||||
if (parentId == id) throw BizException(ErrorCode.BAD_REQUEST.code, "上级菜单不能选择自身")
|
if (parentId == id) throw BizException(ErrorCode.BAD_REQUEST.code, "上级菜单不能选择自身")
|
||||||
if (parentId != null) requireMenu(parentId)
|
if (parentId != null) MenuDao.requireActive(parentId)
|
||||||
SysMenuTable.update({ SysMenuTable.id eq id }) {
|
MenuDao.update(id, request, parentId)
|
||||||
it[SysMenuTable.parentId] = parentId
|
|
||||||
it[SysMenuTable.type] = request.type
|
|
||||||
it[title] = request.title.trim()
|
|
||||||
it[name] = request.name?.trim()
|
|
||||||
it[path] = request.path?.trim()
|
|
||||||
it[component] = request.component?.trim()
|
|
||||||
it[icon] = request.icon?.trim()
|
|
||||||
it[permission] = request.permission?.trim()
|
|
||||||
it[sort] = request.sort
|
|
||||||
it[visible] = request.visible
|
|
||||||
it[keepAlive] = request.keepAlive
|
|
||||||
it[status] = request.status
|
|
||||||
it[updatedAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun delete(id: Uuid) = dbQuery {
|
suspend fun delete(id: Uuid) = dbQuery {
|
||||||
requireMenu(id)
|
val row = MenuDao.requireActive(id)
|
||||||
val hasChildren = SysMenuTable.selectAll().where {
|
if (MenuDao.hasChildren(id)) throw BizException(ErrorCode.BAD_REQUEST.code, "存在子菜单,不能删除")
|
||||||
(SysMenuTable.parentId eq id) and SysMenuTable.deletedAt.isNull()
|
if (MenuDao.isReferenced(id)) throw BizException(ErrorCode.BAD_REQUEST.code, "菜单已被角色引用,不能删除")
|
||||||
}.any()
|
if (row[com.bbit.ticket.database.system.SysMenuTable.builtIn]) {
|
||||||
if (hasChildren) throw BizException(ErrorCode.BAD_REQUEST.code, "存在子菜单,不能删除")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "基础框架内置菜单不可删除")
|
||||||
val referenced = SysRoleMenuTable.selectAll().where { SysRoleMenuTable.menuId eq id }.any()
|
|
||||||
if (referenced) throw BizException(ErrorCode.BAD_REQUEST.code, "菜单已被角色引用,不能删除")
|
|
||||||
val row = requireMenu(id)
|
|
||||||
if (row[builtIn]) throw BizException(ErrorCode.BAD_REQUEST.code, "基础框架内置菜单不可删除")
|
|
||||||
SysMenuTable.update({ SysMenuTable.id eq id }) {
|
|
||||||
it[deletedAt] = OffsetDateTime.now()
|
|
||||||
}
|
}
|
||||||
|
MenuDao.softDelete(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun requireMenu(id: Uuid): ResultRow =
|
|
||||||
SysMenuTable.selectAll().where { (SysMenuTable.id eq id) and SysMenuTable.deletedAt.isNull() }.singleOrNull()
|
|
||||||
?: throw BizException(
|
|
||||||
ErrorCode.MENU_NOT_FOUND.code,
|
|
||||||
ErrorCode.MENU_NOT_FOUND.message,
|
|
||||||
HttpStatusCode.NotFound
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun validateMenuType(type: String) {
|
private fun validateMenuType(type: String) {
|
||||||
if (type !in setOf("CATALOG", "MENU", "BUTTON")) {
|
if (type !in setOf("CATALOG", "MENU", "BUTTON")) {
|
||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "菜单类型必须是目录、菜单或按钮")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "菜单类型必须是目录、菜单或按钮")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildTree(items: List<MenuFlat>): List<MenuTreeNode> {
|
|
||||||
val grouped = items.groupBy { it.parentId }
|
|
||||||
fun children(parentId: Uuid?): List<MenuTreeNode> =
|
|
||||||
(grouped[parentId] ?: emptyList()).sortedBy { it.sort }.map { menu ->
|
|
||||||
MenuTreeNode(
|
|
||||||
id = menu.id.toString(),
|
|
||||||
parentId = menu.parentId?.toString(),
|
|
||||||
type = menu.type,
|
|
||||||
typeLabel = menuTypeLabel(menu.type),
|
|
||||||
title = menu.title,
|
|
||||||
name = menu.name,
|
|
||||||
path = menu.path,
|
|
||||||
component = menu.component,
|
|
||||||
icon = menu.icon,
|
|
||||||
permission = menu.permission,
|
|
||||||
sort = menu.sort,
|
|
||||||
visible = menu.visible,
|
|
||||||
keepAlive = menu.keepAlive,
|
|
||||||
builtIn = menu.builtIn,
|
|
||||||
status = menu.status,
|
|
||||||
statusLabel = statusLabel(menu.status),
|
|
||||||
children = children(menu.id),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return children(null)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,10 @@
|
|||||||
|
|
||||||
package com.bbit.ticket.service.system
|
package com.bbit.ticket.service.system
|
||||||
|
|
||||||
import com.bbit.ticket.database.system.SysOperationLogTable
|
import com.bbit.ticket.dao.system.LogDao
|
||||||
import com.bbit.ticket.utils.traceIdOrNull
|
|
||||||
import com.bbit.ticket.plugins.dbQuery
|
import com.bbit.ticket.plugins.dbQuery
|
||||||
import com.bbit.ticket.utils.CurrentUser
|
import com.bbit.ticket.utils.CurrentUser
|
||||||
import io.ktor.http.formUrlEncode
|
|
||||||
import io.ktor.server.application.ApplicationCall
|
import io.ktor.server.application.ApplicationCall
|
||||||
import io.ktor.server.request.httpMethod
|
|
||||||
import io.ktor.server.request.path
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.insert
|
|
||||||
import java.time.OffsetDateTime
|
|
||||||
import kotlin.uuid.ExperimentalUuidApi
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
|
|
||||||
object OperationLogService {
|
object OperationLogService {
|
||||||
@@ -39,22 +33,6 @@ object OperationLogService {
|
|||||||
errorMessage: String?,
|
errorMessage: String?,
|
||||||
costMs: Long,
|
costMs: Long,
|
||||||
) = dbQuery {
|
) = dbQuery {
|
||||||
SysOperationLogTable.insert {
|
LogDao.saveOperationLog(call, currentUser, operationType, operationName, status, errorMessage, costMs)
|
||||||
it[traceId] = call.traceIdOrNull()
|
|
||||||
it[userId] = currentUser?.id
|
|
||||||
it[username] = currentUser?.username
|
|
||||||
it[orgId] = currentUser?.orgId
|
|
||||||
it[SysOperationLogTable.operationType] = operationType
|
|
||||||
it[SysOperationLogTable.operationName] = operationName
|
|
||||||
it[httpMethod] = call.request.httpMethod.value
|
|
||||||
it[requestPath] = call.request.path().take(255)
|
|
||||||
it[requestParams] = call.request.queryParameters.formUrlEncode().take(1000)
|
|
||||||
it[ip] = call.request.local.remoteHost.take(64)
|
|
||||||
it[userAgent] = call.request.headers["User-Agent"]?.take(255)
|
|
||||||
it[SysOperationLogTable.status] = status
|
|
||||||
it[SysOperationLogTable.errorMessage] = errorMessage
|
|
||||||
it[SysOperationLogTable.costMs] = costMs
|
|
||||||
it[createdAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,140 +2,54 @@
|
|||||||
|
|
||||||
package com.bbit.ticket.service.system
|
package com.bbit.ticket.service.system
|
||||||
|
|
||||||
import com.bbit.ticket.database.system.SysOrgTable
|
import com.bbit.ticket.dao.system.OrgDao
|
||||||
import com.bbit.ticket.database.system.SysUserTable
|
|
||||||
import com.bbit.ticket.entity.system.CreateOrgRequest
|
import com.bbit.ticket.entity.system.CreateOrgRequest
|
||||||
import com.bbit.ticket.entity.system.OrgTreeNode
|
import com.bbit.ticket.entity.system.OrgTreeNode
|
||||||
import com.bbit.ticket.entity.system.UpdateOrgRequest
|
import com.bbit.ticket.entity.system.UpdateOrgRequest
|
||||||
import com.bbit.ticket.entity.common.BizException
|
import com.bbit.ticket.entity.common.BizException
|
||||||
import com.bbit.ticket.entity.common.ErrorCode
|
import com.bbit.ticket.entity.common.ErrorCode
|
||||||
import com.bbit.ticket.entity.common.statusLabel
|
|
||||||
import com.bbit.ticket.plugins.dbQuery
|
import com.bbit.ticket.plugins.dbQuery
|
||||||
import com.bbit.ticket.utils.parseUuid
|
import com.bbit.ticket.utils.parseUuid
|
||||||
import io.ktor.http.HttpStatusCode
|
|
||||||
import org.jetbrains.exposed.v1.core.ResultRow
|
|
||||||
import org.jetbrains.exposed.v1.core.and
|
|
||||||
import org.jetbrains.exposed.v1.core.eq
|
|
||||||
import org.jetbrains.exposed.v1.core.isNull
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.insert
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.update
|
|
||||||
import java.time.OffsetDateTime
|
|
||||||
import kotlin.uuid.ExperimentalUuidApi
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
import kotlin.uuid.Uuid
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
object OrgService {
|
object OrgService {
|
||||||
suspend fun tree(): List<OrgTreeNode> = dbQuery {
|
suspend fun tree(): List<OrgTreeNode> = dbQuery { OrgDao.tree() }
|
||||||
val rows = SysOrgTable.selectAll()
|
|
||||||
.where { SysOrgTable.deletedAt.isNull() }
|
|
||||||
.orderBy(SysOrgTable.sort)
|
|
||||||
.toList()
|
|
||||||
val nodes = rows.map(::toNode)
|
|
||||||
buildTree(nodes)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun create(request: CreateOrgRequest): String = dbQuery {
|
suspend fun create(request: CreateOrgRequest): String = dbQuery {
|
||||||
val code = request.code.trim()
|
val code = request.code.trim()
|
||||||
if (code.isBlank() || request.name.trim().isBlank()) {
|
if (code.isBlank() || request.name.trim().isBlank()) {
|
||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "组织名称和编码不能为空")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "组织名称和编码不能为空")
|
||||||
}
|
}
|
||||||
val exists = SysOrgTable.selectAll().where {
|
if (OrgDao.codeExists(code)) {
|
||||||
(SysOrgTable.code eq code) and SysOrgTable.deletedAt.isNull()
|
|
||||||
}.any()
|
|
||||||
if (exists) {
|
|
||||||
throw BizException(ErrorCode.DATA_CONFLICT.code, "组织编码已存在")
|
throw BizException(ErrorCode.DATA_CONFLICT.code, "组织编码已存在")
|
||||||
}
|
}
|
||||||
val parentId = request.parentId?.let { parseUuid(it, "parentId") }
|
val parentId = request.parentId?.let { parseUuid(it, "parentId") }
|
||||||
if (parentId != null) requireOrg(parentId)
|
if (parentId != null) OrgDao.requireActive(parentId)
|
||||||
val inserted = SysOrgTable.insert {
|
OrgDao.create(request, parentId)
|
||||||
it[SysOrgTable.parentId] = parentId
|
|
||||||
it[name] = request.name.trim()
|
|
||||||
it[SysOrgTable.code] = code
|
|
||||||
it[sort] = request.sort
|
|
||||||
it[status] = request.status
|
|
||||||
it[createdAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
inserted[SysOrgTable.id].toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun update(id: Uuid, request: UpdateOrgRequest) = dbQuery {
|
suspend fun update(id: Uuid, request: UpdateOrgRequest) = dbQuery {
|
||||||
requireOrg(id)
|
OrgDao.requireActive(id)
|
||||||
val parentId = request.parentId?.let { parseUuid(it, "parentId") }
|
val parentId = request.parentId?.let { parseUuid(it, "parentId") }
|
||||||
if (parentId == id) {
|
if (parentId == id) {
|
||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "上级组织不能选择自身")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "上级组织不能选择自身")
|
||||||
}
|
}
|
||||||
if (parentId != null) requireOrg(parentId)
|
if (parentId != null) OrgDao.requireActive(parentId)
|
||||||
SysOrgTable.update({ SysOrgTable.id eq id }) {
|
OrgDao.update(id, request, parentId)
|
||||||
it[SysOrgTable.parentId] = parentId
|
|
||||||
it[name] = request.name.trim()
|
|
||||||
it[sort] = request.sort
|
|
||||||
it[status] = request.status
|
|
||||||
it[updatedAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun delete(id: Uuid) = dbQuery {
|
suspend fun delete(id: Uuid) = dbQuery {
|
||||||
val org = requireOrg(id)
|
val org = OrgDao.requireActive(id)
|
||||||
if (org[SysOrgTable.code] == "DEFAULT_ORG") {
|
if (org[com.bbit.ticket.database.system.SysOrgTable.code] == "DEFAULT_ORG") {
|
||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "默认组织不可删除")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "默认组织不可删除")
|
||||||
}
|
}
|
||||||
val hasChildren = SysOrgTable.selectAll()
|
if (OrgDao.hasChildren(id)) {
|
||||||
.where { (SysOrgTable.parentId eq id) and SysOrgTable.deletedAt.isNull() }
|
|
||||||
.any()
|
|
||||||
if (hasChildren) {
|
|
||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "当前组织存在子组织,不能删除")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "当前组织存在子组织,不能删除")
|
||||||
}
|
}
|
||||||
val hasUsers = SysUserTable.selectAll()
|
if (OrgDao.hasUsers(id)) {
|
||||||
.where { (SysUserTable.orgId eq id) and SysUserTable.deletedAt.isNull() }
|
|
||||||
.any()
|
|
||||||
if (hasUsers) {
|
|
||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "当前组织存在用户,不能删除")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "当前组织存在用户,不能删除")
|
||||||
}
|
}
|
||||||
SysOrgTable.update({ SysOrgTable.id eq id }) {
|
OrgDao.softDelete(id)
|
||||||
it[deletedAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun requireOrg(id: Uuid): ResultRow =
|
|
||||||
SysOrgTable.selectAll().where { (SysOrgTable.id eq id) and SysOrgTable.deletedAt.isNull() }.singleOrNull()
|
|
||||||
?: throw BizException(
|
|
||||||
ErrorCode.ORG_NOT_FOUND.code,
|
|
||||||
ErrorCode.ORG_NOT_FOUND.message,
|
|
||||||
HttpStatusCode.NotFound
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun toNode(row: ResultRow): OrgNodeFlat = OrgNodeFlat(
|
|
||||||
id = row[SysOrgTable.id],
|
|
||||||
parentId = row[SysOrgTable.parentId],
|
|
||||||
name = row[SysOrgTable.name],
|
|
||||||
code = row[SysOrgTable.code],
|
|
||||||
sort = row[SysOrgTable.sort],
|
|
||||||
status = row[SysOrgTable.status],
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun buildTree(nodes: List<OrgNodeFlat>): List<OrgTreeNode> {
|
|
||||||
val byParent = nodes.groupBy { it.parentId }
|
|
||||||
fun children(parentId: Uuid?): List<OrgTreeNode> =
|
|
||||||
(byParent[parentId] ?: emptyList()).sortedBy { it.sort }.map { item ->
|
|
||||||
OrgTreeNode(
|
|
||||||
id = item.id.toString(),
|
|
||||||
parentId = item.parentId?.toString(),
|
|
||||||
name = item.name,
|
|
||||||
code = item.code,
|
|
||||||
sort = item.sort,
|
|
||||||
status = item.status,
|
|
||||||
statusLabel = statusLabel(item.status),
|
|
||||||
children = children(item.id),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return children(null)
|
|
||||||
}
|
|
||||||
private data class OrgNodeFlat(
|
|
||||||
val id: Uuid,
|
|
||||||
val parentId: Uuid?,
|
|
||||||
val name: String,
|
|
||||||
val code: String,
|
|
||||||
val sort: Int,
|
|
||||||
val status: String,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,164 +2,61 @@
|
|||||||
|
|
||||||
package com.bbit.ticket.service.system
|
package com.bbit.ticket.service.system
|
||||||
|
|
||||||
import com.bbit.ticket.database.system.SysMenuTable
|
import com.bbit.ticket.dao.system.RoleDao
|
||||||
import com.bbit.ticket.database.system.SysRoleMenuTable
|
|
||||||
import com.bbit.ticket.database.system.SysRoleTable
|
|
||||||
import com.bbit.ticket.database.system.SysRoleTable.dataScope
|
|
||||||
import com.bbit.ticket.database.system.SysUserRoleTable
|
|
||||||
import com.bbit.ticket.entity.system.CreateRoleRequest
|
import com.bbit.ticket.entity.system.CreateRoleRequest
|
||||||
import com.bbit.ticket.entity.system.RoleDetail
|
import com.bbit.ticket.entity.system.RoleDetail
|
||||||
import com.bbit.ticket.entity.system.RoleItem
|
import com.bbit.ticket.entity.system.RoleItem
|
||||||
|
import com.bbit.ticket.entity.system.UpdateRoleMenusRequest
|
||||||
|
import com.bbit.ticket.entity.system.UpdateRoleRequest
|
||||||
import com.bbit.ticket.entity.common.BizException
|
import com.bbit.ticket.entity.common.BizException
|
||||||
import com.bbit.ticket.entity.common.ErrorCode
|
import com.bbit.ticket.entity.common.ErrorCode
|
||||||
import com.bbit.ticket.entity.common.PageResult
|
import com.bbit.ticket.entity.common.PageResult
|
||||||
import com.bbit.ticket.entity.common.dataScopeLabel
|
|
||||||
import com.bbit.ticket.entity.common.statusLabel
|
|
||||||
import com.bbit.ticket.entity.system.UpdateRoleMenusRequest
|
|
||||||
import com.bbit.ticket.entity.system.UpdateRoleRequest
|
|
||||||
import com.bbit.ticket.plugins.dbQuery
|
import com.bbit.ticket.plugins.dbQuery
|
||||||
import com.bbit.ticket.utils.parseUuid
|
import com.bbit.ticket.utils.parseUuid
|
||||||
import io.ktor.http.HttpStatusCode
|
|
||||||
import org.jetbrains.exposed.v1.core.ResultRow
|
|
||||||
import org.jetbrains.exposed.v1.core.and
|
|
||||||
import org.jetbrains.exposed.v1.core.eq
|
|
||||||
import org.jetbrains.exposed.v1.core.inList
|
|
||||||
import org.jetbrains.exposed.v1.core.isNull
|
|
||||||
import org.jetbrains.exposed.v1.core.like
|
|
||||||
import org.jetbrains.exposed.v1.core.or
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.deleteWhere
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.insert
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.update
|
|
||||||
import java.time.OffsetDateTime
|
|
||||||
import kotlin.uuid.ExperimentalUuidApi
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
import kotlin.uuid.Uuid
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
object RoleService {
|
object RoleService {
|
||||||
@OptIn(ExperimentalUuidApi::class)
|
@OptIn(ExperimentalUuidApi::class)
|
||||||
suspend fun list(page: Int, pageSize: Int, keyword: String?, status: String?): PageResult<RoleItem> = dbQuery {
|
suspend fun list(page: Int, pageSize: Int, keyword: String?, status: String?): PageResult<RoleItem> =
|
||||||
var where = SysRoleTable.deletedAt.isNull()
|
dbQuery { RoleDao.list(page, pageSize, keyword, status) }
|
||||||
if (!keyword.isNullOrBlank()) {
|
|
||||||
where = where and ((SysRoleTable.name like "%$keyword%") or (SysRoleTable.code like "%$keyword%"))
|
|
||||||
}
|
|
||||||
if (!status.isNullOrBlank()) {
|
|
||||||
where = where and (SysRoleTable.status eq status)
|
|
||||||
}
|
|
||||||
val total = SysRoleTable.selectAll().where { where }.count()
|
|
||||||
val rows = SysRoleTable.selectAll().where { where }
|
|
||||||
.orderBy(SysRoleTable.createdAt)
|
|
||||||
.limit(pageSize)
|
|
||||||
.offset(((page - 1) * pageSize).toLong())
|
|
||||||
.toList()
|
|
||||||
PageResult(
|
|
||||||
items = rows.map {
|
|
||||||
RoleItem(
|
|
||||||
id = it[SysRoleTable.id].toString(),
|
|
||||||
name = it[SysRoleTable.name],
|
|
||||||
code = it[SysRoleTable.code],
|
|
||||||
description = it[SysRoleTable.description],
|
|
||||||
status = it[SysRoleTable.status],
|
|
||||||
statusLabel = statusLabel(it[SysRoleTable.status]),
|
|
||||||
dataScope = it[dataScope],
|
|
||||||
dataScopeLabel = dataScopeLabel(it[dataScope]),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
page = page,
|
|
||||||
pageSize = pageSize,
|
|
||||||
total = total,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun create(request: CreateRoleRequest): String = dbQuery {
|
suspend fun create(request: CreateRoleRequest): String = dbQuery {
|
||||||
if (request.name.trim().isBlank() || request.code.trim().isBlank()) {
|
if (request.name.trim().isBlank() || request.code.trim().isBlank()) {
|
||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "角色名称和编码不能为空")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "角色名称和编码不能为空")
|
||||||
}
|
}
|
||||||
val exists = SysRoleTable.selectAll().where {
|
if (RoleDao.codeExists(request.code.trim())) {
|
||||||
(SysRoleTable.code eq request.code.trim()) and SysRoleTable.deletedAt.isNull()
|
throw BizException(ErrorCode.DATA_CONFLICT.code, "角色编码已存在")
|
||||||
}.any()
|
|
||||||
if (exists) throw BizException(ErrorCode.DATA_CONFLICT.code, "角色编码已存在")
|
|
||||||
val inserted = SysRoleTable.insert {
|
|
||||||
it[name] = request.name.trim()
|
|
||||||
it[code] = request.code.trim()
|
|
||||||
it[description] = request.description?.trim()
|
|
||||||
it[status] = request.status
|
|
||||||
it[dataScope] = request.dataScope
|
|
||||||
it[createdAt] = OffsetDateTime.now()
|
|
||||||
}
|
}
|
||||||
inserted[SysRoleTable.id].toString()
|
RoleDao.create(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun detail(id: Uuid): RoleDetail = dbQuery {
|
suspend fun detail(id: Uuid): RoleDetail = dbQuery { RoleDao.detail(id) }
|
||||||
val role = requireRole(id)
|
|
||||||
val menuIds = SysRoleMenuTable.selectAll().where { SysRoleMenuTable.roleId eq id }
|
|
||||||
.map { it[SysRoleMenuTable.menuId].toString() }
|
|
||||||
RoleDetail(
|
|
||||||
id = role[SysRoleTable.id].toString(),
|
|
||||||
name = role[SysRoleTable.name],
|
|
||||||
code = role[SysRoleTable.code],
|
|
||||||
description = role[SysRoleTable.description],
|
|
||||||
status = role[SysRoleTable.status],
|
|
||||||
statusLabel = statusLabel(role[SysRoleTable.status]),
|
|
||||||
dataScope = role[dataScope],
|
|
||||||
dataScopeLabel = dataScopeLabel(role[dataScope]),
|
|
||||||
menuIds = menuIds,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun update(id: Uuid, request: UpdateRoleRequest) = dbQuery {
|
suspend fun update(id: Uuid, request: UpdateRoleRequest) = dbQuery {
|
||||||
requireRole(id)
|
RoleDao.requireActive(id)
|
||||||
SysRoleTable.update({ SysRoleTable.id eq id }) {
|
RoleDao.update(id, request)
|
||||||
it[name] = request.name.trim()
|
|
||||||
it[description] = request.description?.trim()
|
|
||||||
it[status] = request.status
|
|
||||||
it[dataScope] = request.dataScope
|
|
||||||
it[updatedAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun delete(id: Uuid) = dbQuery {
|
suspend fun delete(id: Uuid) = dbQuery {
|
||||||
val role = requireRole(id)
|
val role = RoleDao.requireActive(id)
|
||||||
if (role[SysRoleTable.code] == "SUPER_ADMIN") {
|
if (role[com.bbit.ticket.database.system.SysRoleTable.code] == "SUPER_ADMIN") {
|
||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "超级管理员角色不可删除")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "超级管理员角色不可删除")
|
||||||
}
|
}
|
||||||
val inUse = SysUserRoleTable.selectAll().where { SysUserRoleTable.roleId eq id }.any()
|
if (RoleDao.inUse(id)) {
|
||||||
if (inUse) {
|
|
||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "角色已被用户使用,不能删除")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "角色已被用户使用,不能删除")
|
||||||
}
|
}
|
||||||
SysRoleTable.update({ SysRoleTable.id eq id }) {
|
RoleDao.softDelete(id)
|
||||||
it[deletedAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
SysRoleMenuTable.deleteWhere { SysRoleMenuTable.roleId eq id }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateMenus(id: Uuid, request: UpdateRoleMenusRequest) = dbQuery {
|
suspend fun updateMenus(id: Uuid, request: UpdateRoleMenusRequest) = dbQuery {
|
||||||
requireRole(id)
|
RoleDao.requireActive(id)
|
||||||
val menuIds = request.menuIds.distinct().map { parseUuid(it, "menuId") }
|
val menuIds = request.menuIds.distinct().map { parseUuid(it, "menuId") }
|
||||||
if (menuIds.isNotEmpty()) {
|
if (menuIds.isNotEmpty()) {
|
||||||
val validCount = SysMenuTable.selectAll().where {
|
if (RoleDao.countEnabledMenus(menuIds) != menuIds.size.toLong()) {
|
||||||
(SysMenuTable.id inList menuIds) and
|
|
||||||
SysMenuTable.deletedAt.isNull() and
|
|
||||||
(SysMenuTable.status eq "ENABLED")
|
|
||||||
}.count()
|
|
||||||
if (validCount != menuIds.size.toLong()) {
|
|
||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "包含不存在或禁用菜单")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "包含不存在或禁用菜单")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SysRoleMenuTable.deleteWhere { SysRoleMenuTable.roleId eq id }
|
RoleDao.replaceMenus(id, menuIds)
|
||||||
menuIds.forEach { menuId ->
|
|
||||||
SysRoleMenuTable.insertIgnore {
|
|
||||||
it[roleId] = id
|
|
||||||
it[SysRoleMenuTable.menuId] = menuId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun requireRole(id: Uuid): ResultRow =
|
|
||||||
SysRoleTable.selectAll().where { (SysRoleTable.id eq id) and SysRoleTable.deletedAt.isNull() }.singleOrNull()
|
|
||||||
?: throw BizException(
|
|
||||||
ErrorCode.ROLE_NOT_FOUND.code,
|
|
||||||
ErrorCode.ROLE_NOT_FOUND.message,
|
|
||||||
HttpStatusCode.NotFound
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
package com.bbit.ticket.service.system
|
package com.bbit.ticket.service.system
|
||||||
|
|
||||||
import com.bbit.ticket.database.system.SysOrgTable
|
import com.bbit.ticket.dao.system.UserDao
|
||||||
import com.bbit.ticket.database.system.SysRoleTable
|
|
||||||
import com.bbit.ticket.database.system.SysUserRoleTable
|
|
||||||
import com.bbit.ticket.database.system.SysUserTable
|
|
||||||
import com.bbit.ticket.database.system.SysUserTable.avatar
|
|
||||||
import com.bbit.ticket.database.system.SysUserTable.email
|
|
||||||
import com.bbit.ticket.database.system.SysUserTable.nickname
|
|
||||||
import com.bbit.ticket.database.system.SysUserTable.phone
|
|
||||||
import com.bbit.ticket.database.system.SysUserTable.realName
|
|
||||||
import com.bbit.ticket.entity.system.CreateUserRequest
|
import com.bbit.ticket.entity.system.CreateUserRequest
|
||||||
import com.bbit.ticket.entity.system.UpdateUserPasswordRequest
|
import com.bbit.ticket.entity.system.UpdateUserPasswordRequest
|
||||||
import com.bbit.ticket.entity.system.UpdateUserRequest
|
import com.bbit.ticket.entity.system.UpdateUserRequest
|
||||||
@@ -19,22 +11,8 @@ import com.bbit.ticket.entity.system.UserListItem
|
|||||||
import com.bbit.ticket.entity.common.BizException
|
import com.bbit.ticket.entity.common.BizException
|
||||||
import com.bbit.ticket.entity.common.ErrorCode
|
import com.bbit.ticket.entity.common.ErrorCode
|
||||||
import com.bbit.ticket.entity.common.PageResult
|
import com.bbit.ticket.entity.common.PageResult
|
||||||
import com.bbit.ticket.entity.common.statusLabel
|
|
||||||
import com.bbit.ticket.plugins.dbQuery
|
import com.bbit.ticket.plugins.dbQuery
|
||||||
import com.bbit.ticket.utils.parseUuid
|
import com.bbit.ticket.utils.parseUuid
|
||||||
import io.ktor.http.HttpStatusCode
|
|
||||||
import org.jetbrains.exposed.v1.core.Op
|
|
||||||
import org.jetbrains.exposed.v1.core.and
|
|
||||||
import org.jetbrains.exposed.v1.core.eq
|
|
||||||
import org.jetbrains.exposed.v1.core.inList
|
|
||||||
import org.jetbrains.exposed.v1.core.isNull
|
|
||||||
import org.jetbrains.exposed.v1.core.like
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.deleteWhere
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.insert
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.selectAll
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.update
|
|
||||||
import java.time.OffsetDateTime
|
|
||||||
import kotlin.uuid.ExperimentalUuidApi
|
import kotlin.uuid.ExperimentalUuidApi
|
||||||
import kotlin.uuid.Uuid
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
@@ -47,57 +25,14 @@ object UserService {
|
|||||||
nickname: String?,
|
nickname: String?,
|
||||||
status: String?,
|
status: String?,
|
||||||
orgId: Uuid?,
|
orgId: Uuid?,
|
||||||
): PageResult<UserListItem> = dbQuery {
|
): PageResult<UserListItem> = dbQuery { UserDao.list(page, pageSize, username, nickname, status, orgId) }
|
||||||
val where = buildWhere(username, nickname, status, orgId)
|
|
||||||
val total = SysUserTable.selectAll().where { where }.count()
|
|
||||||
val rows = SysUserTable.selectAll()
|
|
||||||
.where { where }
|
|
||||||
.orderBy(SysUserTable.createdAt)
|
|
||||||
.limit(pageSize)
|
|
||||||
.offset(((page - 1) * pageSize).toLong())
|
|
||||||
.toList()
|
|
||||||
|
|
||||||
val userIds = rows.map { it[SysUserTable.id] }
|
|
||||||
val roleMap = if (userIds.isEmpty()) {
|
|
||||||
emptyMap()
|
|
||||||
} else {
|
|
||||||
(SysUserRoleTable innerJoin SysRoleTable).selectAll()
|
|
||||||
.where {
|
|
||||||
(SysUserRoleTable.userId inList userIds) and
|
|
||||||
SysRoleTable.deletedAt.isNull()
|
|
||||||
}
|
|
||||||
.groupBy { it[SysUserRoleTable.userId] }
|
|
||||||
.mapValues { entry -> entry.value.map { row -> row[SysRoleTable.code] }.distinct() }
|
|
||||||
}
|
|
||||||
|
|
||||||
PageResult(
|
|
||||||
items = rows.map { row ->
|
|
||||||
UserListItem(
|
|
||||||
id = row[SysUserTable.id].toString(),
|
|
||||||
username = row[SysUserTable.username],
|
|
||||||
nickname = row[SysUserTable.nickname],
|
|
||||||
realName = row[realName],
|
|
||||||
orgId = row[SysUserTable.orgId]?.toString(),
|
|
||||||
status = row[SysUserTable.status],
|
|
||||||
statusLabel = statusLabel(row[SysUserTable.status]),
|
|
||||||
roleCodes = roleMap[row[SysUserTable.id]] ?: emptyList(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
page = page,
|
|
||||||
pageSize = pageSize,
|
|
||||||
total = total,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun create(request: CreateUserRequest): String = dbQuery {
|
suspend fun create(request: CreateUserRequest): String = dbQuery {
|
||||||
val username = request.username.trim()
|
val username = request.username.trim()
|
||||||
if (username.isBlank() || request.password.isBlank()) {
|
if (username.isBlank() || request.password.isBlank()) {
|
||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "用户名和密码不能为空")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "用户名和密码不能为空")
|
||||||
}
|
}
|
||||||
val existed = SysUserTable.selectAll().where {
|
if (UserDao.findByUsername(username) != null) {
|
||||||
(SysUserTable.username eq username) and SysUserTable.deletedAt.isNull()
|
|
||||||
}.any()
|
|
||||||
if (existed) {
|
|
||||||
throw BizException(ErrorCode.DATA_CONFLICT.code, "用户名已存在")
|
throw BizException(ErrorCode.DATA_CONFLICT.code, "用户名已存在")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,142 +40,59 @@ object UserService {
|
|||||||
if (orgUuid != null) {
|
if (orgUuid != null) {
|
||||||
ensureOrgExists(orgUuid)
|
ensureOrgExists(orgUuid)
|
||||||
}
|
}
|
||||||
val now = OffsetDateTime.now()
|
UserDao.create(request, PasswordService.hash(request.password), orgUuid)
|
||||||
val row = SysUserTable.insert {
|
|
||||||
it[SysUserTable.username] = username
|
|
||||||
it[passwordHash] = PasswordService.hash(request.password)
|
|
||||||
it[nickname] = request.nickname?.trim()
|
|
||||||
it[realName] = request.realName?.trim()
|
|
||||||
it[phone] = request.phone?.trim()
|
|
||||||
it[email] = request.email?.trim()
|
|
||||||
it[avatar] = request.avatar?.trim()
|
|
||||||
it[orgId] = orgUuid
|
|
||||||
it[status] = request.status
|
|
||||||
it[tokenVersion] = 1
|
|
||||||
it[createdAt] = now
|
|
||||||
}
|
|
||||||
row[SysUserTable.id].toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun detail(id: Uuid): UserDetailResponse = dbQuery {
|
suspend fun detail(id: Uuid): UserDetailResponse = dbQuery { UserDao.detail(id) }
|
||||||
val user = requireUser(id)
|
|
||||||
val roleIds = SysUserRoleTable.selectAll().where { SysUserRoleTable.userId eq id }
|
|
||||||
.map { it[SysUserRoleTable.roleId].toString() }
|
|
||||||
UserDetailResponse(
|
|
||||||
id = user[SysUserTable.id].toString(),
|
|
||||||
username = user[SysUserTable.username],
|
|
||||||
nickname = user[nickname],
|
|
||||||
realName = user[realName],
|
|
||||||
phone = user[phone],
|
|
||||||
email = user[email],
|
|
||||||
avatar = user[avatar],
|
|
||||||
orgId = user[SysUserTable.orgId]?.toString(),
|
|
||||||
status = user[SysUserTable.status],
|
|
||||||
statusLabel = statusLabel(user[SysUserTable.status]),
|
|
||||||
roleIds = roleIds,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun update(id: Uuid, request: UpdateUserRequest) = dbQuery {
|
suspend fun update(id: Uuid, request: UpdateUserRequest) = dbQuery {
|
||||||
requireUser(id)
|
UserDao.requireActive(id)
|
||||||
val orgUuid = request.orgId?.let { parseUuid(it, "orgId") }
|
val orgUuid = request.orgId?.let { parseUuid(it, "orgId") }
|
||||||
if (orgUuid != null) {
|
if (orgUuid != null) {
|
||||||
ensureOrgExists(orgUuid)
|
ensureOrgExists(orgUuid)
|
||||||
}
|
}
|
||||||
SysUserTable.update({ SysUserTable.id eq id }) {
|
UserDao.updateProfile(id, request, orgUuid)
|
||||||
it[nickname] = request.nickname?.trim()
|
|
||||||
it[realName] = request.realName?.trim()
|
|
||||||
it[phone] = request.phone?.trim()
|
|
||||||
it[email] = request.email?.trim()
|
|
||||||
it[avatar] = request.avatar?.trim()
|
|
||||||
it[orgId] = orgUuid
|
|
||||||
it[updatedAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun softDelete(id: Uuid) = dbQuery {
|
suspend fun softDelete(id: Uuid) = dbQuery {
|
||||||
if (id.toString() == "00000000-0000-0000-0000-000000000000") {
|
if (id.toString() == "00000000-0000-0000-0000-000000000000") {
|
||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "系统保留用户不可删除")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "系统保留用户不可删除")
|
||||||
}
|
}
|
||||||
requireUser(id)
|
UserDao.requireActive(id)
|
||||||
SysUserTable.update({ SysUserTable.id eq id }) {
|
UserDao.softDelete(id)
|
||||||
it[deletedAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
SysUserRoleTable.deleteWhere { SysUserRoleTable.userId eq id }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateStatus(id: Uuid, request: UpdateUserStatusRequest) = dbQuery {
|
suspend fun updateStatus(id: Uuid, request: UpdateUserStatusRequest) = dbQuery {
|
||||||
requireUser(id)
|
UserDao.requireActive(id)
|
||||||
SysUserTable.update({ SysUserTable.id eq id }) {
|
UserDao.updateStatus(id, request.status)
|
||||||
it[status] = request.status
|
|
||||||
it[updatedAt] = OffsetDateTime.now()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updatePassword(id: Uuid, request: UpdateUserPasswordRequest) = dbQuery {
|
suspend fun updatePassword(id: Uuid, request: UpdateUserPasswordRequest) = dbQuery {
|
||||||
val user = requireUser(id)
|
val user = UserDao.requireActive(id)
|
||||||
if (request.password.isBlank()) {
|
if (request.password.isBlank()) {
|
||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "密码不能为空")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "密码不能为空")
|
||||||
}
|
}
|
||||||
val nextTokenVersion = user[SysUserTable.tokenVersion] + 1
|
UserDao.updatePassword(
|
||||||
SysUserTable.update({ SysUserTable.id eq id }) {
|
id = id,
|
||||||
it[passwordHash] = PasswordService.hash(request.password)
|
passwordHash = PasswordService.hash(request.password),
|
||||||
it[tokenVersion] = nextTokenVersion
|
nextTokenVersion = user[com.bbit.ticket.database.system.SysUserTable.tokenVersion] + 1,
|
||||||
it[updatedAt] = OffsetDateTime.now()
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateRoles(id: Uuid, request: UpdateUserRolesRequest) = dbQuery {
|
suspend fun updateRoles(id: Uuid, request: UpdateUserRolesRequest) = dbQuery {
|
||||||
requireUser(id)
|
UserDao.requireActive(id)
|
||||||
val roleIds = request.roleIds.distinct().map { parseUuid(it, "roleId") }
|
val roleIds = request.roleIds.distinct().map { parseUuid(it, "roleId") }
|
||||||
if (roleIds.isNotEmpty()) {
|
if (roleIds.isNotEmpty()) {
|
||||||
val validCount = SysRoleTable.selectAll().where {
|
if (UserDao.countEnabledRoles(roleIds) != roleIds.size.toLong()) {
|
||||||
(SysRoleTable.id inList roleIds) and
|
|
||||||
(SysRoleTable.status eq "ENABLED") and
|
|
||||||
SysRoleTable.deletedAt.isNull()
|
|
||||||
}.count()
|
|
||||||
if (validCount != roleIds.size.toLong()) {
|
|
||||||
throw BizException(ErrorCode.BAD_REQUEST.code, "包含不存在或已禁用角色")
|
throw BizException(ErrorCode.BAD_REQUEST.code, "包含不存在或已禁用角色")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SysUserRoleTable.deleteWhere { SysUserRoleTable.userId eq id }
|
UserDao.replaceRoles(id, roleIds)
|
||||||
roleIds.forEach { roleId ->
|
|
||||||
SysUserRoleTable.insertIgnore {
|
|
||||||
it[userId] = id
|
|
||||||
it[SysUserRoleTable.roleId] = roleId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildWhere(username: String?, nickname: String?, status: String?, orgId: Uuid?): Op<Boolean> {
|
|
||||||
var where: Op<Boolean> = SysUserTable.deletedAt.isNull()
|
|
||||||
if (!username.isNullOrBlank()) {
|
|
||||||
where = where and (SysUserTable.username like "%$username%")
|
|
||||||
}
|
|
||||||
if (!nickname.isNullOrBlank()) {
|
|
||||||
where = where and (SysUserTable.nickname like "%$nickname%")
|
|
||||||
}
|
|
||||||
if (!status.isNullOrBlank()) {
|
|
||||||
where = where and (SysUserTable.status eq status)
|
|
||||||
}
|
|
||||||
if (orgId != null) {
|
|
||||||
where = where and (SysUserTable.orgId eq orgId)
|
|
||||||
}
|
|
||||||
return where
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun requireUser(id: Uuid) =
|
|
||||||
SysUserTable.selectAll().where { (SysUserTable.id eq id) and SysUserTable.deletedAt.isNull() }.singleOrNull()
|
|
||||||
?: throw BizException(
|
|
||||||
ErrorCode.USER_NOT_FOUND.code,
|
|
||||||
ErrorCode.USER_NOT_FOUND.message,
|
|
||||||
HttpStatusCode.NotFound
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun ensureOrgExists(orgId: Uuid) {
|
private fun ensureOrgExists(orgId: Uuid) {
|
||||||
val exists = SysOrgTable.selectAll().where { (SysOrgTable.id eq orgId) and SysOrgTable.deletedAt.isNull() }.any()
|
if (!UserDao.orgExists(orgId)) {
|
||||||
if (!exists) {
|
throw BizException(ErrorCode.ORG_NOT_FOUND.code, ErrorCode.ORG_NOT_FOUND.message)
|
||||||
throw BizException(ErrorCode.ORG_NOT_FOUND.code, ErrorCode.ORG_NOT_FOUND.message, HttpStatusCode.BadRequest)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
@Test
|
||||||
|
fun helloWorld() {
|
||||||
|
print("Hello World!")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user