Skip to content

Commit

Permalink
Feature: 执行SQL语句
Browse files Browse the repository at this point in the history
  • Loading branch information
YuS1aN committed Mar 8, 2024
1 parent 3435a14 commit 6c02201
Show file tree
Hide file tree
Showing 22 changed files with 276 additions and 28 deletions.
39 changes: 39 additions & 0 deletions app/src/debug/java/me/kbai/zhenxunui/extends/CoroutineExtends.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package me.kbai.zhenxunui.extends

import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.launch
import me.kbai.zhenxunui.repository.Resource

/**
* @author sean on 2022/4/15
*/

fun LifecycleOwner.launchRepeatOnLifeCycle(
state: Lifecycle.State,
block: suspend CoroutineScope.() -> Unit
) = lifecycleScope.launch {
repeatOnLifecycle(state, block)
}

val Fragment.viewLifecycleScope: CoroutineScope
get() = viewLifecycleOwner.lifecycleScope

inline fun <reified T> Flow<T>.launchAndCollectIn(
owner: LifecycleOwner,
activeState: Lifecycle.State = Lifecycle.State.STARTED,
collector: FlowCollector<T>
) = owner.lifecycleScope.launch {
logI(T::class.java.name, "launchAndCollectIn")
assert(T::class.java != Resource::class.java) { "Resource 类型要用 apiCollect 实现 token 过期重新登录!" }

owner.repeatOnLifecycle(activeState) {
collect(collector)
}
}
3 changes: 3 additions & 0 deletions app/src/main/java/me/kbai/zhenxunui/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.ToNumberPolicy
import com.google.gson.ToNumberStrategy
import me.kbai.zhenxunui.api.ErrorHandleAdapterFactory
import me.kbai.zhenxunui.model.BotBaseInfo

