refactor(vertx-demo):重构项目目录结构

This commit is contained in:
AiKrai 2025-05-12 15:41:14 +08:00
parent 047717ad71
commit 95f76404c2
34 changed files with 1029 additions and 125 deletions

View File

@ -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

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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
) )

View File

@ -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 = ""
} }

View File

@ -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

View File

@ -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 }
} }
} }
} }

View 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 }
}
}
}

View File

@ -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))
}
}

View File

@ -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
} }

View File

@ -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>
} }

View File

@ -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>

View File

@ -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)
} }
} }

View File

@ -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()
} }
} }

View File

@ -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

View File

@ -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()
} }
} }
} }

View File

@ -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

View File

@ -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))
} }

View File

@ -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

View 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) {
/**
* 缓存基本的对象IntegerString实体类等
*
* @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"
}
/**
* 缓存基本的对象IntegerString实体类等
*
* @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
}
}

View File

@ -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(
} }
} }
} }
} }

View File

@ -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

View File

@ -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}" }
} }

View File

@ -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 (

View File

@ -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)"/>

View File

@ -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
) )

View File

@ -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)
) )