feat: 新增 goodsCode 字段并同步迁移 DB v11,修复 IPv6 WebSocket 地址格式,重构 VectorCollectionFragment 相机回调为协程
This commit is contained in:
@@ -25,7 +25,7 @@ import com.shuwei.dish.match.entity.SeasoningSlotEntity
|
|||||||
CookFoodGoodsEntity::class,
|
CookFoodGoodsEntity::class,
|
||||||
SeasoningSlotEntity::class,
|
SeasoningSlotEntity::class,
|
||||||
],
|
],
|
||||||
version = 10,
|
version = 11,
|
||||||
exportSchema = true
|
exportSchema = true
|
||||||
)
|
)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
@@ -232,6 +232,14 @@ val MIGRATION_9_10 = MigrationImpl(9, 10) { db ->
|
|||||||
db.execSQL("DROP INDEX IF EXISTS dm_cook_food_foodId_IDX")
|
db.execSQL("DROP INDEX IF EXISTS dm_cook_food_foodId_IDX")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 版本10→11:为 dm_cook_food_goods 和 dm_seasoning 表新增 goodsCode(物料编码)字段
|
||||||
|
*/
|
||||||
|
val MIGRATION_10_11 = MigrationImpl(10, 11) { db ->
|
||||||
|
db.execSQL("ALTER TABLE dm_cook_food_goods ADD COLUMN goodsCode TEXT DEFAULT ''")
|
||||||
|
db.execSQL("ALTER TABLE dm_seasoning ADD COLUMN goodsCode TEXT DEFAULT ''")
|
||||||
|
}
|
||||||
|
|
||||||
class DatabaseProvider(private val context: Context) {
|
class DatabaseProvider(private val context: Context) {
|
||||||
// 单例模式确保全局唯一实例
|
// 单例模式确保全局唯一实例
|
||||||
val instance: AppDatabase by lazy {
|
val instance: AppDatabase by lazy {
|
||||||
@@ -253,6 +261,7 @@ class DatabaseProvider(private val context: Context) {
|
|||||||
.addMigrations(MIGRATION_7_8)
|
.addMigrations(MIGRATION_7_8)
|
||||||
.addMigrations(MIGRATION_8_9)
|
.addMigrations(MIGRATION_8_9)
|
||||||
.addMigrations(MIGRATION_9_10)
|
.addMigrations(MIGRATION_9_10)
|
||||||
|
.addMigrations(MIGRATION_10_11)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,9 @@ class CookFoodGoodsEntity(
|
|||||||
|
|
||||||
var materId: String? = "",
|
var materId: String? = "",
|
||||||
|
|
||||||
|
/** 物料编码 */
|
||||||
|
var goodsCode: String? = "",
|
||||||
|
|
||||||
var isDel: Int = 0,
|
var isDel: Int = 0,
|
||||||
var createTime: String = DateTimeUtil.formatDateTime(LocalDateTime.now())
|
var createTime: String = DateTimeUtil.formatDateTime(LocalDateTime.now())
|
||||||
) : BaseEntity, Serializable {
|
) : BaseEntity, Serializable {
|
||||||
@@ -80,6 +83,7 @@ class CookFoodGoodsEntity(
|
|||||||
popularName = popularName,
|
popularName = popularName,
|
||||||
zjmCode = zjmCode,
|
zjmCode = zjmCode,
|
||||||
materId = materId,
|
materId = materId,
|
||||||
|
materCode = goodsCode,
|
||||||
materialType = materialType,
|
materialType = materialType,
|
||||||
useWeight = useWeight,
|
useWeight = useWeight,
|
||||||
relateionType = relateionType,
|
relateionType = relateionType,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ data class GoodsItem(
|
|||||||
val popularName: String? = null,
|
val popularName: String? = null,
|
||||||
val zjmCode: String? = null,
|
val zjmCode: String? = null,
|
||||||
val materId: String? = null,
|
val materId: String? = null,
|
||||||
|
val materCode: String? = null,
|
||||||
|
|
||||||
// 从 CookFoodGoodsEntity 迁移的数据字段(DishPartAdapter 显示及转换用)
|
// 从 CookFoodGoodsEntity 迁移的数据字段(DishPartAdapter 显示及转换用)
|
||||||
var materialType: Int = 0,
|
var materialType: Int = 0,
|
||||||
@@ -33,6 +34,11 @@ data class GoodsItem(
|
|||||||
var foodScore: Int = -1
|
var foodScore: Int = -1
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物料编码,与查询接口返回的 materCode 同值,统一对外暴露为 goodsCode 供下游使用
|
||||||
|
*/
|
||||||
|
val goodsCode: String? get() = materCode
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换为 CookFoodGoodsEntity,用于写入数据库或传给 SubmitFoodActivity
|
* 转换为 CookFoodGoodsEntity,用于写入数据库或传给 SubmitFoodActivity
|
||||||
*/
|
*/
|
||||||
@@ -42,6 +48,7 @@ data class GoodsItem(
|
|||||||
popularName = popularName,
|
popularName = popularName,
|
||||||
zjmCode = zjmCode,
|
zjmCode = zjmCode,
|
||||||
materId = materId,
|
materId = materId,
|
||||||
|
goodsCode = goodsCode,
|
||||||
materialType = materialType,
|
materialType = materialType,
|
||||||
useWeight = useWeight,
|
useWeight = useWeight,
|
||||||
relateionType = relateionType,
|
relateionType = relateionType,
|
||||||
@@ -58,6 +65,7 @@ data class GoodsItem(
|
|||||||
popularName = popularName,
|
popularName = popularName,
|
||||||
zjmCode = zjmCode,
|
zjmCode = zjmCode,
|
||||||
materId = materId,
|
materId = materId,
|
||||||
|
goodsCode = goodsCode,
|
||||||
materialType = materialType,
|
materialType = materialType,
|
||||||
relateionType = relateionType,
|
relateionType = relateionType,
|
||||||
allEdible = allEdible,
|
allEdible = allEdible,
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ data class SeasoningEntity(
|
|||||||
|
|
||||||
var materId: String? = "",
|
var materId: String? = "",
|
||||||
|
|
||||||
|
/** 物料编码 */
|
||||||
|
var goodsCode: String? = "",
|
||||||
|
|
||||||
var sort: Int = 9999,
|
var sort: Int = 9999,
|
||||||
/**
|
/**
|
||||||
* isDel表示数据是否已删除,1-已删除,为无效数据,0-正常使用,有效数据
|
* isDel表示数据是否已删除,1-已删除,为无效数据,0-正常使用,有效数据
|
||||||
|
|||||||
@@ -86,7 +86,9 @@ class ScaleWebSocketClient {
|
|||||||
Log.d(TAG, "放弃过期连接任务: $deviceId, version=$version")
|
Log.d(TAG, "放弃过期连接任务: $deviceId, version=$version")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val url = "ws://$host:$port"
|
// IPv6 地址在 URL 中需用方括号包裹,否则 OkHttp 会将冒号后内容误解析为端口
|
||||||
|
val formattedHost = if (host.contains(':')) "[$host]" else host
|
||||||
|
val url = "ws://$formattedHost:$port"
|
||||||
val request = Request.Builder().url(url).build()
|
val request = Request.Builder().url(url).build()
|
||||||
|
|
||||||
val ws = httpClient.newWebSocket(request, object : WebSocketListener() {
|
val ws = httpClient.newWebSocket(request, object : WebSocketListener() {
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ class SettingActivity : BaseActivity() {
|
|||||||
.setTitle("数据清除")
|
.setTitle("数据清除")
|
||||||
.setContent("将物理删除全部 4 张表数据(菜品、食材、调料、槽位),此操作不可恢复,同时重置菜品模式和食堂id,确认继续?")
|
.setContent("将物理删除全部 4 张表数据(菜品、食材、调料、槽位),此操作不可恢复,同时重置菜品模式和食堂id,确认继续?")
|
||||||
.setNegativeButton("取消")
|
.setNegativeButton("取消")
|
||||||
.setPositiveButton("确认清除") {
|
.setPositiveButton("确认") {
|
||||||
appViewModel.clearAllData {
|
appViewModel.clearAllData {
|
||||||
SpTool.cookMode = -1
|
SpTool.cookMode = -1
|
||||||
SpTool.canteenId = "0"
|
SpTool.canteenId = "0"
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ import android.view.inputmethod.InputMethodManager
|
|||||||
import androidx.camera.view.PreviewView
|
import androidx.camera.view.PreviewView
|
||||||
import androidx.core.view.doOnLayout
|
import androidx.core.view.doOnLayout
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import com.shuwei.dish.match.R
|
import com.shuwei.dish.match.R
|
||||||
import com.shuwei.dish.match.adapter.VectorCollectionAdapter
|
import com.shuwei.dish.match.adapter.VectorCollectionAdapter
|
||||||
import com.shuwei.dish.match.adapter.Food3Adapter
|
import com.shuwei.dish.match.adapter.Food3Adapter
|
||||||
@@ -111,15 +115,16 @@ class VectorCollectionFragment : BaseFragment<FragmentVectorCollectionBinding>()
|
|||||||
|
|
||||||
//typealias CameraCallback = (Uri) -> Unit
|
//typealias CameraCallback = (Uri) -> Unit
|
||||||
|
|
||||||
private val cameraCallback: (Uri) -> Unit = cameraCallback@{ uri ->
|
private val cameraCallback: (Uri) -> Unit = { uri ->
|
||||||
try {
|
lifecycleScope.launch {
|
||||||
val index = vectorList.indexOfFirst { it.imageFile == null }
|
try {
|
||||||
if (index == -1) {
|
val index = vectorList.indexOfFirst { it.imageFile == null }
|
||||||
toast("每次只允许保存${MAX_COUNT}条数据")
|
if (index == -1) {
|
||||||
hideWaitingDialog()
|
toast("每次只允许保存${MAX_COUNT}条数据")
|
||||||
return@cameraCallback
|
hideWaitingDialog()
|
||||||
}
|
return@launch
|
||||||
activity?.runOnUiThread {
|
}
|
||||||
|
// 主线程更新 UI
|
||||||
vectorList[index].let {
|
vectorList[index].let {
|
||||||
it.imageVector = null
|
it.imageVector = null
|
||||||
it.bitmap = null
|
it.bitmap = null
|
||||||
@@ -128,53 +133,50 @@ class VectorCollectionFragment : BaseFragment<FragmentVectorCollectionBinding>()
|
|||||||
it.imageUri = uri
|
it.imageUri = uri
|
||||||
}
|
}
|
||||||
vectorAdapter.notifyItemChanged(index)
|
vectorAdapter.notifyItemChanged(index)
|
||||||
}
|
// 切到 IO 线程执行耗时的 Bitmap 解码
|
||||||
// hideWaitingDialog()
|
val bitmap = withContext(Dispatchers.IO) {
|
||||||
Thread {
|
ImageUtil.uriToBitmap(requireActivity(), uri)
|
||||||
ImageUtil.uriToBitmap(requireActivity(), uri)?.let { bitmap ->
|
|
||||||
getImageVector(index, bitmap)
|
|
||||||
}
|
}
|
||||||
}.start()
|
bitmap?.let { getImageVector(index, it) }
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
hideWaitingDialog()
|
hideWaitingDialog()
|
||||||
toast("程序异常${e.message}")
|
toast("程序异常${e.message}")
|
||||||
log("程序异常${e.message}")
|
log("程序异常${e.message}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getImageVector(index: Int, bitmap: Bitmap) {
|
private suspend fun getImageVector(index: Int, bitmap: Bitmap) {
|
||||||
// val bitmap = BitmapCropper.cropCenter(
|
// 切到 IO 线程执行耗时的向量计算和文件保存
|
||||||
// original = srcBmp,
|
val (imageVector, file) = withContext(Dispatchers.IO) {
|
||||||
// targetWidth = 900, targetHeight = 900,
|
try {
|
||||||
//// offsetX = 30, offsetY = 100
|
val vector = FoodModule.bitmap2FloatArray(bitmap, false)
|
||||||
// )
|
val file = BitmapSaver.saveToAppFilesDir(
|
||||||
val imageVector = try {
|
bitmap, requireActivity(), "IMG_CROP_${System.currentTimeMillis()}.jpg"
|
||||||
FoodModule.bitmap2FloatArray(bitmap, false)
|
)
|
||||||
} catch (e: Exception) {
|
log("${this.javaClass.simpleName}-cameraCallback-裁剪bitmap保存文件路径:${file?.absolutePath}")
|
||||||
e.printStackTrace()
|
vector to file
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null to null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 回到主线程处理结果
|
||||||
|
if (imageVector == null) {
|
||||||
toast("操作失败")
|
toast("操作失败")
|
||||||
log("操作失败:${e.message}")
|
log("操作失败")
|
||||||
hideWaitingDialog()
|
hideWaitingDialog()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val file = BitmapSaver.saveToAppFilesDir(
|
vectorList[index].let {
|
||||||
bitmap, requireActivity(), "IMG_CROP_${System.currentTimeMillis()}.jpg"
|
it.imageVector = imageVector
|
||||||
)
|
it.bitmap = null
|
||||||
log("${this.javaClass.simpleName}-cameraCallback-裁剪bitmap保存文件路径:${file?.absolutePath}")
|
it.isShowCamera = false
|
||||||
|
it.imageFile = file
|
||||||
activity?.runOnUiThread {
|
|
||||||
vectorList[index].let {
|
|
||||||
it.imageVector = imageVector
|
|
||||||
it.bitmap = null
|
|
||||||
it.isShowCamera = false
|
|
||||||
it.imageFile = file
|
|
||||||
}
|
|
||||||
vectorAdapter.notifyItemChanged(index)
|
|
||||||
}
|
|
||||||
if (bitmap.isRecycled.not()) {
|
|
||||||
bitmap.recycle()
|
|
||||||
}
|
}
|
||||||
|
vectorAdapter.notifyItemChanged(index)
|
||||||
|
if (bitmap.isRecycled.not()) bitmap.recycle()
|
||||||
hideWaitingDialog()
|
hideWaitingDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user