Skip to content

Commit ee4428a

Browse files
author
Fastace
committed
修正文件恢复第二阶段文件大小为0的BUG
1 parent e387f2c commit ee4428a

4 files changed

Lines changed: 104 additions & 71 deletions

File tree

source/feature/main/restore/src/main/kotlin/com/xayah/feature/main/restore/CloudFilesRestorePage.kt

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.xayah.feature.main.restore
22

33
import android.util.Log
4+
import androidx.compose.foundation.lazy.itemsIndexed
45
import androidx.compose.foundation.layout.Arrangement
56
import androidx.compose.foundation.layout.Box
67
import androidx.compose.foundation.layout.Column
@@ -121,9 +122,7 @@ fun CloudFilesRestorePage(
121122
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
122123

123124
LaunchedEffect(key1 = accountName) {
124-
Log.d("CloudFilesRestore", "LaunchedEffect 触发,账户名: $accountName")
125125
if (accountName.isNotEmpty()) {
126-
Log.d("CloudFilesRestore", "调用 viewModel.setCloudEntity")
127126
viewModel.setCloudEntity(accountName)
128127
}
129128
}
@@ -137,76 +136,58 @@ fun CloudFilesRestorePage(
137136
.fillMaxSize()
138137
.padding(SizeTokens.Level16)
139138
) {
140-
val currentState = uiState
141-
when (currentState) {
139+
when (val currentState = uiState) {
142140
is CloudFilesRestoreUiState.Loading -> {
143-
Box(
144-
modifier = Modifier.fillMaxSize(),
145-
contentAlignment = Alignment.Center
146-
) {
141+
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
147142
CircularProgressIndicator()
148143
}
149144
}
150145

151146
is CloudFilesRestoreUiState.Success -> {
147+
// ✅ 关键修正:在 LazyColumn 外部获取 context
148+
val context = LocalContext.current
149+
152150
if (currentState.groups.isEmpty()) {
153-
Box(
154-
modifier = Modifier.fillMaxSize(),
155-
contentAlignment = Alignment.Center
156-
) {
151+
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
157152
TitleLargeText(text = "没有找到云端文件备份")
158153
}
159154
} else {
160155
LazyColumn(
161156
verticalArrangement = Arrangement.spacedBy(SizeTokens.Level8)
162157
) {
163-
items(
164-
currentState.groups,
165-
key = { item: ResticFileBackupGroup -> "${item.fullPath}-${item.timestamp}" }
166-
) { group: ResticFileBackupGroup ->
158+
itemsIndexed(
159+
items = currentState.groups,
160+
// ✅ 明确 Lambda 参数名,增强编译器推断稳定性
161+
key = { index, group -> "${group.fullPath}-${group.timestamp}-$index" }
162+
) { index, group ->
167163
CloudFileBackupGroupItem(
168164
group = group,
165+
context = context, // ✅ 使用上面获取好的 context 变量
169166
onClick = {
170167
try {
171-
Log.d("CloudFilesRestorePage", "开始处理点击事件")
172-
Log.d("CloudFilesRestorePage", "备份项被点击: ${group.mediaName}")
173-
174-
// 1. JSON 序列化 group 对象
168+
// 序列化逻辑...
175169
val groupJson = Json.encodeToString(group)
176170
val encodedJson = URLEncoder.encode(groupJson, "UTF-8")
177-
Log.d("CloudFilesRestorePage", "JSON序列化成功: ${encodedJson.take(100)}...")
178-
179-
// 2. URL 编码账户名称
180171
val cleanAccountName = accountName.replace("accountName=", "")
181172
val encodedAccountName = URLEncoder.encode(cleanAccountName, "UTF-8")
182-
Log.d("CloudFilesRestorePage", "账户名编码: $encodedAccountName")
183173

184-
// 3. 构建导航路由
185174
val url = MainRoutes.CloudFilesBackupDetail.getRoute(
186175
encodedJson,
187176
encodedAccountName
188177
)
189-
Log.d("CloudFilesRestorePage", "构建路由: $url")
190-
191-
// 4. 执行导航
192178
navController.navigateSingle(url)
193-
Log.d("CloudFilesRestorePage", "导航调用完成")
194179
} catch (e: Exception) {
195180
Log.e("CloudFilesRestorePage", "点击事件处理失败", e)
196181
}
197-
},
198-
context = LocalContext.current
182+
}
199183
)
200184
}
201185
}
202186
}
203187
}
204188

205189
is CloudFilesRestoreUiState.Error -> {
206-
Box(
207-
modifier = Modifier.fillMaxSize(),
208-
contentAlignment = Alignment.Center
209-
) {
190+
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
210191
Column(
211192
horizontalAlignment = Alignment.CenterHorizontally,
212193
verticalArrangement = Arrangement.spacedBy(SizeTokens.Level16)

source/feature/main/restore/src/main/kotlin/com/xayah/feature/main/restore/CloudFilesRestoreViewModel.kt

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,18 +287,30 @@ class CloudFilesRestoreViewModel @Inject constructor(
287287
try {
288288
Log.d(TAG, "=== 开始计算激活媒体的大小 ===")
289289
val backupDir = "${context.localBackupSaveDir()}/restore/"
290-
// 只查询激活的媒体,与第二阶段保持一致
291290
val activatedMedia = mediaDao.queryActivated(OpType.RESTORE, "", backupDir)
292291

293292
Log.d(TAG, "找到 ${activatedMedia.size} 个已激活媒体")
294293
activatedMedia.forEach { media ->
295294
Log.d(TAG, "计算媒体大小: ${media.name}")
296-
filesRepo.calculateLocalFileArchiveSize(media)
295+
Log.d(TAG, "原始路径: ${media.path}")
296+
297+
// 直接计算实际恢复文件的大小
298+
val mediaFile = File("${backupDir}files/${media.name}/media.tar")
299+
Log.d(TAG, "恢复文件路径: ${mediaFile.absolutePath}")
300+
Log.d(TAG, "媒体文件存在: ${mediaFile.exists()}, 大小: ${mediaFile.length()}")
301+
302+
if (mediaFile.exists()) {
303+
media.mediaInfo.displayBytes = mediaFile.length()
304+
mediaDao.upsert(media)
305+
Log.d(TAG, "大小计算完成: ${media.displayStatsBytes}")
306+
} else {
307+
Log.w(TAG, "媒体文件不存在: ${mediaFile.absolutePath}")
308+
}
297309
}
298310

299311
Log.d(TAG, "=== 激活媒体大小计算完成 ===")
300312
} catch (e: Exception) {
301-
Log.e(TAG, "计算媒体大小失败", e)
313+
Log.e(TAG, "计算大小失败", e)
302314
}
303315
}
304316

source/feature/main/restore/src/main/kotlin/com/xayah/feature/main/restore/ResticFilesRestorePage.kt

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import androidx.compose.foundation.layout.Column
66
import androidx.compose.foundation.layout.fillMaxSize
77
import androidx.compose.foundation.layout.padding
88
import androidx.compose.foundation.lazy.LazyColumn
9-
import androidx.compose.foundation.lazy.items
9+
import androidx.compose.foundation.lazy.itemsIndexed
1010
import androidx.compose.material3.Button
1111
import androidx.compose.material3.CircularProgressIndicator
1212
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -31,12 +31,9 @@ import androidx.compose.foundation.layout.size
3131
import androidx.compose.material3.Icon
3232
import androidx.compose.material3.MaterialTheme
3333
import androidx.compose.material3.Surface
34-
import androidx.compose.material3.Text
3534
import androidx.compose.material.icons.Icons
36-
import androidx.compose.material.icons.Icons.Default
37-
import android.content.Context
3835
import androidx.compose.material.icons.filled.Folder
39-
import com.xayah.feature.main.restore.ResticFilesRestoreViewModel
36+
import android.content.Context
4037
import com.xayah.core.ui.theme.ThemedColorSchemeKeyTokens
4138
import com.xayah.core.ui.theme.value
4239
import com.xayah.core.util.DateUtil
@@ -81,7 +78,7 @@ fun ResticFileBackupGroupItem(
8178
)
8279

8380
BodyMediumText(
84-
text = group.fullPath, // 显示完整路径
81+
text = group.fullPath,
8582
color = ThemedColorSchemeKeyTokens.Outline.value,
8683
maxLines = 2
8784
)
@@ -137,6 +134,9 @@ fun ResticFilesRestorePage(
137134
}
138135

139136
is ResticFilesRestoreUiState.Success -> {
137+
// ✅ 关键修正:在 LazyColumn 外部获取 context
138+
val context = LocalContext.current
139+
140140
if (currentState.groups.isEmpty()) {
141141
Box(
142142
modifier = Modifier.fillMaxSize(),
@@ -148,19 +148,20 @@ fun ResticFilesRestorePage(
148148
LazyColumn(
149149
verticalArrangement = Arrangement.spacedBy(SizeTokens.Level8)
150150
) {
151-
items(
152-
currentState.groups,
153-
key = { item: ResticFileBackupGroup -> "${item.fullPath}-${item.timestamp}" }
154-
) { group: ResticFileBackupGroup ->
151+
itemsIndexed(
152+
items = currentState.groups,
153+
// ✅ 明确 Lambda 参数名,增强编译器推断稳定性
154+
key = { index, group -> "${group.fullPath}-${group.timestamp}-$index" }
155+
) { index, group ->
155156
ResticFileBackupGroupItem(
156157
group = group,
158+
context = context, // ✅ 使用上面获取好的 context 变量
157159
onClick = {
158160
val groupJson = Json.encodeToString(group)
159161
val encodedJson = URLEncoder.encode(groupJson, "UTF-8")
160162
val url = MainRoutes.ResticFilesBackupDetail.getRoute(encodedJson)
161163
navController.navigateSingle(url)
162-
},
163-
context = LocalContext.current
164+
}
164165
)
165166
}
166167
}

source/feature/main/restore/src/main/kotlin/com/xayah/feature/main/restore/ResticFilesRestoreViewModel.kt

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -254,18 +254,28 @@ class ResticFilesRestoreViewModel @Inject constructor(
254254
try {
255255
Log.d("ResticFilesRestore", "=== 开始计算激活媒体的大小 ===")
256256
val backupDir = "${context.localBackupSaveDir()}/restore/"
257-
// 修改:只查询激活的媒体,与第二阶段保持一致
258257
val activatedMedia = mediaDao.queryActivated(OpType.RESTORE, "", backupDir)
259258

260259
Log.d("ResticFilesRestore", "找到 ${activatedMedia.size} 个已激活媒体")
261260
activatedMedia.forEach { media ->
262-
Log.d("ResticFilesRestore", "计算媒体大小: ${media.name}")
263-
filesRepo.calculateLocalFileArchiveSize(media)
261+
Log.d("ResticFilesRestore", "计算媒体大小: ${media.name}, 路径: ${media.path}")
262+
263+
// 直接计算实际恢复文件的大小
264+
val mediaFile = File("${backupDir}files/${media.name}/media.tar")
265+
Log.d("ResticFilesRestore", "媒体文件存在: ${mediaFile.exists()}, 大小: ${mediaFile.length()}")
266+
267+
if (mediaFile.exists()) {
268+
media.mediaInfo.displayBytes = mediaFile.length()
269+
mediaDao.upsert(media)
270+
Log.d("ResticFilesRestore", "大小计算完成: ${media.displayStatsBytes}")
271+
} else {
272+
Log.w("ResticFilesRestore", "媒体文件不存在: ${mediaFile.absolutePath}")
273+
}
264274
}
265275

266276
Log.d("ResticFilesRestore", "=== 激活媒体大小计算完成 ===")
267277
} catch (e: Exception) {
268-
Log.e("ResticFilesRestore", "计算媒体大小失败", e)
278+
Log.e("ResticFilesRestore", "计算大小失败", e)
269279
}
270280
}
271281

@@ -305,43 +315,72 @@ class ResticFilesRestoreViewModel @Inject constructor(
305315
}
306316

307317
fun loadBackedUpFiles() {
318+
Log.d("ResticFilesRestore", "=== loadBackedUpFiles 开始 ===")
319+
308320
viewModelScope.launch {
321+
val startTime = System.currentTimeMillis()
309322
_uiState.value = ResticFilesRestoreUiState.Loading
310-
311-
val repoPath = context.readResticRepoPath()
312-
val password = context.readResticPassword()
313-
314-
if (repoPath.isNullOrEmpty() || password.isNullOrEmpty()) {
315-
_uiState.value = ResticFilesRestoreUiState.Error("Restic not configured")
316-
return@launch
317-
}
323+
Log.d("ResticFilesRestore", "UI状态设置为Loading")
318324

319325
try {
326+
Log.d("ResticFilesRestore", "读取本地 Restic 配置")
327+
val repoPath = context.readResticRepoPath()
328+
val password = context.readResticPassword()
329+
330+
if (repoPath.isNullOrEmpty() || password.isNullOrEmpty()) {
331+
Log.e("ResticFilesRestore", "Restic配置不完整")
332+
_uiState.value = ResticFilesRestoreUiState.Error("Restic not configured")
333+
return@launch
334+
}
335+
Log.d("ResticFilesRestore", "Restic配置读取成功")
336+
337+
Log.d("ResticFilesRestore", "开始调用 listBackedUpFiles")
320338
val files = resticRepo.listBackedUpFiles(repoPath, password)
339+
Log.d("ResticFilesRestore", "listBackedUpFiles 返回 ${files.size} 个文件备份项")
321340

322-
// 按媒体名称+前缀路径+时间戳分组
323-
val groupedByPath = files
341+
if (files.isEmpty()) {
342+
Log.w("ResticFilesRestore", "未找到任何本地文件备份")
343+
_uiState.value = ResticFilesRestoreUiState.Success(emptyList())
344+
return@launch
345+
}
346+
347+
Log.d("ResticFilesRestore", "开始分组文件备份")
348+
val groupedBackups = files
324349
.groupBy {
325-
// 提取前缀路径(去掉最后一层)
326350
val prefixPath = it.fullPath.substringBeforeLast("/")
327351
Triple(it.mediaName, prefixPath, it.timestamp)
328352
}
329-
.map { (groupKey, backups) ->
353+
.map { (groupKey, backupsInGroup) ->
330354
val (mediaName, prefixPath, timestamp) = groupKey
331-
ResticFileBackupGroup(
355+
Log.d("ResticFilesRestore", "处理分组: $mediaName-$prefixPath-$timestamp, 包含 ${backupsInGroup.size} 个备份项")
356+
357+
val group = ResticFileBackupGroup(
332358
mediaName = mediaName,
333-
fullPath = prefixPath, // 使用前缀路径用于分组逻辑
359+
fullPath = prefixPath,
334360
timestamp = timestamp,
335-
backups = backups, // 直接使用backups
361+
backups = backupsInGroup.sortedBy { backup ->
362+
when (backup.dataType) {
363+
DataType.PACKAGE_MEDIA -> 0
364+
DataType.PACKAGE_CONFIG -> 1
365+
else -> 2
366+
}
367+
},
336368
mediaLabel = mediaName
337369
)
370+
Log.d("ResticFilesRestore", "创建备份组: ${group.mediaName}, 时间戳: ${group.timestamp}, 备份数量: ${group.backups.size}")
371+
group
338372
}
339373
.sortedByDescending { it.timestamp }
340374

341-
_uiState.value = ResticFilesRestoreUiState.Success(groupedByPath)
375+
val duration = System.currentTimeMillis() - startTime
376+
Log.d("ResticFilesRestore", "文件备份分组完成,共 ${groupedBackups.size} 个组,耗时: ${duration}ms")
377+
_uiState.value = ResticFilesRestoreUiState.Success(groupedBackups)
378+
342379
} catch (e: Exception) {
343-
_uiState.value = ResticFilesRestoreUiState.Error(e.message ?: "Unknown error")
380+
Log.e("ResticFilesRestore", "加载本地文件备份时发生异常", e)
381+
_uiState.value = ResticFilesRestoreUiState.Error("加载失败: ${e.message}")
344382
}
345383
}
384+
Log.d("ResticFilesRestore", "=== loadBackedUpFiles 结束 ===")
346385
}
347386
}

0 commit comments

Comments
 (0)