fix(t209): retain login inputs after failure

This commit is contained in:
QiuSW
2026-07-27 16:03:08 +08:00
parent f99744e2ed
commit 5782c3088d
4 changed files with 38 additions and 18 deletions
@@ -358,9 +358,7 @@ class MainActivity : ComponentActivity() {
state = procurementState, state = procurementState,
readiness = readiness, readiness = readiness,
onLogin = { input: LoginInput -> onLogin = { input: LoginInput ->
lifecycleScope.launch {
procurementRepository.login(input) procurementRepository.login(input)
}
}, },
onClaim = { onClaim = {
lifecycleScope.launch { lifecycleScope.launch {
@@ -34,6 +34,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -50,12 +51,13 @@ import com.roubao.autopilot.readiness.DeviceReadinessSnapshot
import com.roubao.autopilot.ui.theme.BaoziTheme import com.roubao.autopilot.ui.theme.BaoziTheme
import java.time.Instant import java.time.Instant
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Composable @Composable
fun ProcurementScreen( fun ProcurementScreen(
state: ProcurementUiState, state: ProcurementUiState,
readiness: DeviceReadinessSnapshot, readiness: DeviceReadinessSnapshot,
onLogin: (LoginInput) -> Unit, onLogin: suspend (LoginInput) -> Boolean,
onClaim: () -> Unit, onClaim: () -> Unit,
onStart: (ExecutionMode) -> Unit, onStart: (ExecutionMode) -> Unit,
onRelease: () -> Unit, onRelease: () -> Unit,
@@ -260,9 +262,10 @@ private fun ExecutionModeSelector(
@Composable @Composable
private fun LoginSection( private fun LoginSection(
state: ProcurementUiState, state: ProcurementUiState,
onLogin: (LoginInput) -> Unit onLogin: suspend (LoginInput) -> Boolean
) { ) {
val colors = BaoziTheme.colors val colors = BaoziTheme.colors
val coroutineScope = rememberCoroutineScope()
var backendUrl by remember(state.profile.backendUrl) { var backendUrl by remember(state.profile.backendUrl) {
mutableStateOf(state.profile.backendUrl) mutableStateOf(state.profile.backendUrl)
} }
@@ -274,6 +277,7 @@ private fun LoginSection(
mutableStateOf(state.profile.deviceId) mutableStateOf(state.profile.deviceId)
} }
var deviceToken by remember { mutableStateOf("") } var deviceToken by remember { mutableStateOf("") }
var submitting by remember { mutableStateOf(false) }
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
Text( Text(
@@ -329,19 +333,28 @@ private fun LoginSection(
) )
Button( Button(
onClick = { onClick = {
onLogin( if (!submitting) {
LoginInput( val input = LoginInput(
backendUrl = backendUrl, backendUrl = backendUrl,
username = username, username = username,
password = password, password = password,
deviceId = deviceId, deviceId = deviceId,
deviceToken = deviceToken deviceToken = deviceToken
) )
) submitting = true
coroutineScope.launch {
try {
if (onLogin(input)) {
password = "" password = ""
deviceToken = "" deviceToken = ""
}
} finally {
submitting = false
}
}
}
}, },
enabled = !state.busy, enabled = !state.busy && !submitting,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Icon(Icons.Filled.Lock, contentDescription = null) Icon(Icons.Filled.Lock, contentDescription = null)
+6 -2
View File
@@ -5,7 +5,8 @@
## 当前快照 ## 当前快照
- 日期:2026-07-27 - 日期:2026-07-27
- 阶段:T-207 App 本地 VLM、候选、证据和结果回传完成,下一步 T-208 - 阶段:T-207 App 本地 VLM、候选、证据和结果回传完成;T-209 登录失败保留输入完成,
下一步 T-208
- Git:当前分支为 `main`;T-001 至 T-004、T-101 至 T-104、T-201 至 T-206 - Git:当前分支为 `main`;T-001 至 T-004、T-101 至 T-104、T-201 至 T-206
均已纳入 Git 历史;T-207 待本次提交 均已纳入 Git 历史;T-207 待本次提交
- 生产代码:`android-buyer/` 已接入 Roubao Android 源码 - 生产代码:`android-buyer/` 已接入 Roubao Android 源码
@@ -40,6 +41,8 @@
状态、严格 HTTPS/Debug loopback 地址策略、`HttpTaskSource`、参考图校验、手动领取、 状态、严格 HTTPS/Debug loopback 地址策略、`HttpTaskSource`、参考图校验、手动领取、
预览/start 二次确认、release、30 秒前台 heartbeat、有限离线、`SAFE_STOPPED`、 预览/start 二次确认、release、30 秒前台 heartbeat、有限离线、`SAFE_STOPPED`、
取消确认和进程恢复;后台任务不读取或覆盖本地 VLM provider 取消确认和进程恢复;后台任务不读取或覆盖本地 VLM provider
- 登录重试:采购登录失败、本地校验失败或网络失败时保留当前页面输入,只有成功才清理
Compose 内存中的密码和设备密钥;敏感值不新增持久化或日志路径
- T-207 结果闭环:`execution_events`、受控截图、候选批次、模型出处、人工结论与 - T-207 结果闭环:`execution_events`、受控截图、候选批次、模型出处、人工结论与
失败记录均按 execution 存储;Android 使用加密 outbox 按事件、截图、候选和终态 失败记录均按 execution 存储;Android 使用加密 outbox 按事件、截图、候选和终态
顺序回传,授权到期补报单独审计,所有终态固定 `order_submitted=false`。管理任务 顺序回传,授权到期补报单独审计,所有终态固定 `order_submitted=false`。管理任务
@@ -91,6 +94,7 @@
| `docs/tasks/T-205.md` | DONE | 原子 claim、租约、execution 和取消安全确认 | | `docs/tasks/T-205.md` | DONE | 原子 claim、租约、execution 和取消安全确认 |
| `docs/tasks/T-206.md` | DONE | App 领取、默认 30 分钟有限离线执行、恢复和安全停止 | | `docs/tasks/T-206.md` | DONE | App 领取、默认 30 分钟有限离线执行、恢复和安全停止 |
| `docs/tasks/T-207.md` | DONE | App 本地 VLM、Key 加密、候选、事件、截图和结果回传 | | `docs/tasks/T-207.md` | DONE | App 本地 VLM、Key 加密、候选、事件、截图和结果回传 |
| `docs/tasks/T-209.md` | DONE | 登录失败保留采购员和设备输入以便直接重试 |
| `docs/design/` | 已确认 | T-202 原型索引、4 个管理页和 7 个 Android 页面 | | `docs/design/` | 已确认 | T-202 原型索引、4 个管理页和 7 个 Android 页面 |
| `deepseek总结.txt` | 已有 | 历史讨论摘要,不是正式需求权威 | | `deepseek总结.txt` | 已有 | 历史讨论摘要,不是正式需求权威 |
| `android-buyer/` | 已有 | Roubao `main` 固定 commit 的 Android 基线 | | `android-buyer/` | 已有 | Roubao `main` 固定 commit 的 Android 基线 |
@@ -101,7 +105,7 @@
## 任务摘要 ## 任务摘要
- 已完成:T-001 至 T-004、T-101 至 T-104、T-201 至 T-207。 - 已完成:T-001 至 T-004、T-101 至 T-104、T-201 至 T-207、T-209。
- 正在进行:无。 - 正在进行:无。
- 下一个可领取任务:T-208 候选决策数据与人工理由闭环。 - 下一个可领取任务:T-208 候选决策数据与人工理由闭环。
- 后置任务:T-208 候选决策数据与人工理由闭环;不得跳过 T-206/T-207 提前实现。 - 后置任务:T-208 候选决策数据与人工理由闭环;不得跳过 T-206/T-207 提前实现。
+10 -5
View File
@@ -4,7 +4,7 @@ title: 保留失败采购登录的已填写凭证
phase: 2 phase: 2
deps: deps:
- T-206 - T-206
status: TODO status: DONE
created: 2026-07-27 created: 2026-07-27
context_ref: d86a88e context_ref: d86a88e
work_branch: null work_branch: null
@@ -34,10 +34,10 @@ write_paths:
## 验收要点 ## 验收要点
- [ ] 服务端拒绝、网络失败和本地校验失败后,五个已填写字段可直接修改并重试。 - [x] 服务端拒绝、网络失败和本地校验失败后,五个已填写字段可直接修改并重试。
- [ ] 成功登录后不保留登录页敏感输入,设备密钥只进入既有加密存储。 - [x] 成功登录后不保留登录页敏感输入,设备密钥只进入既有加密存储。
- [ ] 密码和设备密钥不进入日志、普通 Preferences 或错误文案。 - [x] 密码和设备密钥不进入日志、普通 Preferences 或错误文案。
- [ ] Android 单元测试与 Debug 构建通过。 - [x] Android 单元测试与 Debug 构建通过。
## 边界 ## 边界
@@ -47,3 +47,8 @@ write_paths:
## 执行记录 ## 执行记录
- 2026-07-27:任务已登记,待实现。 - 2026-07-27:任务已登记,待实现。
- 2026-07-27:登录 UI 改为等待 Repository 布尔结果;失败时保留 Compose 内存中的
后台地址、账号、密码、设备 ID 和设备密钥,成功才清理敏感输入。新增本地提交锁,
防止请求尚未开始时重复点击。
- 2026-07-27:`./gradlew.bat :app:compileDebugKotlin :app:testDebugUnitTest
:app:assembleDebug` 通过;最新 Debug APK 已安装并冷启动至 PKG110,无 crash/ANR。