上一轮只是"能编译"。这一轮在模拟器上真装了、真登录、真点了,才发现两个问题:
1. workspaces() / members() 直接按 List 解析,但服务端列表接口返回的是
{"items": [...]}。表现就是登录成功后首页红字「响应格式异常(HTTP 200)」,
而且工作区永远显示为空。新增 WorkspacePage / MemberPage 容器类型。
2. 之前 Api.kt 的请求体全是没加 @Serializable 的普通 data class(上一提交已修),
这次把成员/工作区的解析也按同样思路收口。
环境侧(写进 android/README + 新增 android/scripts/emulator.ps1):
- sdkmanager 拉不到远端索引,系统镜像是直连 dl.google.com 手动装的;
压缩包里多一层 x86_64/,要把内容提上一层,否则 AGP 找不到 system.img
- AVD 放工程内(~/.android 受限不可写),建 AVD 与启动都靠 ANDROID_USER_HOME / ANDROID_AVD_HOME
- 无头启动要 -no-window -gpu swiftshader_indirect
- 点按坐标要拿 uiautomator dump 的 bounds,别拿降采样截图猜
- adb shell input text 打不了中文(NPE),中文数据走接口或直接写库
验证(模拟器 API 35,连 fnsvr-01 的真库):
登录 → 建工作区 ShipFast → 任务列表显示 4 条中文任务(状态、截止日都对)
→ 勾选完成,条目从「进行中」消失(乐观锁生效)→ 工单页、知识页均正常渲染。
截图放进 2.Design/screens/android-{login,tasks}.png。
This commit is contained in:
@@ -163,6 +163,21 @@ data class TicketPage(
|
||||
@SerialName("has_more") val hasMore: Boolean = false,
|
||||
)
|
||||
|
||||
/** 工作区/成员列表的外层容器:服务端统一返回 `{"items": [...]}`。 */
|
||||
@Serializable
|
||||
data class WorkspacePage(
|
||||
val items: List<Workspace> = emptyList(),
|
||||
@SerialName("next_cursor") val nextCursor: String? = null,
|
||||
@SerialName("has_more") val hasMore: Boolean = false,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MemberPage(
|
||||
val items: List<Member> = emptyList(),
|
||||
@SerialName("next_cursor") val nextCursor: String? = null,
|
||||
@SerialName("has_more") val hasMore: Boolean = false,
|
||||
)
|
||||
|
||||
/** 知识的附带信息,与 backend/app/api/knowledge.py 的 `_doc_out` 对齐。 */
|
||||
@Serializable
|
||||
data class KnowledgeInfo(
|
||||
@@ -293,7 +308,13 @@ class Api(private val tokens: TokenStore) {
|
||||
|
||||
suspend fun me(): MeResult = call(path = "/auth/me", auth = true)
|
||||
|
||||
suspend fun workspaces(): List<Workspace> = call(path = "/workspaces", auth = true)
|
||||
/**
|
||||
* 列表类接口服务端返回的是 `{"items": [...]}` 而不是裸数组
|
||||
* (`backend/app/api/workspaces.py` 的 list_workspaces),
|
||||
* 直接按 List 解析会得到 "响应格式异常(HTTP 200)"。
|
||||
*/
|
||||
suspend fun workspaces(): List<Workspace> =
|
||||
call<WorkspacePage>(path = "/workspaces", auth = true).items
|
||||
|
||||
suspend fun createWorkspace(name: String): Workspace = call(
|
||||
path = "/workspaces",
|
||||
@@ -302,7 +323,7 @@ class Api(private val tokens: TokenStore) {
|
||||
)
|
||||
|
||||
suspend fun members(workspaceId: String): List<Member> =
|
||||
call(path = "/workspaces/$workspaceId/members", auth = true)
|
||||
call<MemberPage>(path = "/workspaces/$workspaceId/members", auth = true).items
|
||||
|
||||
suspend fun createInvitation(workspaceId: String): Invitation =
|
||||
call(path = "/workspaces/$workspaceId/invitations", body = "{}", auth = true)
|
||||
|
||||
Reference in New Issue
Block a user