refactor(vertx-demo):重构项目目录结构
This commit is contained in:
parent
047717ad71
commit
95f76404c2
@ -1,3 +1,5 @@
|
|||||||
|
闲暇时编写,问题较多,仅供参考。
|
||||||
|
|
||||||
# Vert.x 快速开发模板
|
# Vert.x 快速开发模板
|
||||||
|
|
||||||
这是一个基于 Vert.x 库的后端快速开发模板,采用 Kotlin 语言和协程实现高性能异步编程。本项目提供了完整的开发框架,能够帮助开发者快速搭建响应式、模块化、高性能的后端服务。
|
这是一个基于 Vert.x 库的后端快速开发模板,采用 Kotlin 语言和协程实现高性能异步编程。本项目提供了完整的开发框架,能够帮助开发者快速搭建响应式、模块化、高性能的后端服务。
|
||||||
@ -7,7 +9,7 @@
|
|||||||
- **核心框架**: Vert.x 4.5.x (响应式、事件驱动框架)
|
- **核心框架**: Vert.x 4.5.x (响应式、事件驱动框架)
|
||||||
- **编程语言**: Kotlin 1.9.x (协程支持)
|
- **编程语言**: Kotlin 1.9.x (协程支持)
|
||||||
- **依赖注入**: Google Guice 7.0.0
|
- **依赖注入**: Google Guice 7.0.0
|
||||||
- **数据库**: PostgreSQL (主), MySQL (可选)
|
- **数据库**: PostgreSQL, (不支持MySQL)
|
||||||
- **缓存**: Redis
|
- **缓存**: Redis
|
||||||
- **认证**: JWT
|
- **认证**: JWT
|
||||||
- **构建工具**: Gradle with Kotlin DSL
|
- **构建工具**: Gradle with Kotlin DSL
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package app.config
|
|||||||
|
|
||||||
import app.config.provider.JWTAuthProvider
|
import app.config.provider.JWTAuthProvider
|
||||||
import app.config.provider.DbPoolProvider
|
import app.config.provider.DbPoolProvider
|
||||||
|
import app.config.provider.RedisProvider
|
||||||
import cn.hutool.core.lang.Snowflake
|
import cn.hutool.core.lang.Snowflake
|
||||||
import cn.hutool.core.util.IdUtil
|
import cn.hutool.core.util.IdUtil
|
||||||
import com.google.inject.AbstractModule
|
import com.google.inject.AbstractModule
|
||||||
@ -10,6 +11,7 @@ import com.google.inject.Injector
|
|||||||
import com.google.inject.Singleton
|
import com.google.inject.Singleton
|
||||||
import io.vertx.core.Vertx
|
import io.vertx.core.Vertx
|
||||||
import io.vertx.ext.auth.jwt.JWTAuth
|
import io.vertx.ext.auth.jwt.JWTAuth
|
||||||
|
import io.vertx.redis.client.Redis
|
||||||
import io.vertx.sqlclient.Pool
|
import io.vertx.sqlclient.Pool
|
||||||
import io.vertx.sqlclient.SqlClient
|
import io.vertx.sqlclient.SqlClient
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -41,6 +43,7 @@ class InjectorModule(
|
|||||||
|
|
||||||
bind(Snowflake::class.java).toInstance(IdUtil.getSnowflake())
|
bind(Snowflake::class.java).toInstance(IdUtil.getSnowflake())
|
||||||
|
|
||||||
|
bind(Redis::class.java).toProvider(RedisProvider::class.java).`in`(Singleton::class.java)
|
||||||
bind(Pool::class.java).toProvider(DbPoolProvider::class.java).`in`(Singleton::class.java)
|
bind(Pool::class.java).toProvider(DbPoolProvider::class.java).`in`(Singleton::class.java)
|
||||||
bind(SqlClient::class.java).to(Pool::class.java)
|
bind(SqlClient::class.java).to(Pool::class.java)
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,24 @@
|
|||||||
|
package app.config.provider
|
||||||
|
|
||||||
|
import com.google.inject.Inject
|
||||||
|
import com.google.inject.Provider
|
||||||
|
import io.vertx.core.Vertx
|
||||||
|
import io.vertx.redis.client.Redis
|
||||||
|
import io.vertx.redis.client.RedisClientType
|
||||||
|
import io.vertx.redis.client.RedisOptions
|
||||||
|
import org.aikrai.vertx.config.RedisConfig
|
||||||
|
|
||||||
|
class RedisProvider @Inject constructor(
|
||||||
|
private val vertx: Vertx,
|
||||||
|
private val redisConfig: RedisConfig
|
||||||
|
) : Provider<Redis> {
|
||||||
|
override fun get(): Redis {
|
||||||
|
val options = RedisOptions()
|
||||||
|
.setType(RedisClientType.STANDALONE)
|
||||||
|
.addConnectionString("redis://${redisConfig.host}:${redisConfig.port}/${redisConfig.db}")
|
||||||
|
.setMaxPoolSize(redisConfig.poolSize)
|
||||||
|
.setMaxPoolWaiting(redisConfig.maxPoolWaiting)
|
||||||
|
redisConfig.password?.let { options.setPassword(it) }
|
||||||
|
return Redis.createClient(vertx, options)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
package app.controller
|
package app.controller
|
||||||
|
|
||||||
import app.data.domain.account.LoginDTO
|
import app.data.dto.account.LoginDTO
|
||||||
import app.service.account.AccountService
|
import app.service.account.AccountService
|
||||||
import com.google.inject.Inject
|
import com.google.inject.Inject
|
||||||
import io.vertx.ext.web.RoutingContext
|
import io.vertx.ext.web.RoutingContext
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package app.controller
|
package app.controller
|
||||||
|
|
||||||
import app.data.domain.account.Account
|
import app.data.domain.account.Account
|
||||||
import app.data.domain.account.AccountRepository
|
import app.repository.AccountRepository
|
||||||
import app.data.emun.Status
|
import app.data.emun.Status
|
||||||
import app.service.account.AccountService
|
import app.service.account.AccountService
|
||||||
import com.google.inject.Inject
|
import com.google.inject.Inject
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package app.data.domain.account
|
package app.data.domain.account
|
||||||
|
|
||||||
|
import app.data.emun.SexType
|
||||||
import app.data.emun.Status
|
import app.data.emun.Status
|
||||||
import org.aikrai.vertx.db.annotation.*
|
import org.aikrai.vertx.db.annotation.*
|
||||||
import org.aikrai.vertx.jackson.JsonUtil
|
import org.aikrai.vertx.jackson.JsonUtil
|
||||||
@ -13,21 +14,47 @@ class Account : BaseEntity() {
|
|||||||
@TableFieldComment("用户ID")
|
@TableFieldComment("用户ID")
|
||||||
var userId: Long = 0L
|
var userId: Long = 0L
|
||||||
|
|
||||||
@TableField("user_name")
|
@TableFieldComment("部门ID")
|
||||||
var userName: String? = ""
|
var deptId: Long? = 0L
|
||||||
|
|
||||||
|
@TableField(length = 30)
|
||||||
|
@TableFieldComment("用户账号")
|
||||||
|
var userName: String = ""
|
||||||
|
|
||||||
|
@TableField(length = 30)
|
||||||
|
@TableFieldComment("用户昵称")
|
||||||
|
var nickName: String = ""
|
||||||
|
|
||||||
|
@TableField(length = 2)
|
||||||
|
@TableFieldComment("用户类型")
|
||||||
var userType: String? = ""
|
var userType: String? = ""
|
||||||
|
|
||||||
|
@TableField(length = 50)
|
||||||
|
@TableFieldComment("用户邮箱")
|
||||||
var email: String? = ""
|
var email: String? = ""
|
||||||
|
|
||||||
var phone: String? = ""
|
@TableField(length = 11)
|
||||||
|
@TableFieldComment("手机号码")
|
||||||
|
var phonenumber: String? = ""
|
||||||
|
|
||||||
|
@TableField(length = 1)
|
||||||
|
@TableFieldComment("用户性别(0男 1女 2未知)")
|
||||||
|
var sex: SexType? = SexType.UNKNOWN
|
||||||
|
|
||||||
|
@TableField(length = 100)
|
||||||
|
@TableFieldComment("头像地址")
|
||||||
var avatar: String? = null
|
var avatar: String? = null
|
||||||
|
|
||||||
|
@TableField(length = 100)
|
||||||
|
@TableFieldComment("密码")
|
||||||
var password: String? = null
|
var password: String? = null
|
||||||
|
|
||||||
|
@TableField(length = 1)
|
||||||
|
@TableFieldComment("帐号状态(0正常 1停用)")
|
||||||
var status: Status? = Status.ACTIVE
|
var status: Status? = Status.ACTIVE
|
||||||
|
|
||||||
|
@TableField(length = 1)
|
||||||
|
@TableFieldComment("删除标志(0代表存在 2代表删除)")
|
||||||
var delFlag: Char? = null
|
var delFlag: Char? = null
|
||||||
|
|
||||||
var loginIp: String? = null
|
var loginIp: String? = null
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
package app.data.domain.role
|
|
||||||
|
|
||||||
import com.google.inject.ImplementedBy
|
|
||||||
import org.aikrai.vertx.db.wrapper.Repository
|
|
||||||
|
|
||||||
@ImplementedBy(RoleRepositoryImpl::class)
|
|
||||||
interface RoleRepository : Repository<Long, Role>
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package app.data.domain.account.modle
|
package app.data.dto.account
|
||||||
|
|
||||||
import app.data.domain.account.Account
|
import app.data.domain.account.Account
|
||||||
import app.data.domain.menu.Menu
|
import app.data.domain.menu.Menu
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package app.data.domain.account.modle
|
package app.data.dto.account
|
||||||
|
|
||||||
import app.data.domain.role.Role
|
import app.data.domain.role.Role
|
||||||
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
package app.data.domain.account
|
package app.data.dto.account
|
||||||
|
|
||||||
data class LoginDTO(
|
data class LoginDTO(
|
||||||
var username: String,
|
var username: String,
|
||||||
var password: String
|
var password: String
|
||||||
)
|
)
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package app.base.domain.auth.modle
|
package app.data.dto.account
|
||||||
|
|
||||||
class LoginUser {
|
class LoginUser {
|
||||||
var accountId: Long = 0L
|
var accountId: Long = 0L
|
||||||
@ -7,4 +7,4 @@ class LoginUser {
|
|||||||
var expireTime: Long = 0L
|
var expireTime: Long = 0L
|
||||||
var ipaddr: String = ""
|
var ipaddr: String = ""
|
||||||
var client: String = ""
|
var client: String = ""
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package app.data.domain.menu.modle
|
package app.data.dto.menu
|
||||||
|
|
||||||
import app.data.domain.menu.Menu
|
import app.data.domain.menu.Menu
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package app.base.domain.auth.menu
|
package app.data.emun
|
||||||
|
|
||||||
enum class MenuType(val desc: String) {
|
enum class MenuType(val desc: String) {
|
||||||
M("目录"),
|
M("目录"),
|
||||||
@ -8,7 +8,7 @@ enum class MenuType(val desc: String) {
|
|||||||
companion object {
|
companion object {
|
||||||
fun parse(value: String?): MenuType? {
|
fun parse(value: String?): MenuType? {
|
||||||
if (value.isNullOrBlank()) return null
|
if (value.isNullOrBlank()) return null
|
||||||
return MenuType.values().find { it.name == value || it.desc == value }
|
return MenuType.entries.find { it.name == value || it.desc == value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
28
vertx-demo/src/main/kotlin/app/data/emun/SexType.kt
Normal file
28
vertx-demo/src/main/kotlin/app/data/emun/SexType.kt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package app.data.emun
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue
|
||||||
|
import org.aikrai.vertx.db.annotation.EnumValue
|
||||||
|
|
||||||
|
enum class SexType(private val code: Int, private val description: String) {
|
||||||
|
MALE(0, "男"),
|
||||||
|
FEMALE(1, "女"),
|
||||||
|
UNKNOWN(2, "未知");
|
||||||
|
|
||||||
|
@JsonValue
|
||||||
|
@EnumValue
|
||||||
|
fun getCode(): Int {
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return description
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JsonCreator
|
||||||
|
fun parse(code: Int): SexType? {
|
||||||
|
return SexType.entries.find { it.code == code }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,46 +0,0 @@
|
|||||||
package app.port.reids
|
|
||||||
|
|
||||||
import com.google.inject.Inject
|
|
||||||
import com.google.inject.Singleton
|
|
||||||
import io.vertx.core.Vertx
|
|
||||||
import io.vertx.kotlin.coroutines.coAwait
|
|
||||||
import io.vertx.redis.client.*
|
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
|
||||||
import org.aikrai.vertx.config.RedisConfig
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class RedisClient @Inject constructor(
|
|
||||||
vertx: Vertx,
|
|
||||||
redisConfig: RedisConfig
|
|
||||||
) {
|
|
||||||
private val logger = KotlinLogging.logger { }
|
|
||||||
|
|
||||||
private var redisClient = Redis.createClient(
|
|
||||||
vertx,
|
|
||||||
RedisOptions()
|
|
||||||
.setType(RedisClientType.STANDALONE)
|
|
||||||
.addConnectionString("redis://${redisConfig.host}:${redisConfig.port}/${redisConfig.db}")
|
|
||||||
.setPassword(redisConfig.pass ?: "")
|
|
||||||
.setMaxPoolSize(redisConfig.poolSize)
|
|
||||||
.setMaxPoolWaiting(redisConfig.maxPoolWaiting)
|
|
||||||
)
|
|
||||||
|
|
||||||
// EX秒,PX毫秒
|
|
||||||
suspend fun set(key: String, value: String, expireSeconds: Int) {
|
|
||||||
redisClient.send(Request.cmd(Command.SET, key, value, "EX", expireSeconds))
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun get(key: String): String? {
|
|
||||||
val res = redisClient.send(Request.cmd(Command.GET, key)).coAwait()
|
|
||||||
return res?.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun incr(key: String): Int {
|
|
||||||
val res = redisClient.send(Request.cmd(Command.INCR, key)).coAwait()
|
|
||||||
return res?.toInteger() ?: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fun expire(key: String, expire: String) {
|
|
||||||
redisClient.send(Request.cmd(Command.EXPIRE, key, expire))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +1,9 @@
|
|||||||
package app.data.domain.account
|
package app.repository
|
||||||
|
|
||||||
import app.data.domain.account.modle.AccountRoleAccessDTO
|
import app.data.domain.account.Account
|
||||||
import app.data.domain.account.modle.AccountRoleDTO
|
import app.repository.impl.AccountRepositoryImpl
|
||||||
|
import app.data.dto.account.AccountRoleAccessDTO
|
||||||
|
import app.data.dto.account.AccountRoleDTO
|
||||||
import com.google.inject.ImplementedBy
|
import com.google.inject.ImplementedBy
|
||||||
import org.aikrai.vertx.db.wrapper.Repository
|
import org.aikrai.vertx.db.wrapper.Repository
|
||||||
|
|
||||||
@ -14,4 +16,4 @@ interface AccountRepository : Repository<Long, Account> {
|
|||||||
suspend fun getAccountRole(id: Long): AccountRoleDTO?
|
suspend fun getAccountRole(id: Long): AccountRoleDTO?
|
||||||
suspend fun bindRoles(id: Long, roles: List<Long>)
|
suspend fun bindRoles(id: Long, roles: List<Long>)
|
||||||
suspend fun removeAllRole(id: Long): Int
|
suspend fun removeAllRole(id: Long): Int
|
||||||
}
|
}
|
||||||
@ -1,9 +1,11 @@
|
|||||||
package app.data.domain.menu
|
package app.repository
|
||||||
|
|
||||||
|
import app.data.domain.menu.Menu
|
||||||
|
import app.repository.impl.MenuRepositoryImpl
|
||||||
import com.google.inject.ImplementedBy
|
import com.google.inject.ImplementedBy
|
||||||
import org.aikrai.vertx.db.wrapper.Repository
|
import org.aikrai.vertx.db.wrapper.Repository
|
||||||
|
|
||||||
@ImplementedBy(MenuRepositoryImpl::class)
|
@ImplementedBy(MenuRepositoryImpl::class)
|
||||||
interface MenuRepository : Repository<Long, Menu> {
|
interface MenuRepository : Repository<Long, Menu> {
|
||||||
suspend fun list(name: String? = null, accountId: Long? = null, roleId: Long? = null): List<Menu>
|
suspend fun list(name: String? = null, accountId: Long? = null, roleId: Long? = null): List<Menu>
|
||||||
}
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package app.repository
|
||||||
|
|
||||||
|
import app.data.domain.role.Role
|
||||||
|
import app.repository.impl.RoleRepositoryImpl
|
||||||
|
import com.google.inject.ImplementedBy
|
||||||
|
import org.aikrai.vertx.db.wrapper.Repository
|
||||||
|
|
||||||
|
@ImplementedBy(RoleRepositoryImpl::class)
|
||||||
|
interface RoleRepository : Repository<Long, Role>
|
||||||
@ -1,7 +1,9 @@
|
|||||||
package app.data.domain.account
|
package app.repository.impl
|
||||||
|
|
||||||
import app.data.domain.account.modle.AccountRoleAccessDTO
|
import app.data.domain.account.Account
|
||||||
import app.data.domain.account.modle.AccountRoleDTO
|
import app.data.dto.account.AccountRoleAccessDTO
|
||||||
|
import app.data.dto.account.AccountRoleDTO
|
||||||
|
import app.repository.AccountRepository
|
||||||
import com.google.inject.Inject
|
import com.google.inject.Inject
|
||||||
import io.vertx.sqlclient.SqlClient
|
import io.vertx.sqlclient.SqlClient
|
||||||
import org.aikrai.vertx.db.wrapper.RepositoryImpl
|
import org.aikrai.vertx.db.wrapper.RepositoryImpl
|
||||||
@ -16,7 +18,7 @@ class AccountRepositoryImpl @Inject constructor(
|
|||||||
): List<Account> {
|
): List<Account> {
|
||||||
return queryBuilder()
|
return queryBuilder()
|
||||||
.eq(!userName.isNullOrBlank(), Account::userName, userName)
|
.eq(!userName.isNullOrBlank(), Account::userName, userName)
|
||||||
.eq(!phone.isNullOrBlank(), Account::phone, phone)
|
.eq(!phone.isNullOrBlank(), Account::phonenumber, phone)
|
||||||
.getList()
|
.getList()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,4 +103,4 @@ class AccountRepositoryImpl @Inject constructor(
|
|||||||
val sql = "DELETE FROM account_role WHERE account_id = #{$id}"
|
val sql = "DELETE FROM account_role WHERE account_id = #{$id}"
|
||||||
return execute(sql)
|
return execute(sql)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
package app.data.domain.menu
|
package app.repository.impl
|
||||||
|
|
||||||
|
import app.data.domain.menu.Menu
|
||||||
|
import app.repository.MenuRepository
|
||||||
import com.google.inject.Inject
|
import com.google.inject.Inject
|
||||||
import io.vertx.sqlclient.SqlClient
|
import io.vertx.sqlclient.SqlClient
|
||||||
import org.aikrai.vertx.db.wrapper.RepositoryImpl
|
import org.aikrai.vertx.db.wrapper.RepositoryImpl
|
||||||
@ -11,4 +13,4 @@ class MenuRepositoryImpl @Inject constructor(
|
|||||||
override suspend fun list(name: String?, accountId: Long?, roleId: Long?): List<Menu> {
|
override suspend fun list(name: String?, accountId: Long?, roleId: Long?): List<Menu> {
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,9 +1,11 @@
|
|||||||
package app.data.domain.role
|
package app.repository.impl
|
||||||
|
|
||||||
|
import app.data.domain.role.Role
|
||||||
|
import app.repository.RoleRepository
|
||||||
import com.google.inject.Inject
|
import com.google.inject.Inject
|
||||||
import io.vertx.sqlclient.SqlClient
|
import io.vertx.sqlclient.SqlClient
|
||||||
import org.aikrai.vertx.db.wrapper.RepositoryImpl
|
import org.aikrai.vertx.db.wrapper.RepositoryImpl
|
||||||
|
|
||||||
class RoleRepositoryImpl @Inject constructor(
|
class RoleRepositoryImpl @Inject constructor(
|
||||||
sqlClient: SqlClient
|
sqlClient: SqlClient
|
||||||
) : RepositoryImpl<Long, Role>(sqlClient), RoleRepository
|
) : RepositoryImpl<Long, Role>(sqlClient), RoleRepository
|
||||||
@ -1,6 +1,8 @@
|
|||||||
package app.data.domain.menu
|
package app.service
|
||||||
|
|
||||||
import app.data.domain.account.Account
|
import app.data.domain.account.Account
|
||||||
|
import app.data.domain.menu.Menu
|
||||||
|
import app.repository.MenuRepository
|
||||||
import com.google.inject.Inject
|
import com.google.inject.Inject
|
||||||
import com.google.inject.Singleton
|
import com.google.inject.Singleton
|
||||||
import io.vertx.ext.auth.User
|
import io.vertx.ext.auth.User
|
||||||
@ -47,7 +49,7 @@ class MenuManager @Inject constructor(
|
|||||||
perms: String
|
perms: String
|
||||||
) {
|
) {
|
||||||
if (menuRepository.list(menuName).isNotEmpty()) {
|
if (menuRepository.list(menuName).isNotEmpty()) {
|
||||||
throw Meta.error("MenuNameConflict", "菜单名称已存在")
|
throw Meta.Companion.error("MenuNameConflict", "菜单名称已存在")
|
||||||
}
|
}
|
||||||
val menu = Menu().apply {
|
val menu = Menu().apply {
|
||||||
this.menuName = menuName
|
this.menuName = menuName
|
||||||
@ -73,10 +75,10 @@ class MenuManager @Inject constructor(
|
|||||||
visible: String?,
|
visible: String?,
|
||||||
perms: String?
|
perms: String?
|
||||||
) {
|
) {
|
||||||
val menu = menuRepository.get(menuId) ?: throw Meta.notFound("MenuNotFound", "菜单不存在")
|
val menu = menuRepository.get(menuId) ?: throw Meta.Companion.notFound("MenuNotFound", "菜单不存在")
|
||||||
|
|
||||||
if (menuName != null && menuName != menu.menuName && menuRepository.list(menuName).isNotEmpty()) {
|
if (menuName != null && menuName != menu.menuName && menuRepository.list(menuName).isNotEmpty()) {
|
||||||
throw Meta.error("MenuNameConflict", "菜单名称已存在")
|
throw Meta.Companion.error("MenuNameConflict", "菜单名称已存在")
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.apply {
|
menu.apply {
|
||||||
@ -147,4 +149,4 @@ class MenuManager @Inject constructor(
|
|||||||
return getChildList(list, t).isNotEmpty()
|
return getChildList(list, t).isNotEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,8 +1,8 @@
|
|||||||
package app.service.account
|
package app.service.account
|
||||||
|
|
||||||
import app.data.domain.account.Account
|
import app.data.domain.account.Account
|
||||||
import app.data.domain.account.AccountRepository
|
import app.repository.AccountRepository
|
||||||
import app.data.domain.account.LoginDTO
|
import app.data.dto.account.LoginDTO
|
||||||
import app.service.auth.TokenService
|
import app.service.auth.TokenService
|
||||||
import cn.hutool.core.lang.Snowflake
|
import cn.hutool.core.lang.Snowflake
|
||||||
import cn.hutool.crypto.SecureUtil
|
import cn.hutool.crypto.SecureUtil
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package app.service.auth
|
package app.service.auth
|
||||||
|
|
||||||
import app.data.domain.account.AccountRepository
|
import app.repository.AccountRepository
|
||||||
import app.port.reids.RedisClient
|
import app.utils.RedisUtil
|
||||||
import cn.hutool.core.util.IdUtil
|
import cn.hutool.core.util.IdUtil
|
||||||
import com.google.inject.Inject
|
import com.google.inject.Inject
|
||||||
import com.google.inject.Singleton
|
import com.google.inject.Singleton
|
||||||
@ -14,20 +14,24 @@ import io.vertx.ext.auth.jwt.JWTAuth
|
|||||||
import io.vertx.ext.web.RoutingContext
|
import io.vertx.ext.web.RoutingContext
|
||||||
import io.vertx.kotlin.coroutines.coAwait
|
import io.vertx.kotlin.coroutines.coAwait
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
|
import io.vertx.redis.client.Redis
|
||||||
import org.aikrai.vertx.auth.AuthUser
|
import org.aikrai.vertx.auth.AuthUser
|
||||||
import org.aikrai.vertx.constant.CacheConstants
|
import org.aikrai.vertx.constant.CacheConstants
|
||||||
import org.aikrai.vertx.constant.Constants
|
import org.aikrai.vertx.constant.Constants
|
||||||
import org.aikrai.vertx.jackson.JsonUtil
|
import org.aikrai.vertx.jackson.JsonUtil
|
||||||
import org.aikrai.vertx.utlis.Meta
|
import org.aikrai.vertx.utlis.Meta
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class TokenService @Inject constructor(
|
class TokenService @Inject constructor(
|
||||||
|
redis: Redis,
|
||||||
private val jwtAuth: JWTAuth,
|
private val jwtAuth: JWTAuth,
|
||||||
private val redisClient: RedisClient,
|
|
||||||
private val accountRepository: AccountRepository,
|
private val accountRepository: AccountRepository,
|
||||||
) {
|
) {
|
||||||
private val logger = KotlinLogging.logger { }
|
private val logger = KotlinLogging.logger { }
|
||||||
private val expireSeconds = 60 * 60 * 24 * 7
|
private val expireSeconds = 60L * 60 * 24 * 7
|
||||||
|
private val redisUtil = RedisUtil(redis)
|
||||||
|
|
||||||
|
|
||||||
suspend fun getLoginUser(ctx: RoutingContext): AuthUser {
|
suspend fun getLoginUser(ctx: RoutingContext): AuthUser {
|
||||||
val request = ctx.request()
|
val request = ctx.request()
|
||||||
@ -38,7 +42,7 @@ class TokenService @Inject constructor(
|
|||||||
val token = authorization.substring(6)
|
val token = authorization.substring(6)
|
||||||
val user = parseToken(token) ?: throw Meta.unauthorized("token")
|
val user = parseToken(token) ?: throw Meta.unauthorized("token")
|
||||||
val userToken = user.principal().getString(Constants.LOGIN_USER_KEY) ?: throw Meta.unauthorized("token")
|
val userToken = user.principal().getString(Constants.LOGIN_USER_KEY) ?: throw Meta.unauthorized("token")
|
||||||
val authInfoStr = redisClient.get(CacheConstants.LOGIN_TOKEN_KEY + userToken) ?: throw Meta.unauthorized("token")
|
val authInfoStr = redisUtil.getObject<String>(CacheConstants.LOGIN_TOKEN_KEY + userToken) ?: throw Meta.unauthorized("token")
|
||||||
return JsonUtil.parseObject(authInfoStr, AuthUser::class.java)
|
return JsonUtil.parseObject(authInfoStr, AuthUser::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +52,7 @@ class TokenService @Inject constructor(
|
|||||||
val user = userInfo?.account ?: throw Meta.notFound("AccountNotFound", "账号不存在")
|
val user = userInfo?.account ?: throw Meta.notFound("AccountNotFound", "账号不存在")
|
||||||
val authInfo = AuthUser(userInfo.account.userId, token, JsonUtil.toJsonObject(user), userInfo.rolesArr.toSet(), userInfo.accessArr.toSet(), ip, client)
|
val authInfo = AuthUser(userInfo.account.userId, token, JsonUtil.toJsonObject(user), userInfo.rolesArr.toSet(), userInfo.accessArr.toSet(), ip, client)
|
||||||
val authInfoStr = JsonUtil.toJsonStr(authInfo)
|
val authInfoStr = JsonUtil.toJsonStr(authInfo)
|
||||||
redisClient.set(CacheConstants.LOGIN_TOKEN_KEY + token, authInfoStr, expireSeconds)
|
redisUtil.setObject(CacheConstants.LOGIN_TOKEN_KEY + token, authInfoStr, expireSeconds, TimeUnit.SECONDS)
|
||||||
return genToken(mapOf(Constants.LOGIN_USER_KEY to token))
|
return genToken(mapOf(Constants.LOGIN_USER_KEY to token))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package app.util
|
package app.utils
|
||||||
|
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine
|
import com.github.benmanes.caffeine.cache.Caffeine
|
||||||
import com.google.inject.Singleton
|
import com.google.inject.Singleton
|
||||||
835
vertx-demo/src/main/kotlin/app/utils/RedisUtil.kt
Normal file
835
vertx-demo/src/main/kotlin/app/utils/RedisUtil.kt
Normal file
@ -0,0 +1,835 @@
|
|||||||
|
package app.utils
|
||||||
|
|
||||||
|
import io.vertx.kotlin.coroutines.coAwait
|
||||||
|
import io.vertx.redis.client.Command
|
||||||
|
import io.vertx.redis.client.Redis
|
||||||
|
import io.vertx.redis.client.Request
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class RedisUtil constructor(private val redis: Redis) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存基本的对象,Integer、String、实体类等
|
||||||
|
*
|
||||||
|
* @param key 缓存的键值
|
||||||
|
* @param value 缓存的值
|
||||||
|
*/
|
||||||
|
suspend fun <T> setObject(key: String, value: T): Boolean {
|
||||||
|
val request = Request.cmd(Command.SET)
|
||||||
|
.arg(key)
|
||||||
|
.arg(value.toString())
|
||||||
|
.arg("KEEPTTL")
|
||||||
|
val response = redis.send(request).coAwait()
|
||||||
|
return response?.toString() == "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存基本的对象,Integer、String、实体类等
|
||||||
|
*
|
||||||
|
* @param key 缓存的键值
|
||||||
|
* @param value 缓存的值
|
||||||
|
* @param timeout 时间
|
||||||
|
* @param timeUnit 时间颗粒度
|
||||||
|
*/
|
||||||
|
suspend fun <T> setObject(key: String, value: T, timeout: Long, timeUnit: TimeUnit): Boolean {
|
||||||
|
val expireSeconds = timeUnit.toSeconds(timeout)
|
||||||
|
val request = Request.cmd(Command.SET, key, value.toString(), "EX", expireSeconds.toString())
|
||||||
|
val response = redis.send(request).coAwait()
|
||||||
|
return response?.toString() == "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置有效时间
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param timeout 超时时间
|
||||||
|
* @return true=设置成功;false=设置失败
|
||||||
|
*/
|
||||||
|
suspend fun expire(key: String, timeout: Long): Boolean {
|
||||||
|
return expire(key, timeout, TimeUnit.SECONDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置有效时间
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param timeout 超时时间
|
||||||
|
* @param unit 时间单位
|
||||||
|
* @return true=设置成功;false=设置失败
|
||||||
|
*/
|
||||||
|
suspend fun expire(key: String, timeout: Long, unit: TimeUnit): Boolean {
|
||||||
|
val expireSeconds = unit.toSeconds(timeout)
|
||||||
|
val response = redis.send(Request.cmd(Command.EXPIRE, key, expireSeconds.toString())).coAwait()
|
||||||
|
return response?.toLong() == 1L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取有效时间
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @return 有效时间
|
||||||
|
*/
|
||||||
|
suspend fun getExpire(key: String): Long? {
|
||||||
|
val response = redis.send(Request.cmd(Command.TTL, key)).coAwait()
|
||||||
|
val ttl = response?.toLong()
|
||||||
|
return if (ttl == -1L || ttl == -2L) null else ttl
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断 key是否存在
|
||||||
|
*
|
||||||
|
* @param key 键
|
||||||
|
* @return true 存在 false不存在
|
||||||
|
*/
|
||||||
|
suspend fun hasKey(key: String): Boolean {
|
||||||
|
val response = redis.send(Request.cmd(Command.EXISTS, key)).coAwait()
|
||||||
|
return (response?.toLong() ?: 0) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得缓存的基本对象。
|
||||||
|
*
|
||||||
|
* @param key 缓存键值
|
||||||
|
* @return 缓存键值对应的数据
|
||||||
|
*/
|
||||||
|
suspend fun <T> getObject(key: String): T? {
|
||||||
|
val response = redis.send(Request.cmd(Command.GET, key)).coAwait()
|
||||||
|
return response?.toString() as? T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除单个对象
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
*/
|
||||||
|
suspend fun deleteObject(key: String): Boolean {
|
||||||
|
val response = redis.send(Request.cmd(Command.DEL, key)).coAwait()
|
||||||
|
return response?.toLong() == 1L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除集合对象
|
||||||
|
*
|
||||||
|
* @param collection 多个对象
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
suspend fun deleteObject(collection: Collection<String>): Boolean {
|
||||||
|
if (collection.isEmpty()) return false
|
||||||
|
val response = redis.send(Request.cmd(Command.DEL, *collection.toTypedArray())).coAwait()
|
||||||
|
return (response?.toLong() ?: 0) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存List数据
|
||||||
|
*
|
||||||
|
* @param key 缓存的键值
|
||||||
|
* @param dataList 待缓存的List数据
|
||||||
|
* @return 缓存的对象
|
||||||
|
*/
|
||||||
|
suspend fun <T> setList(key: String, dataList: List<T>): Long {
|
||||||
|
val args = mutableListOf<String>().apply {
|
||||||
|
add(key)
|
||||||
|
dataList.forEach { add(it.toString()) }
|
||||||
|
}
|
||||||
|
val response = redis.send(Request.cmd(Command.RPUSH, *args.toTypedArray())).coAwait()
|
||||||
|
return response?.toLong() ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得缓存的list对象
|
||||||
|
*
|
||||||
|
* @param key 缓存的键值
|
||||||
|
* @return 缓存键值对应的数据
|
||||||
|
*/
|
||||||
|
suspend fun <T> getList(key: String): List<T>? {
|
||||||
|
val response = redis.send(Request.cmd(Command.LRANGE, key, "0", "-1")).coAwait()
|
||||||
|
return response?.map { it.toString() as T }?.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存Set
|
||||||
|
*
|
||||||
|
* @param key 缓存键值
|
||||||
|
* @param dataSet 缓存的数据
|
||||||
|
* @return 缓存数据的对象
|
||||||
|
*/
|
||||||
|
suspend fun <T> setSet(key: String, dataSet: Set<T>): Long {
|
||||||
|
val args = mutableListOf<String>().apply {
|
||||||
|
add(key)
|
||||||
|
dataSet.forEach { add(it.toString()) }
|
||||||
|
}
|
||||||
|
val response = redis.send(Request.cmd(Command.SADD, *args.toTypedArray())).coAwait()
|
||||||
|
return response?.toLong() ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得缓存的set
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
suspend fun <T> getSet(key: String): Set<T>? {
|
||||||
|
val response = redis.send(Request.cmd(Command.SMEMBERS, key)).coAwait()
|
||||||
|
return response?.map { it.toString() as T }?.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向Set中添加一个或多个元素
|
||||||
|
*
|
||||||
|
* @param key 缓存键值
|
||||||
|
* @param values 要添加的元素
|
||||||
|
* @return 成功添加的元素数量
|
||||||
|
*/
|
||||||
|
suspend fun <T> sAdd(key: String, vararg values: T): Long {
|
||||||
|
val request = Request.cmd(Command.SADD)
|
||||||
|
.arg(key)
|
||||||
|
|
||||||
|
// 逐个添加参数
|
||||||
|
values.forEach {
|
||||||
|
request.arg(it.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
val response = redis.send(request).coAwait()
|
||||||
|
return response?.toLong() ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向Set中添加一个或多个元素,并设置过期时间
|
||||||
|
*
|
||||||
|
* @param key 缓存键值
|
||||||
|
* @param value 要添加的元素
|
||||||
|
* @param timeout 过期时间
|
||||||
|
* @param timeUnit 时间单位
|
||||||
|
* @return 是否成功添加元素
|
||||||
|
*/
|
||||||
|
suspend fun <T> sAdd(key: String, value: T, timeout: Long, timeUnit: TimeUnit): Boolean {
|
||||||
|
val added = sAdd(key, value) > 0
|
||||||
|
if (added) {
|
||||||
|
expire(key, timeout, timeUnit)
|
||||||
|
}
|
||||||
|
return added
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从Set中移除一个或多个元素
|
||||||
|
*
|
||||||
|
* @param key 缓存键值
|
||||||
|
* @param values 要移除的元素
|
||||||
|
* @return 成功移除的元素数量
|
||||||
|
*/
|
||||||
|
suspend fun <T> sRemove(key: String, vararg values: T): Long {
|
||||||
|
val args = mutableListOf<String>().apply {
|
||||||
|
add(key)
|
||||||
|
values.forEach { add(it.toString()) }
|
||||||
|
}
|
||||||
|
val response = redis.send(Request.cmd(Command.SREM, *args.toTypedArray())).coAwait()
|
||||||
|
return response?.toLong() ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Set中的所有成员
|
||||||
|
*
|
||||||
|
* @param key 缓存键值
|
||||||
|
* @return Set中的所有成员
|
||||||
|
*/
|
||||||
|
suspend fun sMembers(key: String): List<String> {
|
||||||
|
val response = redis.send(Request.cmd(Command.SMEMBERS, key)).coAwait()
|
||||||
|
return response?.map { it.toString() } ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存Map
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @param dataMap
|
||||||
|
*/
|
||||||
|
suspend fun <T> setMap(key: String, dataMap: Map<String, T>): Boolean {
|
||||||
|
if (dataMap.isEmpty()) return false
|
||||||
|
val args = mutableListOf<String>().apply {
|
||||||
|
add(key)
|
||||||
|
dataMap.forEach { (k, v) ->
|
||||||
|
add(k)
|
||||||
|
add(v.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val response = redis.send(Request.cmd(Command.HMSET, *args.toTypedArray())).coAwait()
|
||||||
|
return response?.toString() == "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得缓存的Map
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
suspend fun <T> getMap(key: String): Map<String, T>? {
|
||||||
|
val response = redis.send(Request.cmd(Command.HGETALL, key)).coAwait()
|
||||||
|
if (response == null || response.size() == 0) return null
|
||||||
|
|
||||||
|
val map = mutableMapOf<String, T>()
|
||||||
|
// for (i in 0 until response.size() step 2) {
|
||||||
|
// val k = response.get(i).toString()
|
||||||
|
// val v = response.get(i + 1).toString() as T
|
||||||
|
// map[k] = v
|
||||||
|
// }
|
||||||
|
for (item in response) {
|
||||||
|
val list = item.toMutableList()
|
||||||
|
val k = list[0].toString()
|
||||||
|
val v = list[1].toString() as T
|
||||||
|
map[k] = v
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 往Hash中存入数据
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param hKey Hash键
|
||||||
|
* @param value 值
|
||||||
|
*/
|
||||||
|
suspend fun <T> setMapValue(key: String, hKey: String, value: T): Boolean {
|
||||||
|
val response = redis.send(Request.cmd(Command.HSET, key, hKey, value.toString())).coAwait()
|
||||||
|
return response?.toLong() == 1L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Hash中的数据
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param hKey Hash键
|
||||||
|
* @return Hash中的对象
|
||||||
|
*/
|
||||||
|
suspend fun <T> getMapValue(key: String, hKey: String): T? {
|
||||||
|
val response = redis.send(Request.cmd(Command.HGET, key, hKey)).coAwait()
|
||||||
|
return response?.toString() as? T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取多个Hash中的数据
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param hKeys Hash键集合
|
||||||
|
* @return Hash对象集合
|
||||||
|
*/
|
||||||
|
suspend fun <T> getMultiMapValue(key: String, hKeys: Collection<String>): List<T?>? {
|
||||||
|
val args = mutableListOf<String>().apply {
|
||||||
|
add(key)
|
||||||
|
addAll(hKeys)
|
||||||
|
}
|
||||||
|
val response = redis.send(Request.cmd(Command.HMGET, *args.toTypedArray())).coAwait()
|
||||||
|
return response?.map { if (it == null) null else it.toString() as T }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除Hash中的某条数据
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param hKey Hash键
|
||||||
|
* @return 是否成功
|
||||||
|
*/
|
||||||
|
suspend fun deleteMapValue(key: String, hKey: String): Boolean {
|
||||||
|
val response = redis.send(Request.cmd(Command.HDEL, key, hKey)).coAwait()
|
||||||
|
return response?.toLong() == 1L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得缓存的基本对象列表
|
||||||
|
*
|
||||||
|
* @param pattern 字符串前缀
|
||||||
|
* @return 对象列表
|
||||||
|
*/
|
||||||
|
suspend fun keys(pattern: String): List<String>? {
|
||||||
|
val response = redis.send(Request.cmd(Command.KEYS, pattern)).coAwait()
|
||||||
|
return response?.map { it.toString() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自增操作
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @return 自增后的值
|
||||||
|
*/
|
||||||
|
suspend fun incr(key: String): Long {
|
||||||
|
val response = redis.send(Request.cmd(Command.INCR, key)).coAwait()
|
||||||
|
return response?.toLong() ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自增指定步长
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param increment 步长
|
||||||
|
* @return 自增后的值
|
||||||
|
*/
|
||||||
|
suspend fun incrBy(key: String, increment: Long): Long {
|
||||||
|
val response = redis.send(Request.cmd(Command.INCRBY, key, increment.toString())).coAwait()
|
||||||
|
return response?.toLong() ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自减操作
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @return 自减后的值
|
||||||
|
*/
|
||||||
|
suspend fun decr(key: String): Long {
|
||||||
|
val response = redis.send(Request.cmd(Command.DECR, key)).coAwait()
|
||||||
|
return response?.toLong() ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自减指定步长
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param decrement 步长
|
||||||
|
* @return 自减后的值
|
||||||
|
*/
|
||||||
|
suspend fun decrBy(key: String, decrement: Long): Long {
|
||||||
|
val response = redis.send(Request.cmd(Command.DECRBY, key, decrement.toString())).coAwait()
|
||||||
|
return response?.toLong() ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 浮点数自增
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param increment 增量
|
||||||
|
* @return 自增后的值
|
||||||
|
*/
|
||||||
|
suspend fun incrByFloat(key: String, increment: Double): Double {
|
||||||
|
val response = redis.send(Request.cmd(Command.INCRBYFLOAT, key, increment.toString())).coAwait()
|
||||||
|
return response?.toDouble() ?: 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置键值并返回旧值
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param value 新值
|
||||||
|
* @return 旧值
|
||||||
|
*/
|
||||||
|
suspend fun <T> getSet(key: String, value: T): T? {
|
||||||
|
val response = redis.send(Request.cmd(Command.GETSET, key, value.toString())).coAwait()
|
||||||
|
return response?.toString() as? T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置键值(仅当键不存在时)
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param value 值
|
||||||
|
* @return 是否设置成功
|
||||||
|
*/
|
||||||
|
suspend fun <T> setIfAbsent(key: String, value: T): Boolean {
|
||||||
|
val response = redis.send(Request.cmd(Command.SETNX, key, value.toString())).coAwait()
|
||||||
|
return response?.toLong() == 1L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字符串长度
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @return 字符串长度
|
||||||
|
*/
|
||||||
|
suspend fun strlen(key: String): Long {
|
||||||
|
val response = redis.send(Request.cmd(Command.STRLEN, key)).coAwait()
|
||||||
|
return response?.toLong() ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 追加字符串
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param value 追加的值
|
||||||
|
* @return 追加后的字符串长度
|
||||||
|
*/
|
||||||
|
suspend fun append(key: String, value: String): Long {
|
||||||
|
val response = redis.send(Request.cmd(Command.APPEND, key, value)).coAwait()
|
||||||
|
return response?.toLong() ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取子字符串
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param start 开始位置
|
||||||
|
* @param end 结束位置
|
||||||
|
* @return 子字符串
|
||||||
|
*/
|
||||||
|
suspend fun getRange(key: String, start: Long, end: Long): String? {
|
||||||
|
val response = redis.send(Request.cmd(Command.GETRANGE, key, start.toString(), end.toString())).coAwait()
|
||||||
|
return response?.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置子字符串
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param offset 偏移量
|
||||||
|
* @param value 值
|
||||||
|
* @return 修改后的字符串长度
|
||||||
|
*/
|
||||||
|
suspend fun setRange(key: String, offset: Long, value: String): Long {
|
||||||
|
val response = redis.send(Request.cmd(Command.SETRANGE, key, offset.toString(), value)).coAwait()
|
||||||
|
return response?.toLong() ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置多个键值对
|
||||||
|
*
|
||||||
|
* @param keyValues 键值对(key1, value1, key2, value2...)
|
||||||
|
* @return 是否成功
|
||||||
|
*/
|
||||||
|
suspend fun mset(vararg keyValues: String): Boolean {
|
||||||
|
if (keyValues.size % 2 != 0) throw IllegalArgumentException("参数数量必须为偶数")
|
||||||
|
val response = redis.send(Request.cmd(Command.MSET, *keyValues)).coAwait()
|
||||||
|
return response?.toString() == "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取多个键的值
|
||||||
|
*
|
||||||
|
* @param keys 键集合
|
||||||
|
* @return 值列表
|
||||||
|
*/
|
||||||
|
suspend fun mget(vararg keys: String): List<String?> {
|
||||||
|
val response = redis.send(Request.cmd(Command.MGET, *keys)).coAwait()
|
||||||
|
return response?.map { it?.toString() } ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向有序集合添加一个或多个成员,或者更新已存在成员的分数
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param score 分数
|
||||||
|
* @param member 成员
|
||||||
|
* @return 成功添加的新成员的数量,不包括那些被更新的、已经存在的成员
|
||||||
|
*/
|
||||||
|
suspend fun zadd(key: String, score: Double, member: String): Long {
|
||||||
|
val request = Request.cmd(Command.ZADD)
|
||||||
|
.arg(key)
|
||||||
|
.arg(score.toString())
|
||||||
|
.arg(member)
|
||||||
|
|
||||||
|
val response = redis.send(request).coAwait()
|
||||||
|
return response?.toLong() ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为有序集合的成员增加分数
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param increment 增量分数
|
||||||
|
* @param member 成员
|
||||||
|
* @return 增加后的分数
|
||||||
|
*/
|
||||||
|
suspend fun zincrby(key: String, increment: Double, member: String): Double {
|
||||||
|
val request = Request.cmd(Command.ZINCRBY)
|
||||||
|
.arg(key)
|
||||||
|
.arg(increment.toString())
|
||||||
|
.arg(member)
|
||||||
|
|
||||||
|
val response = redis.send(request).coAwait()
|
||||||
|
return response?.toDouble() ?: 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取有序集合中指定成员的分数
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param member 成员
|
||||||
|
* @return 分数,如果成员不存在或键不存在则返回null
|
||||||
|
*/
|
||||||
|
suspend fun zscore(key: String, member: String): Double? {
|
||||||
|
val request = Request.cmd(Command.ZSCORE)
|
||||||
|
.arg(key)
|
||||||
|
.arg(member)
|
||||||
|
|
||||||
|
val response = redis.send(request).coAwait()
|
||||||
|
return response?.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取有序集合中指定区间的成员,按分数从高到低排序
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @param start 开始位置
|
||||||
|
* @param stop 结束位置
|
||||||
|
* @param withScores 是否返回分数
|
||||||
|
* @return 指定区间的成员列表,如果withScores为true,则返回成员和分数的交替列表
|
||||||
|
*/
|
||||||
|
suspend fun zrevrange(key: String, start: Long, stop: Long, withScores: Boolean = false): List<String> {
|
||||||
|
// 使用可变参数列表而不是数组,避免类型转换问题
|
||||||
|
val request = if (withScores) {
|
||||||
|
Request.cmd(Command.ZREVRANGE)
|
||||||
|
.arg(key)
|
||||||
|
.arg(start.toString())
|
||||||
|
.arg(stop.toString())
|
||||||
|
.arg("WITHSCORES")
|
||||||
|
} else {
|
||||||
|
Request.cmd(Command.ZREVRANGE)
|
||||||
|
.arg(key)
|
||||||
|
.arg(start.toString())
|
||||||
|
.arg(stop.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
val response = redis.send(request).coAwait()
|
||||||
|
return response?.map { it.toString() } ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取有序集合中所有成员的分数总和
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @return 所有成员的分数总和
|
||||||
|
*/
|
||||||
|
suspend fun zsumScores(key: String): Double {
|
||||||
|
// 首先检查键是否存在
|
||||||
|
if (!hasKey(key)) return 0.0
|
||||||
|
|
||||||
|
val request = Request.cmd(Command.ZRANGE)
|
||||||
|
.arg(key)
|
||||||
|
.arg("0")
|
||||||
|
.arg("-1")
|
||||||
|
.arg("WITHSCORES")
|
||||||
|
|
||||||
|
val response = redis.send(request).coAwait()
|
||||||
|
if (response == null || response.size() == 0) return 0.0
|
||||||
|
|
||||||
|
var sum = 0.0
|
||||||
|
for (item in response) {
|
||||||
|
sum += item.toMutableList()[1]?.toString()?.toDoubleOrNull() ?: 0.0
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用 SCAN 命令获取匹配指定模式的所有键
|
||||||
|
*
|
||||||
|
* @param pattern 匹配模式(例如:content:clicks:*)
|
||||||
|
* @return 匹配模式的键列表
|
||||||
|
*/
|
||||||
|
suspend fun scan(pattern: String): List<String> {
|
||||||
|
val keys = mutableListOf<String>()
|
||||||
|
var cursor = "0"
|
||||||
|
|
||||||
|
do {
|
||||||
|
val request = Request.cmd(Command.SCAN)
|
||||||
|
.arg(cursor)
|
||||||
|
.arg("MATCH")
|
||||||
|
.arg(pattern)
|
||||||
|
.arg("COUNT")
|
||||||
|
.arg("100") // 每次迭代返回的键数量
|
||||||
|
|
||||||
|
val response = redis.send(request).coAwait()
|
||||||
|
if (response != null && response.size() >= 2) {
|
||||||
|
cursor = response.get(0).toString()
|
||||||
|
|
||||||
|
// 从索引1处获取键列表
|
||||||
|
val scanKeys = response.get(1)
|
||||||
|
for (i in 0 until scanKeys.size()) {
|
||||||
|
keys.add(scanKeys.get(i).toString())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} while (cursor != "0")
|
||||||
|
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算多个有序集合的并集,并将结果存储在新的键中
|
||||||
|
*
|
||||||
|
* @param destKey 目标键,存储计算结果
|
||||||
|
* @param keys 要计算并集的有序集合键列表
|
||||||
|
* @param weights 各有序集的权重(可选)
|
||||||
|
* @param aggregate 结果集的聚合方式(可选,默认为 SUM)
|
||||||
|
* @return 目标键中的元素数量
|
||||||
|
*/
|
||||||
|
suspend fun zunionstore(
|
||||||
|
destKey: String,
|
||||||
|
keys: Array<String>,
|
||||||
|
weights: DoubleArray? = null,
|
||||||
|
aggregate: String = "SUM"
|
||||||
|
): Long {
|
||||||
|
if (keys.isEmpty()) return 0
|
||||||
|
|
||||||
|
val request = Request.cmd(Command.ZUNIONSTORE)
|
||||||
|
.arg(destKey)
|
||||||
|
.arg(keys.size.toString())
|
||||||
|
|
||||||
|
// 添加源集合键
|
||||||
|
keys.forEach { request.arg(it) }
|
||||||
|
|
||||||
|
// 添加权重(如果指定)
|
||||||
|
if (weights != null && weights.size == keys.size) {
|
||||||
|
request.arg("WEIGHTS")
|
||||||
|
weights.forEach { request.arg(it.toString()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加聚合方式
|
||||||
|
if (aggregate in listOf("SUM", "MIN", "MAX")) {
|
||||||
|
request.arg("AGGREGATE")
|
||||||
|
request.arg(aggregate)
|
||||||
|
}
|
||||||
|
|
||||||
|
val response = redis.send(request).coAwait()
|
||||||
|
return response?.toLong() ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算多个有序集合的并集,使用相同的权重
|
||||||
|
*
|
||||||
|
* @param destKey 目标键,存储计算结果
|
||||||
|
* @param keys 要计算并集的有序集合键列表
|
||||||
|
* @return 目标键中的元素数量
|
||||||
|
*/
|
||||||
|
suspend fun zunionstoreWithEqualWeights(destKey: String, keys: Array<String>): Long {
|
||||||
|
val weights = DoubleArray(keys.size) { 1.0 }
|
||||||
|
return zunionstore(destKey, keys, weights)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算多个有序集合的并集,对结果取最大值
|
||||||
|
*
|
||||||
|
* @param destKey 目标键,存储计算结果
|
||||||
|
* @param keys 要计算并集的有序集合键列表
|
||||||
|
* @return 目标键中的元素数量
|
||||||
|
*/
|
||||||
|
suspend fun zunionstoreMax(destKey: String, keys: Array<String>): Long {
|
||||||
|
return zunionstore(destKey, keys, null, "MAX")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算两个或多个有序集合的差集,并将结果存储在新的键中
|
||||||
|
* 此方法仅适用于Redis 6.2+版本
|
||||||
|
*
|
||||||
|
* @param destKey 目标键,存储计算结果
|
||||||
|
* @param keys 要计算差集的有序集合键数组,第一个集合是基准
|
||||||
|
* @return 目标键中的元素数量
|
||||||
|
*/
|
||||||
|
suspend fun zdiffstore(destKey: String, keys: Array<String>): Long {
|
||||||
|
if (keys.isEmpty() || keys.size < 2) return 0L
|
||||||
|
|
||||||
|
val request = Request.cmd(Command.ZDIFFSTORE)
|
||||||
|
.arg(destKey)
|
||||||
|
.arg(keys.size.toString())
|
||||||
|
|
||||||
|
// 添加源集合键
|
||||||
|
keys.forEach { request.arg(it) }
|
||||||
|
|
||||||
|
val response = redis.send(request).coAwait()
|
||||||
|
return response?.toLong() ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取有序集合的大小(成员数量)
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @return 有序集合的大小
|
||||||
|
*/
|
||||||
|
suspend fun zcard(key: String): Long {
|
||||||
|
val request = Request.cmd(Command.ZCARD)
|
||||||
|
.arg(key)
|
||||||
|
|
||||||
|
val response = redis.send(request).coAwait()
|
||||||
|
return response?.toLong() ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取有序集合中所有成员
|
||||||
|
*
|
||||||
|
* @param key Redis键
|
||||||
|
* @return 有序集合的所有成员
|
||||||
|
*/
|
||||||
|
suspend fun zall(key: String): List<String> {
|
||||||
|
return zrevrange(key, 0, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算两个有序集合的差集,并返回结果
|
||||||
|
* 此方法仅适用于Redis 6.2+版本
|
||||||
|
*
|
||||||
|
* @param keys 要计算差集的有序集合键数组,第一个集合是基准
|
||||||
|
* @return 差集结果
|
||||||
|
*/
|
||||||
|
suspend fun zdiff(vararg keys: String): List<String> {
|
||||||
|
if (keys.isEmpty() || keys.size < 2) return emptyList()
|
||||||
|
|
||||||
|
val request = Request.cmd(Command.ZDIFF)
|
||||||
|
|
||||||
|
// 添加集合数量
|
||||||
|
request.arg(keys.size.toString())
|
||||||
|
|
||||||
|
// 添加源集合键
|
||||||
|
keys.forEach { request.arg(it) }
|
||||||
|
|
||||||
|
val response = redis.send(request).coAwait()
|
||||||
|
return response?.map { it.toString() } ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行 Lua 脚本
|
||||||
|
*
|
||||||
|
* @param script Lua 脚本
|
||||||
|
* @param keys 脚本中使用的 KEYS 参数
|
||||||
|
* @param args 脚本中使用的 ARGV 参数
|
||||||
|
* @return 脚本执行结果
|
||||||
|
*/
|
||||||
|
suspend fun eval(script: String, keys: List<String> = emptyList(), vararg args: String): List<String>? {
|
||||||
|
val request = Request.cmd(Command.EVAL)
|
||||||
|
.arg(script)
|
||||||
|
.arg(keys.size.toString())
|
||||||
|
|
||||||
|
// 添加 KEYS 参数
|
||||||
|
keys.forEach { request.arg(it) }
|
||||||
|
|
||||||
|
// 添加 ARGV 参数
|
||||||
|
args.forEach { request.arg(it) }
|
||||||
|
|
||||||
|
val response = redis.send(request).coAwait()
|
||||||
|
return response?.map { it.toString() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行 Lua 脚本并返回整数结果
|
||||||
|
*
|
||||||
|
* @param script Lua 脚本
|
||||||
|
* @param keys 脚本中使用的 KEYS 参数
|
||||||
|
* @param args 脚本中使用的 ARGV 参数
|
||||||
|
* @return 脚本执行结果(整数)
|
||||||
|
*/
|
||||||
|
suspend fun evalToInt(script: String, keys: List<String> = emptyList(), vararg args: String): Int? {
|
||||||
|
val result = eval(script, keys, *args)
|
||||||
|
return result?.firstOrNull()?.toIntOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行 Lua 脚本并返回长整数结果
|
||||||
|
*
|
||||||
|
* @param script Lua 脚本
|
||||||
|
* @param keys 脚本中使用的 KEYS 参数
|
||||||
|
* @param args 脚本中使用的 ARGV 参数
|
||||||
|
* @return 脚本执行结果(长整数)
|
||||||
|
*/
|
||||||
|
suspend fun evalToLong(script: String, keys: List<String> = emptyList(), vararg args: String): Long? {
|
||||||
|
val result = eval(script, keys, *args)
|
||||||
|
return result?.firstOrNull()?.toLongOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行 Lua 脚本并返回布尔结果
|
||||||
|
*
|
||||||
|
* @param script Lua 脚本
|
||||||
|
* @param keys 脚本中使用的 KEYS 参数
|
||||||
|
* @param args 脚本中使用的 ARGV 参数
|
||||||
|
* @return 脚本执行结果(布尔值)
|
||||||
|
*/
|
||||||
|
suspend fun evalToBoolean(script: String, keys: List<String> = emptyList(), vararg args: String): Boolean {
|
||||||
|
val result = eval(script, keys, *args)
|
||||||
|
return result?.firstOrNull()?.toIntOrNull() == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,16 +1,15 @@
|
|||||||
package app.port.aipfox
|
package app.utils.openapi
|
||||||
|
|
||||||
import app.util.openapi.OpenApiSpecGenerator
|
|
||||||
import com.google.inject.Inject
|
import com.google.inject.Inject
|
||||||
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import io.vertx.core.Vertx
|
import io.vertx.core.Vertx
|
||||||
import io.vertx.core.http.HttpMethod
|
import io.vertx.core.http.HttpMethod
|
||||||
import io.vertx.core.json.JsonObject
|
import io.vertx.core.json.JsonObject
|
||||||
import io.vertx.ext.web.client.WebClient
|
import io.vertx.ext.web.client.WebClient
|
||||||
import io.vertx.ext.web.client.WebClientOptions
|
import io.vertx.ext.web.client.WebClientOptions
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
|
||||||
import org.aikrai.vertx.config.Config
|
import org.aikrai.vertx.config.Config
|
||||||
|
|
||||||
class ApifoxClient @Inject constructor(
|
class ApifoxUtil @Inject constructor(
|
||||||
private val vertx: Vertx,
|
private val vertx: Vertx,
|
||||||
) {
|
) {
|
||||||
private val logger = KotlinLogging.logger { }
|
private val logger = KotlinLogging.logger { }
|
||||||
@ -47,4 +46,4 @@ class ApifoxClient @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package app.util.openapi
|
package app.utils.openapi
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil
|
import cn.hutool.core.util.StrUtil
|
||||||
import io.swagger.v3.core.util.Json
|
import io.swagger.v3.core.util.Json
|
||||||
@ -2,7 +2,7 @@ package app.verticle
|
|||||||
|
|
||||||
import app.config.handler.JwtAuthHandler
|
import app.config.handler.JwtAuthHandler
|
||||||
import app.config.handler.ResponseHandler
|
import app.config.handler.ResponseHandler
|
||||||
import app.port.aipfox.ApifoxClient
|
import app.utils.openapi.ApifoxUtil
|
||||||
import com.google.inject.Inject
|
import com.google.inject.Inject
|
||||||
import com.google.inject.Injector
|
import com.google.inject.Injector
|
||||||
import io.vertx.core.http.HttpMethod
|
import io.vertx.core.http.HttpMethod
|
||||||
@ -27,7 +27,7 @@ class WebVerticle @Inject constructor(
|
|||||||
private val requestLogHandler: RequestLogHandler,
|
private val requestLogHandler: RequestLogHandler,
|
||||||
private val responseHandler: ResponseHandler,
|
private val responseHandler: ResponseHandler,
|
||||||
private val globalErrorHandler: GlobalErrorHandler,
|
private val globalErrorHandler: GlobalErrorHandler,
|
||||||
private val apiFoxClient: ApifoxClient,
|
private val apiFoxUtil: ApifoxUtil,
|
||||||
) : CoroutineVerticle() {
|
) : CoroutineVerticle() {
|
||||||
private val logger = KotlinLogging.logger { }
|
private val logger = KotlinLogging.logger { }
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ class WebVerticle @Inject constructor(
|
|||||||
.listen(serverConfig.port)
|
.listen(serverConfig.port)
|
||||||
.coAwait()
|
.coAwait()
|
||||||
// 生成ApiFox接口
|
// 生成ApiFox接口
|
||||||
apiFoxClient.importOpenapi()
|
apiFoxUtil.importOpenapi()
|
||||||
logger.info { "HTTP服务启动 - http://127.0.0.1:${server.actualPort()}${serverConfig.context}" }
|
logger.info { "HTTP服务启动 - http://127.0.0.1:${server.actualPort()}${serverConfig.context}" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,12 +16,15 @@ CREATE TABLE sys_menu (
|
|||||||
|
|
||||||
CREATE TABLE sys_user (
|
CREATE TABLE sys_user (
|
||||||
user_id BIGINT DEFAULT 0 NOT NULL,
|
user_id BIGINT DEFAULT 0 NOT NULL,
|
||||||
user_name VARCHAR(255) DEFAULT '',
|
dept_id BIGINT DEFAULT 0,
|
||||||
user_type VARCHAR(255) DEFAULT '',
|
user_name VARCHAR(30) DEFAULT '',
|
||||||
email VARCHAR(255) DEFAULT '',
|
nick_name VARCHAR(30) DEFAULT '',
|
||||||
phone VARCHAR(255) DEFAULT '',
|
user_type VARCHAR(2) DEFAULT '',
|
||||||
avatar VARCHAR(255) DEFAULT '',
|
email VARCHAR(50) DEFAULT '',
|
||||||
password VARCHAR(255) DEFAULT '',
|
phonenumber VARCHAR(11) DEFAULT '',
|
||||||
|
sex INTEGER DEFAULT 2,
|
||||||
|
avatar VARCHAR(100) DEFAULT '',
|
||||||
|
password VARCHAR(100) DEFAULT '',
|
||||||
status INTEGER DEFAULT 0,
|
status INTEGER DEFAULT 0,
|
||||||
del_flag CHAR(1) DEFAULT 0,
|
del_flag CHAR(1) DEFAULT 0,
|
||||||
login_ip VARCHAR(255) DEFAULT '',
|
login_ip VARCHAR(255) DEFAULT '',
|
||||||
@ -31,9 +34,17 @@ CREATE TABLE sys_user (
|
|||||||
|
|
||||||
-- 添加字段注释
|
-- 添加字段注释
|
||||||
COMMENT ON COLUMN sys_user.user_id IS '用户ID';
|
COMMENT ON COLUMN sys_user.user_id IS '用户ID';
|
||||||
|
COMMENT ON COLUMN sys_user.dept_id IS '部门ID';
|
||||||
|
COMMENT ON COLUMN sys_user.user_name IS '用户账号';
|
||||||
CREATE UNIQUE INDEX idx_phone ON sys_user (phone);
|
COMMENT ON COLUMN sys_user.nick_name IS '用户昵称';
|
||||||
|
COMMENT ON COLUMN sys_user.user_type IS '用户类型';
|
||||||
|
COMMENT ON COLUMN sys_user.email IS '用户邮箱';
|
||||||
|
COMMENT ON COLUMN sys_user.phonenumber IS '手机号码';
|
||||||
|
COMMENT ON COLUMN sys_user.sex IS '用户性别(0男 1女 2未知)';
|
||||||
|
COMMENT ON COLUMN sys_user.avatar IS '头像地址';
|
||||||
|
COMMENT ON COLUMN sys_user.password IS '密码';
|
||||||
|
COMMENT ON COLUMN sys_user.status IS '帐号状态(0正常 1停用)';
|
||||||
|
COMMENT ON COLUMN sys_user.del_flag IS '删除标志(0代表存在 2代表删除)';
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE sys_role (
|
CREATE TABLE sys_role (
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<migration generated="2025-03-21T15:27:45.6470429">
|
<migration generated="2025-05-12T11:18:05.3077594">
|
||||||
<changeSet type="apply">
|
<changeSet type="apply">
|
||||||
<createTable name="sys_menu" pkName="pk_sys_menu">
|
<createTable name="sys_menu" pkName="pk_sys_menu">
|
||||||
<column name="menu_id" notnull="true" primaryKey="true" type="BIGINT"/>
|
<column name="menu_id" notnull="true" primaryKey="true" type="BIGINT"/>
|
||||||
@ -16,12 +16,15 @@
|
|||||||
</createTable>
|
</createTable>
|
||||||
<createTable name="sys_user" pkName="pk_sys_user">
|
<createTable name="sys_user" pkName="pk_sys_user">
|
||||||
<column name="user_id" notnull="true" primaryKey="true" type="BIGINT"/>
|
<column name="user_id" notnull="true" primaryKey="true" type="BIGINT"/>
|
||||||
<column name="user_name" type="VARCHAR(255)"/>
|
<column name="dept_id" type="BIGINT"/>
|
||||||
<column name="user_type" type="VARCHAR(255)"/>
|
<column name="user_name" type="VARCHAR(30)"/>
|
||||||
<column name="email" type="VARCHAR(255)"/>
|
<column name="nick_name" type="VARCHAR(30)"/>
|
||||||
<column name="phone" type="VARCHAR(255)"/>
|
<column name="user_type" type="VARCHAR(2)"/>
|
||||||
<column name="avatar" type="VARCHAR(255)"/>
|
<column name="email" type="VARCHAR(50)"/>
|
||||||
<column name="password" type="VARCHAR(255)"/>
|
<column name="phonenumber" type="VARCHAR(11)"/>
|
||||||
|
<column defaultValue="2" name="sex" type="INTEGER"/>
|
||||||
|
<column name="avatar" type="VARCHAR(100)"/>
|
||||||
|
<column name="password" type="VARCHAR(100)"/>
|
||||||
<column defaultValue="0" name="status" type="INTEGER"/>
|
<column defaultValue="0" name="status" type="INTEGER"/>
|
||||||
<column name="del_flag" type="CHAR(1)"/>
|
<column name="del_flag" type="CHAR(1)"/>
|
||||||
<column name="login_ip" type="VARCHAR(255)"/>
|
<column name="login_ip" type="VARCHAR(255)"/>
|
||||||
|
|||||||
@ -19,7 +19,7 @@ data class RedisConfig(
|
|||||||
val host: String,
|
val host: String,
|
||||||
val port: Int,
|
val port: Int,
|
||||||
val db: Int,
|
val db: Int,
|
||||||
val pass: String?,
|
val password: String?,
|
||||||
val poolSize: Int = 8,
|
val poolSize: Int = 8,
|
||||||
val maxPoolWaiting: Int = 32
|
val maxPoolWaiting: Int = 32
|
||||||
)
|
)
|
||||||
|
|||||||
@ -31,7 +31,7 @@ class FrameworkConfigModule : AbstractModule() {
|
|||||||
host = Config.getString("redis.host", "localhost"),
|
host = Config.getString("redis.host", "localhost"),
|
||||||
port = Config.getInt("redis.port", 6379),
|
port = Config.getInt("redis.port", 6379),
|
||||||
db = Config.getInt("redis.database", 0),
|
db = Config.getInt("redis.database", 0),
|
||||||
pass = Config.getStringOrNull("redis.password"),
|
password = Config.getStringOrNull("redis.password"),
|
||||||
poolSize = Config.getInt("redis.maxPoolSize", 8),
|
poolSize = Config.getInt("redis.maxPoolSize", 8),
|
||||||
maxPoolWaiting = Config.getInt("redis.maxPoolWaiting", 32)
|
maxPoolWaiting = Config.getInt("redis.maxPoolWaiting", 32)
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user