Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff8c5361cb | ||
|
|
23dafcf73f | ||
|
|
3d5e9646f6 |
@@ -74,6 +74,8 @@ class AgentStatusFragment : Fragment() {
|
||||
private lateinit var mediaPermissionDetail: TextView
|
||||
private lateinit var mediaPermissionAction: android.widget.Button
|
||||
private lateinit var deviceText: TextView
|
||||
private lateinit var deviceWifiText: TextView
|
||||
private lateinit var deviceDebugText: TextView
|
||||
private val mediaPermissionRequestLauncher by lazy {
|
||||
registerForActivityResult(androidx.activity.result.contract.ActivityResultContracts.RequestPermission()) {
|
||||
refreshStatus()
|
||||
@@ -168,6 +170,20 @@ class AgentStatusFragment : Fragment() {
|
||||
}
|
||||
addView(mediaPermissionAction)
|
||||
}))
|
||||
addView(context.card(context.cardColumn().apply {
|
||||
addView(context.label("设备连接", 14f, context.getColor(R.color.agent_text_muted)))
|
||||
deviceWifiText = context.label("Wi-Fi IP:—", 18f, context.getColor(R.color.agent_text), true).apply {
|
||||
setPadding(0, context.dp(6), 0, 0)
|
||||
}
|
||||
deviceDebugText = context.label("无线调试:—", 18f, context.getColor(R.color.agent_text_muted), true).apply {
|
||||
setPadding(0, context.dp(6), 0, 0)
|
||||
}
|
||||
addView(deviceWifiText)
|
||||
addView(deviceDebugText)
|
||||
addView(context.label(DeviceConnectionPolicy.PORT_HINT, 14f, context.getColor(R.color.agent_text_muted)).apply {
|
||||
setPadding(0, context.dp(6), 0, 0)
|
||||
})
|
||||
}))
|
||||
}
|
||||
refreshLayout = SwipeRefreshLayout(context).apply {
|
||||
setColorSchemeResources(R.color.agent_primary_light)
|
||||
@@ -245,6 +261,10 @@ class AgentStatusFragment : Fragment() {
|
||||
private fun refreshStatus() {
|
||||
if (!isAdded || view == null) return
|
||||
val context = requireContext()
|
||||
val deviceConnection = DeviceConnectionPolicy.display(DeviceConnectionReader.read(context))
|
||||
deviceWifiText.text = deviceConnection.wifiText
|
||||
deviceDebugText.text = deviceConnection.debugText
|
||||
deviceDebugText.setTextColor(context.getColor(deviceConnection.debugColor))
|
||||
val state = stateStore.read()
|
||||
val connected = state.code in setOf("ONLINE", "BUSY", "COLLECTION_COOLDOWN")
|
||||
connectionTitle.text = when (state.code) {
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package cn.ilapage.goauto.agent.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.provider.Settings
|
||||
import androidx.annotation.ColorRes
|
||||
import cn.ilapage.goauto.agent.R
|
||||
import java.net.Inet4Address
|
||||
import java.net.InetAddress
|
||||
|
||||
/** 无线调试开关的读取结果;读取失败为 [UNKNOWN]。 */
|
||||
internal enum class WirelessDebugState { ENABLED, DISABLED, UNKNOWN }
|
||||
|
||||
internal data class DeviceConnectionReading(
|
||||
val wifiIpv4: String?,
|
||||
val wirelessDebug: WirelessDebugState,
|
||||
)
|
||||
|
||||
internal data class DeviceConnectionDisplay(
|
||||
val wifiText: String,
|
||||
val debugText: String,
|
||||
@ColorRes val debugColor: Int,
|
||||
)
|
||||
|
||||
/** 原始读数到显示文案/颜色的纯函数策略,不依赖 Android 系统服务。 */
|
||||
internal object DeviceConnectionPolicy {
|
||||
const val PORT_HINT = "端口请在 开发者选项 → 无线调试 中查看"
|
||||
const val NO_WIFI_TEXT = "未连接 Wi-Fi"
|
||||
|
||||
/** 仅当前网络为 Wi-Fi 时,从地址列表中取第一个非回环 IPv4;否则返回 null。 */
|
||||
fun selectWifiIpv4(isWifi: Boolean, addresses: List<InetAddress>): String? {
|
||||
if (!isWifi) return null
|
||||
return addresses.firstOrNull { it is Inet4Address && !it.isLoopbackAddress }?.hostAddress
|
||||
}
|
||||
|
||||
/** adb_wifi_enabled 原始值:1 开启,0 未开启,其他值或读取失败(null)为未知。 */
|
||||
fun debugState(raw: Int?): WirelessDebugState = when (raw) {
|
||||
1 -> WirelessDebugState.ENABLED
|
||||
0 -> WirelessDebugState.DISABLED
|
||||
else -> WirelessDebugState.UNKNOWN
|
||||
}
|
||||
|
||||
fun display(reading: DeviceConnectionReading): DeviceConnectionDisplay {
|
||||
val (text, color) = when (reading.wirelessDebug) {
|
||||
WirelessDebugState.ENABLED -> "已开启" to R.color.agent_primary_light
|
||||
WirelessDebugState.DISABLED -> "未开启" to R.color.agent_warning
|
||||
WirelessDebugState.UNKNOWN -> "无法读取" to R.color.agent_text_muted
|
||||
}
|
||||
return DeviceConnectionDisplay(
|
||||
wifiText = "Wi-Fi IP:${reading.wifiIpv4 ?: NO_WIFI_TEXT}",
|
||||
debugText = "无线调试:$text",
|
||||
debugColor = color,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** 系统读取的薄封装;任何异常都降级为「未知」,不记录日志、不持久化。 */
|
||||
internal object DeviceConnectionReader {
|
||||
fun read(context: Context): DeviceConnectionReading = DeviceConnectionReading(
|
||||
wifiIpv4 = readWifiIpv4(context),
|
||||
wirelessDebug = DeviceConnectionPolicy.debugState(readDebugRaw(context)),
|
||||
)
|
||||
|
||||
private fun readWifiIpv4(context: Context): String? = try {
|
||||
val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
|
||||
val network = manager?.activeNetwork
|
||||
val isWifi = network?.let { manager.getNetworkCapabilities(it) }
|
||||
?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true
|
||||
val addresses = if (isWifi && manager != null && network != null) {
|
||||
manager.getLinkProperties(network)?.linkAddresses?.map { it.address }.orEmpty()
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
DeviceConnectionPolicy.selectWifiIpv4(isWifi, addresses)
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
private fun readDebugRaw(context: Context): Int? = try {
|
||||
Settings.Global.getInt(context.contentResolver, "adb_wifi_enabled", 0)
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package cn.ilapage.goauto.agent
|
||||
|
||||
import cn.ilapage.goauto.agent.ui.DeviceConnectionPolicy
|
||||
import cn.ilapage.goauto.agent.ui.DeviceConnectionReading
|
||||
import cn.ilapage.goauto.agent.ui.WirelessDebugState
|
||||
import java.net.InetAddress
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Test
|
||||
|
||||
class DeviceConnectionPolicyTest {
|
||||
private fun ip(text: String) = InetAddress.getByName(text)
|
||||
|
||||
@Test fun wifiWithIpv4ReturnsAddress() {
|
||||
assertEquals("192.168.0.93", DeviceConnectionPolicy.selectWifiIpv4(true, listOf(ip("192.168.0.93"))))
|
||||
}
|
||||
|
||||
@Test fun nonWifiReturnsNull() {
|
||||
assertNull(DeviceConnectionPolicy.selectWifiIpv4(false, listOf(ip("10.0.0.2"))))
|
||||
}
|
||||
|
||||
@Test fun wifiWithOnlyIpv6ReturnsNull() {
|
||||
assertNull(DeviceConnectionPolicy.selectWifiIpv4(true, listOf(ip("2001:db8::1"))))
|
||||
}
|
||||
|
||||
@Test fun wifiWithMultipleAddressesPicksIpv4() {
|
||||
assertEquals(
|
||||
"172.16.1.5",
|
||||
DeviceConnectionPolicy.selectWifiIpv4(true, listOf(ip("2001:db8::1"), ip("172.16.1.5"))),
|
||||
)
|
||||
}
|
||||
|
||||
@Test fun loopbackIpv4IsSkipped() {
|
||||
assertNull(DeviceConnectionPolicy.selectWifiIpv4(true, listOf(ip("127.0.0.1"))))
|
||||
}
|
||||
|
||||
@Test fun debugRawValuesMapToState() {
|
||||
assertEquals(WirelessDebugState.ENABLED, DeviceConnectionPolicy.debugState(1))
|
||||
assertEquals(WirelessDebugState.DISABLED, DeviceConnectionPolicy.debugState(0))
|
||||
assertEquals(WirelessDebugState.UNKNOWN, DeviceConnectionPolicy.debugState(2))
|
||||
assertEquals(WirelessDebugState.UNKNOWN, DeviceConnectionPolicy.debugState(null))
|
||||
}
|
||||
|
||||
@Test fun displayEnabledUsesPrimaryColor() {
|
||||
val d = DeviceConnectionPolicy.display(DeviceConnectionReading("192.168.0.93", WirelessDebugState.ENABLED))
|
||||
assertEquals("Wi-Fi IP:192.168.0.93", d.wifiText)
|
||||
assertEquals("无线调试:已开启", d.debugText)
|
||||
assertEquals(R.color.agent_primary_light, d.debugColor)
|
||||
}
|
||||
|
||||
@Test fun displayDisabledUsesWarningColorAndNoWifiText() {
|
||||
val d = DeviceConnectionPolicy.display(DeviceConnectionReading(null, WirelessDebugState.DISABLED))
|
||||
assertEquals("Wi-Fi IP:未连接 Wi-Fi", d.wifiText)
|
||||
assertEquals("无线调试:未开启", d.debugText)
|
||||
assertEquals(R.color.agent_warning, d.debugColor)
|
||||
}
|
||||
|
||||
@Test fun displayUnknownUsesMutedColor() {
|
||||
val d = DeviceConnectionPolicy.display(DeviceConnectionReading(null, WirelessDebugState.UNKNOWN))
|
||||
assertEquals("无线调试:无法读取", d.debugText)
|
||||
assertEquals(R.color.agent_text_muted, d.debugColor)
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
|
||||
wiki_page: Deployment-and-Operations
|
||||
wiki_url: https://git.ilapage.cn/OPC/goauto/wiki/Deployment-and-Operations.-
|
||||
wiki_revision: b50250e0d61eff0b33f468e7cdf74139fe76bb65
|
||||
synchronized_at: 2026-10-10T03:10:43Z
|
||||
wiki_revision: d08a0dfbb8356f9cd7212d495e4f11beca59e293
|
||||
synchronized_at: 2026-10-10T03:56:35Z
|
||||
<!-- gitea-wiki-mirror:end -->
|
||||
|
||||
<!-- gitea-wiki-mirror:start -->
|
||||
@@ -409,3 +409,13 @@ Server实现 `d84728d`、Android实现 `dbe7e98`,main 合并记录见 #378;
|
||||
- 本地Android验证(cwd仓库根,JDK17及现有Android SDK):`./android/gradlew.bat -p android :app:testDebugUnitTest --tests '*HeartbeatReport*Test' --tests '*AgentTransportTimeoutTest' --tests '*SyncDiagnostic*Test' --tests '*HandledRecoveryDiagnosticTest' --tests '*AgentDiagnosticStoreMigrationTest' --tests '*AgentSyncCycleTest' :app:assembleDebug :app:compileReleaseUnitTestKotlin`。发布前还应执行#377受影响的outbox、运行占位、互斥、重购等回归;SQLite JDBC验证不等同于真机升级。
|
||||
- Android兼容性测试在`android/build/diagnostics378-client-reports.json`生成纯合成HTTP捕获报告;在server目录将该绝对路径注入`GOAUTO_378_ANDROID_REPORTS`,执行`go test ./app/goauto/device -run TestAndroidGeneratedHeartbeatReports -count=1 -v`,使用真实validator/handler验证三类报告与重放。未设置路径时该项明确skip,不得据此宣称跨端通过;产物不提交Git。
|
||||
- 线上迁移、发布、重启、装机仍需单独授权;本地构建/夹具不代表真机或线上30分钟心跳验收。
|
||||
|
||||
|
||||
## #372 v3 导出店铺列的纯 Web 发布(2026-10-10)
|
||||
|
||||
- 用户授权合并 #372 v3 并部署线上。`feat/372-export-shop` 以 `--no-ff` 合并为 main `3d5e964`;Web 单元测试 93/93 通过,`build:prod` 通过。相对上一线上 Web(`4ad59bd`),Web 只有 v3 的导出改动。
|
||||
- **main 已含 #378 的 Server 代码和迁移,本次没有部署**:线上 Server 仍是 `72a15f8` 构建的二进制。#378 的 Server 发布和迁移需另行授权。
|
||||
- 发布目录 `/home/goauto/releases/20261010-3d5e964-372v3-web`,只替换 dist。goauto-server 与 config 从上一发布逐字复制(cmp/diff 一致),static/temp/var 沿用原目录,旧 js/css 哈希资源保留。index.html SHA256 `b900a40ec4691f0fb2c3d89e1e20826e7ca3205db311a33c24087767a515979a`。
|
||||
- 只原子切换 current,不重启 GoAuto/Nginx;切换前后 PID 4786 不变,服务 active。
|
||||
- 按内容验收:`/` 与 `/login` 返回新 index,10 项 JS/CSS 200,线上导出模块的 SYB 表头含「店铺」,health 200,验证码 code=200,未登录业务接口 401。登录后的实际导出待业务验收。
|
||||
- 回滚:current 原子切回 `/home/goauto/releases/20261010-4ad59bd-372v2-web`,不需要重启。
|
||||
|
||||
Reference in New Issue
Block a user