Expand All @@ -29,6 +31,7 @@ object Constants {

val gson: Gson = GsonBuilder()
.registerTypeAdapterFactory(ErrorHandleAdapterFactory())
.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
.create()

var currentBot: BotBaseInfo? = null
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/me/kbai/zhenxunui/api/ApiResponse.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ data class ApiResponse<T>(
val success: Boolean,
val code: Int,
val info: String,
val warning: String?,
val data: T?
)
2 changes: 1 addition & 1 deletion app/src/main/java/me/kbai/zhenxunui/api/ApiService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -118,5 +118,5 @@ interface ApiService {
suspend fun getSqlLog(): ApiResponse<List<SqlLog>>

@POST("database/exec_sql")
suspend fun executeSql(@Body sql: ExecuteSql): RawApiResponse
suspend fun executeSql(@Body sql: ExecuteSql): ApiResponse<List<LinkedHashMap<String, *>>>
}
20 changes: 20 additions & 0 deletions app/src/main/java/me/kbai/zhenxunui/extends/ApiExtends.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ package me.kbai.zhenxunui.extends

import android.app.Application
import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.launch
import me.kbai.zhenxunui.repository.Resource

/**
Expand Down Expand Up @@ -35,6 +41,20 @@ suspend fun <T : Resource<*>> Flow<T>.apiCollect(
action.invoke(value)
}

/**
* 要记得在 active state 恢复 button 状态
*/
fun <T : Resource<*>> Flow<T>.launchAndApiCollectIn(
owner: LifecycleOwner,
button: View? = null,
activeState: Lifecycle.State = Lifecycle.State.STARTED,
action: suspend (value: T) -> Unit
) = owner.lifecycleScope.launch {
owner.repeatOnLifecycle(activeState) {
apiCollect(button = button, action = action)
}
}

//fun <T : Resource<*>> Flow<T>.checkToken(
// application: Application
//): Flow<T> = filter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import me.kbai.zhenxunui.api.RawApiResponse
import me.kbai.zhenxunui.api.TypedWebSocketHolder
import me.kbai.zhenxunui.api.WebSocketHolder
import me.kbai.zhenxunui.model.ChatMessage
import me.kbai.zhenxunui.model.ExecuteSql
import me.kbai.zhenxunui.model.HandleRequest
import me.kbai.zhenxunui.model.PluginDetail
import me.kbai.zhenxunui.model.PluginInfo
Expand Down Expand Up @@ -118,6 +119,8 @@ object ApiRepository {

fun getTableColumn(table: String) = networkFlow { BotApi.service.getTableColumn(table) }

fun executeSql(sql: ExecuteSql) = networkFlow { BotApi.service.executeSql(sql) }

private fun <T> networkFlow(
tempData: T? = null,
f: suspend () -> ApiResponse<T>
Expand Down Expand Up @@ -154,9 +157,9 @@ object ApiRepository {
return@flow
}
if (resp.success) {
emit(Resource.success(resp.data, resp.info, resp.code))
emit(Resource.success(resp.data, resp.warning ?: resp.info, resp.code))
} else {
emit(Resource.error(null, resp.info, resp.code))
emit(Resource.error(null, resp.warning ?: resp.info, resp.code))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import me.kbai.zhenxunui.Constants
import me.kbai.zhenxunui.R
import me.kbai.zhenxunui.base.BaseFragment
import me.kbai.zhenxunui.databinding.FragmentConsoleBinding
Expand Down
21 changes: 18 additions & 3 deletions app/src/main/java/me/kbai/zhenxunui/ui/db/DbManageFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import android.view.ViewGroup
import androidx.fragment.app.viewModels
import me.kbai.zhenxunui.base.BaseFragment
import me.kbai.zhenxunui.databinding.FragmentDbManageBinding
import me.kbai.zhenxunui.extends.launchAndApiCollectIn
import me.kbai.zhenxunui.extends.setOnDebounceClickListener
import me.kbai.zhenxunui.extends.viewLifecycleScope
import me.kbai.zhenxunui.repository.Resource
import me.kbai.zhenxunui.tool.GlobalToast
import me.kbai.zhenxunui.viewmodel.DbManageViewModel

/**
Expand All @@ -22,16 +25,28 @@ class DbManageFragment : BaseFragment<FragmentDbManageBinding>() {
): FragmentDbManageBinding = FragmentDbManageBinding.inflate(inflater, container, false)

override fun initView() = viewBinding.run {

srlRefresh.setOnRefreshListener {
mViewModel.requestTableList()
}

btnSql.setOnDebounceClickListener {
//TODO
btnSql.setOnDebounceClickListener { btn ->
mViewModel.executeSql(etSql.text?.toString().orEmpty())
.launchAndApiCollectIn(viewLifecycleOwner, btn) {
if (it.status == Resource.Status.LOADING) return@launchAndApiCollectIn
GlobalToast.showToast(it.message)

if (it.data != null) {
SqlResultDialogFragment().show(childFragmentManager)
}
}
}
}

override fun onStart() {
super.onStart()
viewBinding.btnSql.isEnabled = true
}

override fun initData() {
mViewModel.tables.observe(viewLifecycleOwner) {
viewBinding.srlRefresh.isRefreshing = false
Expand Down
9 changes: 4 additions & 5 deletions app/src/main/java/me/kbai/zhenxunui/ui/db/DbTableAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ package me.kbai.zhenxunui.ui.db
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.core.view.size
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import me.kbai.zhenxunui.databinding.ItemDbManageTableBinding
import me.kbai.zhenxunui.extends.setOnDebounceClickListener
Expand Down Expand Up @@ -43,6 +41,7 @@ class DbTableAdapter(
viewModel.getColumns(item.name)
.collect {
if (!holder.recycled) {
delay(elFields.remainPlayTime)
pbWaiting.isVisible = false
rvFields.adapter = TableFieldAdapter(it)
rvFields.post { elFields.expand() }
Expand All @@ -51,15 +50,15 @@ class DbTableAdapter(
}
}

rvFields.adapter = null
elFields.collapse(false)
pbWaiting.isVisible = true
holder.recycled = false
}
}

override fun onViewRecycled(holder: ItemViewHolder) {
override fun onViewRecycled(holder: ItemViewHolder) = holder.binding.run {
holder.recycled = true
rvFields.adapter = null
}

class ItemViewHolder(val binding: ItemDbManageTableBinding) :
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package me.kbai.zhenxunui.ui.db

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import me.kbai.zhenxunui.base.BaseDialogFragment
import me.kbai.zhenxunui.databinding.DialogSqlResultBinding
import me.kbai.zhenxunui.viewmodel.DbManageViewModel

class SqlResultDialogFragment : BaseDialogFragment() {

private lateinit var mBinding: DialogSqlResultBinding

private val mViewModel by viewModels<DbManageViewModel>({ requireParentFragment() })

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
) = DialogSqlResultBinding.inflate(inflater, container, false).also { mBinding = it }.root

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
mViewModel.executeSqlResult.observe(viewLifecycleOwner) {
mBinding.rvRows.adapter = SqlResultRowAdapter(it)
}
}
}
24 changes: 24 additions & 0 deletions app/src/main/java/me/kbai/zhenxunui/ui/db/SqlResultRowAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package me.kbai.zhenxunui.ui.db

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import me.kbai.zhenxunui.databinding.ItemSqlResultRowBinding

class SqlResultRowAdapter(
val data: List<Map<String, *>>
) : RecyclerView.Adapter<SqlResultRowAdapter.ItemViewHolder>() {

class ItemViewHolder(val binding: ItemSqlResultRowBinding) :
RecyclerView.ViewHolder(binding.root)

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
ItemSqlResultRowBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)

override fun getItemCount() = data.size

override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
holder.binding.rvValues.adapter = SqlResultValueAdapter(data[position])
}
}
33 changes: 33 additions & 0 deletions app/src/main/java/me/kbai/zhenxunui/ui/db/SqlResultValueAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package me.kbai.zhenxunui.ui.db

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import me.kbai.zhenxunui.databinding.ItemSqlResultValueBinding

class SqlResultValueAdapter(
map: Map<String, *>
) : RecyclerView.Adapter<SqlResultValueAdapter.ItemViewHolder>() {
val data: List<Map.Entry<String, *>> = ArrayList<Map.Entry<String, *>>(map.entries)

class ItemViewHolder(val binding: ItemSqlResultValueBinding) :
RecyclerView.ViewHolder(binding.root)

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
ItemSqlResultValueBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)

override fun getItemCount() = data.size

override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val item = data[position]

holder.binding.run {
tvField.text = item.key
tvValue.text = when (item.value) {
is String -> "\"${item.value}\""
else -> item.value.toString()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import androidx.navigation.fragment.findNavController
import me.kbai.zhenxunui.R
import me.kbai.zhenxunui.base.BaseFragment
import me.kbai.zhenxunui.databinding.FragmentConversationBinding
import me.kbai.zhenxunui.extends.launchAndCollectIn
import me.kbai.zhenxunui.extends.launchAndApiCollectIn
import me.kbai.zhenxunui.extends.setOnDebounceClickListener
import me.kbai.zhenxunui.tool.GlobalToast
import me.kbai.zhenxunui.viewmodel.ConversationViewModel
Expand Down Expand Up @@ -54,7 +54,7 @@ class ConversationFragment : BaseFragment<FragmentConversationBinding>() {
etText.setText("")

mViewModel.sendMessage(mUserId, mGroupId, message)
.launchAndCollectIn(this@ConversationFragment) {
.launchAndApiCollectIn(this@ConversationFragment) {
if (it.success()) {
mMessageAdapter.addData(
mViewModel.insertSentMessage(mGroupId, mUserId, message)
Expand All @@ -66,6 +66,11 @@ class ConversationFragment : BaseFragment<FragmentConversationBinding>() {
}
}

override fun onStart() {
super.onStart()
viewBinding.btnSend.isEnabled = true
}

override fun initData() {
mGroupId = arguments?.getString(ARGS_GROUP_ID)
mUserId = arguments?.getString(ARGS_USER_ID)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,8 @@ class PluginConfigAdapter(
val strVal = it?.toString()
try {
mEditableData.configs[config.key] = when (config.type) {
ConfigValueType.INT -> strVal?.toInt()
ConfigValueType.FLOAT -> strVal?.toFloat()
ConfigValueType.INT -> strVal?.toLong()
ConfigValueType.FLOAT -> strVal?.toDouble()
ConfigValueType.BOOL -> strVal?.lowercase()?.toBoolean()
else -> strVal
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.webkit.internal.ApiFeature.M
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import me.kbai.zhenxunui.extends.apiCollect
import me.kbai.zhenxunui.model.ExecuteSql
import me.kbai.zhenxunui.model.TableColumn
import me.kbai.zhenxunui.model.TableListItem
import me.kbai.zhenxunui.repository.ApiRepository
Expand All @@ -19,6 +20,13 @@ class DbManageViewModel : ViewModel() {

private val _columnMap: MutableMap<String, List<TableColumn>> = HashMap()

private val _executeSqlResult: MutableLiveData<List<LinkedHashMap<String, *>>> =
MutableLiveData()

@Suppress("UNCHECKED_CAST")
val executeSqlResult: LiveData<List<Map<String, *>>> =
_executeSqlResult as LiveData<List<Map<String, *>>>

fun requestTableList() = viewModelScope.launch {
ApiRepository.getTableList().apiCollect {
if (it.success()) {
Expand All @@ -40,4 +48,7 @@ class DbManageViewModel : ViewModel() {
emit(res.data)
}
}

fun executeSql(sql: String) = ApiRepository.executeSql(ExecuteSql(sql))
.onEach { res -> res.data?.also { _executeSqlResult.value = it } }
}
Loading

0 comments on commit 6c02201

Please sign in to comment.