戰(zhàn) 30】雙方確認(rèn)后才能結(jié)案:如何聯(lián)動(dòng) Claim 與兩條 Report 狀態(tài))
【尋跡校園 HarmonyOS NEXT 實(shí)戰(zhàn) 30】雙方確認(rèn)后才能結(jié)案如何聯(lián)動(dòng) Claim 與兩條 Report 狀態(tài)這是“尋跡校園 HarmonyOS NEXT 實(shí)戰(zhàn)”系列第 30 篇。本文結(jié)合HandoffService.confirmCompletion()、ClaimService.complete()、HandoffParty與本地回歸測(cè)試說(shuō)明為什么一方確認(rèn)只能記錄進(jìn)度第二方確認(rèn)后才能完成 Handoff、Claim并聯(lián)動(dòng)拾得與來(lái)源丟失兩條 Report。上圖為原創(chuàng)生成的雙確認(rèn)結(jié)案插畫不是應(yīng)用截圖。CLAIMANT與KEEPER兩個(gè)完成信號(hào)匯合后業(yè)務(wù)才進(jìn)入COMPLETED并繼續(xù)更新 Claim 和關(guān)聯(lián) Report。一、為什么一方點(diǎn)擊“完成”不能直接結(jié)案線下交接存在兩個(gè)不同事實(shí)拾得者確認(rèn)已經(jīng)交出物品申請(qǐng)者確認(rèn)已經(jīng)收到物品。如果任意一方單擊后就把記錄永久關(guān)閉可能出現(xiàn)“拾得者到了但沒(méi)有見(jiàn)到對(duì)方”“申請(qǐng)者拿到的不是目標(biāo)物品”“一方誤點(diǎn)完成”等爭(zhēng)議。雙方確認(rèn)不是形式上的兩個(gè)按鈕而是結(jié)案條件的兩個(gè)獨(dú)立證據(jù)。二、先區(qū)分“安排確認(rèn)”和“完成確認(rèn)”交接流程有兩層確認(rèn)claimantConfirmed / keeperConfirmed雙方同意地點(diǎn)和時(shí)間claimantCompleted / keeperCompleted雙方確認(rèn)實(shí)際交接已經(jīng)發(fā)生。安排確認(rèn)只讓 Handoff 進(jìn)入CONFIRMED。只有完成確認(rèn)同時(shí)為真才進(jìn)入COMPLETED。把這兩層混在一個(gè)布爾值里會(huì)讓“同意去圖書(shū)館見(jiàn)面”被誤解為“已經(jīng)收到物品”。三、HandoffParty 讓調(diào)用意圖顯式化項(xiàng)目用枚舉表達(dá)確認(rèn)方exportenumHandoffParty{CLAIMANTCLAIMANT,KEEPERKEEPER}confirmCompletion(claimId, party)根據(jù)角色只更新一方標(biāo)記。相比傳入兩個(gè)任意布爾值枚舉更容易審查也能避免調(diào)用方一次篡改雙方結(jié)果。四、只有 CONFIRMED 才允許確認(rèn)完成Service 首先讀取 Handoff 并檢查if(!record||record.status!HandoffStatus.CONFIRMED){returnnewOperationResultHandoffRecord(false,雙方確認(rèn)安排后才能確認(rèn)已交接);}PROPOSED、RESCHEDULED、CANCELLED、EXPIRED和COMPLETED都不能進(jìn)入這條寫入路徑。這防止用戶繞過(guò)地點(diǎn)確認(rèn)也避免已結(jié)束流程被舊頁(yè)面重新修改。五、第一次完成確認(rèn)為什么仍保持 CONFIRMED如果只有claimantCompleted或keeperCompleted為真Service 保存記錄但不改變 Handoff 狀態(tài)。返回文案是已記錄一方確認(rèn)等待另一方確認(rèn)。頁(yè)面繼續(xù)展示兩行獨(dú)立進(jìn)度已確認(rèn)一方按鈕禁用另一方仍可操作。用戶能看到流程在前進(jìn)但不會(huì)誤以為已經(jīng)結(jié)案。六、第二次確認(rèn)如何觸發(fā) COMPLETEDService 每次更新角色標(biāo)記后檢查if(record.claimantCompletedrecord.keeperCompleted){record.statusHandoffStatus.COMPLETED;}只有兩個(gè)條件同時(shí)成立保存后的 Handoff 才是COMPLETED。隨后 Service 調(diào)用claimService.complete(claimId)把結(jié)果擴(kuò)散到認(rèn)領(lǐng)和報(bào)告聚合。上圖展示第二方確認(rèn)后的級(jí)聯(lián)路徑Handoff 完成只是第一步Claim 和兩條關(guān)聯(lián) Report 全部更新后用戶才應(yīng)看到完整結(jié)案結(jié)果。七、Claim 完成為什么要求原狀態(tài)為 ACCEPTEDClaimService.complete()只接受ACCEPTEDClaim。若申請(qǐng)已取消、拒絕、過(guò)期或完成調(diào)用會(huì)失敗。這條前置條件保證 Handoff 不能把一個(gè)已經(jīng)失效的認(rèn)領(lǐng)重新結(jié)案。Handoff 和 Claim 的狀態(tài)必須相互兼容而不是誰(shuí)最后寫入誰(shuí)覆蓋。八、為什么要更新兩條 Report認(rèn)領(lǐng)目標(biāo)是FOUND拾得記錄可選的sourceReportId指向申請(qǐng)者自己的LOST丟失記錄。交接完成后拾得報(bào)告已經(jīng)找到失主應(yīng)為RESOLVED來(lái)源丟失報(bào)告已經(jīng)找回物品也應(yīng)為RESOLVEDClaim 為COMPLETEDHandoff 為COMPLETED。如果只關(guān)閉拾得記錄失主自己的丟失記錄仍會(huì)出現(xiàn)在候選列表繼續(xù)制造無(wú)效匹配。九、沒(méi)有 sourceReportId 時(shí)怎樣處理用戶也可能從拾得詳情直接發(fā)起認(rèn)領(lǐng)沒(méi)有關(guān)聯(lián)自己的丟失記錄。此時(shí)sourceReportId為空。Service 始終結(jié)案目標(biāo)拾得報(bào)告只在來(lái)源 ID 非空時(shí)更新第二條報(bào)告awaitreportService.markResolved(current.reportId);if(current.sourceReportId.length0){awaitreportService.markResolved(current.sourceReportId);}可選關(guān)聯(lián)不能阻止主流程完成也不能用空字符串查詢 Repository。十、結(jié)案后為什么禁止取消HandoffService.cancel()明確拒絕COMPLETED、CANCELLED和EXPIRED狀態(tài)。完成代表雙方已經(jīng)確認(rèn)實(shí)際交接并且關(guān)聯(lián)記錄可能已經(jīng)從候選列表移除。普通取消按鈕不應(yīng)反向打開(kāi)這些實(shí)體。如果正式產(chǎn)品需要爭(zhēng)議申訴應(yīng)新增獨(dú)立申訴流程和審計(jì)記錄而不是復(fù)用“取消安排”把歷史狀態(tài)倒退。十一、ArkUI 頁(yè)面如何表達(dá)單機(jī)角色模擬HandoffPage提供兩個(gè)按鈕“模擬拾得者確認(rèn)已交出”“模擬失主確認(rèn)已收到”。頁(yè)面頂部明確說(shuō)明這是單機(jī)比賽演示正式版本需要兩個(gè)已驗(yàn)證身份分別完成。這段文案非常重要。兩個(gè)按鈕出現(xiàn)在同一臺(tái)設(shè)備上只能證明狀態(tài)機(jī)和 UI 鏈路不能證明真實(shí)雙賬號(hào)簽名、遠(yuǎn)程通知或防抵賴能力。十二、本地測(cè)試驗(yàn)證了完整級(jí)聯(lián)回歸測(cè)試按真實(shí)順序執(zhí)行創(chuàng)建并接受 Claim使用固定地點(diǎn)發(fā)起 Handoff確認(rèn)交接安排CLAIMANT首次確認(rèn)完成斷言 Handoff 仍為CONFIRMED斷言 Claim 仍為ACCEPTEDKEEPER第二次確認(rèn)完成斷言 Handoff 與 Claim 均為COMPLETED斷言目標(biāo)和來(lái)源 Report 均為RESOLVED斷言完成后取消失敗。powershell-ExecutionPolicy Bypass-File.\scripts\test-local-state-machines.ps1這組斷言把“按鈕點(diǎn)擊成功”升級(jí)為跨實(shí)體結(jié)果驗(yàn)證。十三、當(dāng)前寫入順序存在重要的部分失敗窗口現(xiàn)有實(shí)現(xiàn)先把 Handoff 保存為COMPLETED再調(diào)用ClaimService.complete()。如果后續(xù) Claim 或 Report 更新失敗方法會(huì)返回失敗但 Handoff 可能已經(jīng)是COMPLETED。用戶再次調(diào)用confirmCompletion()時(shí)又會(huì)因?yàn)闋顟B(tài)不再是CONFIRMED而被拒絕。這是一個(gè)真實(shí)的一致性風(fēng)險(xiǎn)錯(cuò)誤提示存在不代表自動(dòng)補(bǔ)償已經(jīng)完成。十四、為什么不能把 try/catch 寫成“事務(wù)”try/catch能捕獲異常并映射用戶文案卻不會(huì)自動(dòng)回滾已經(jīng)成功的 Repository 寫入。當(dāng)前跨實(shí)體順序大致為順序?qū)懭?Handoff →COMPLETED2Claim →COMPLETED3FOUND Report →RESOLVED4可選 LOST Report →RESOLVED任何后一步失敗都可能留下前面已提交的數(shù)據(jù)。準(zhǔn)確表述應(yīng)是“順序協(xié)調(diào)并返回部分失敗”而不是“事務(wù)性結(jié)案”。十五、正式版怎樣實(shí)現(xiàn)原子或可補(bǔ)償結(jié)案服務(wù)端可以選擇在同一事務(wù)中更新 Handoff、Claim 和兩條 Report為每條記錄增加版本號(hào)阻止并發(fā)舊寫使用結(jié)案命令 ID 保證重復(fù)請(qǐng)求冪等寫入 outbox 事件再由可靠消費(fèi)者補(bǔ)齊派生狀態(tài)對(duì)部分失敗記錄建立補(bǔ)償任務(wù)管理端顯示不一致告警保留雙方確認(rèn)時(shí)間和身份審計(jì)申訴流程不直接回滾歷史完成狀態(tài)??蛻舳酥粦?yīng)展示服務(wù)端確認(rèn)后的權(quán)威結(jié)果。十六、結(jié)案后的頁(yè)面刷新應(yīng)來(lái)自權(quán)威數(shù)據(jù)完成后onDataChanged()增加共享dataRevision首頁(yè)、消息和“我的發(fā)布”重新讀取 Service 數(shù)據(jù)。頁(yè)面不應(yīng)該手工把多個(gè)卡片改成“已完成”。從 Repository 重新聚合能避免某一頁(yè)面漏改來(lái)源 Report也便于進(jìn)程重啟后恢復(fù)一致視圖。十七、消息文案要區(qū)分三個(gè)階段用戶至少會(huì)看到三種不同結(jié)果一方已確認(rèn)等待另一方雙方已確認(rèn)所有實(shí)體結(jié)案成功Handoff 已完成但后續(xù)聯(lián)動(dòng)失敗需要重新進(jìn)入檢查。把它們?nèi)繉懗伞安僮鞒晒Α睍?huì)掩蓋最關(guān)鍵的一致性問(wèn)題。十八、真機(jī)與多用戶驗(yàn)收仍缺什么當(dāng)前本地測(cè)試和單機(jī)頁(yè)面可以證明主要業(yè)務(wù)規(guī)則但正式驗(yàn)收還需要兩個(gè)真實(shí)賬號(hào)分別確認(rèn)服務(wù)端拒絕同一賬號(hào)代替雙方并發(fā)雙擊與重試保持冪等一方離線后恢復(fù)推送和消息狀態(tài)一致進(jìn)程重啟后確認(rèn)標(biāo)記不丟失部分失敗可以自動(dòng)補(bǔ)償審計(jì)日志不泄露私密核驗(yàn)答案申訴與爭(zhēng)議不會(huì)靜默篡改歷史記錄。這些均不能由當(dāng)前同設(shè)備角色模擬外推。工程復(fù)盤多實(shí)體結(jié)案先列出最小一致性清單一次完整結(jié)案至少要回答五個(gè)問(wèn)題Handoff 是否已經(jīng)由雙方確認(rèn)完成Claim 是否從允許的狀態(tài)進(jìn)入COMPLETED目標(biāo)拾得 Report 是否進(jìn)入RESOLVED存在來(lái)源丟失 Report 時(shí)它是否同步結(jié)束重復(fù)調(diào)用是否仍返回同一個(gè)業(yè)務(wù)結(jié)果。把這五項(xiàng)寫成清單比在頁(yè)面成功回調(diào)里零散修改對(duì)象更容易審查。順序?qū)懭雽?shí)現(xiàn)還要為每一步指定失敗后的動(dòng)作。第一方確認(rèn)只改變 Handoff 進(jìn)度不觸發(fā)其余實(shí)體第二方確認(rèn)觸發(fā)級(jí)聯(lián)時(shí)任何部分失敗都應(yīng)保留操作 ID 和已完成步驟下一次重試從權(quán)威狀態(tài)繼續(xù)而不是從頭把終態(tài)記錄倒退。當(dāng)前單機(jī) Repository 沒(méi)有跨表事務(wù)所以文章只報(bào)告本地順序?qū)懭牒蜏y(cè)試覆蓋并把原子性明確列為未實(shí)現(xiàn)邊界。讀取側(cè)也需要一致性保護(hù)。列表、詳情和消息頁(yè)應(yīng)在結(jié)案后統(tǒng)一刷新 Service 快照如果發(fā)現(xiàn) Handoff 已完成但 Report 仍可認(rèn)領(lǐng)應(yīng)顯示可恢復(fù)錯(cuò)誤并觸發(fā)受控補(bǔ)償不能讓兩個(gè)頁(yè)面長(zhǎng)期展示相反結(jié)論。日志只記錄實(shí)體 ID、狀態(tài)遷移和補(bǔ)償結(jié)果私密核驗(yàn)答案不進(jìn)入結(jié)案審計(jì)。最終驗(yàn)收應(yīng)覆蓋順序和重復(fù)拾得者先確認(rèn)、失主后確認(rèn)反向順序兩方重復(fù)點(diǎn)擊第一方確認(rèn)后重啟應(yīng)用第二方確認(rèn)時(shí)其中一條關(guān)聯(lián)報(bào)告缺失。每個(gè)案例都要同時(shí)斷言四類實(shí)體的權(quán)威狀態(tài)和用戶可見(jiàn)文案才能證明“雙方確認(rèn)”不是一個(gè)局部按鈕效果。十九、本文小結(jié)雙方確認(rèn)結(jié)案的核心是把“安排已同意”“拾得者已交出”“失主已收到”拆成獨(dú)立事實(shí)。第一方完成確認(rèn)只記錄進(jìn)度第二方確認(rèn)后才觸發(fā) Handoff、Claim 和關(guān)聯(lián) Report 的聯(lián)動(dòng)結(jié)案?!皩ほE校園”當(dāng)前已經(jīng)用生產(chǎn) Service 和本地回退測(cè)試證明主要級(jí)聯(lián)與取消限制但跨 Repository 原子事務(wù)、真實(shí)雙賬號(hào)身份、服務(wù)端審計(jì)和部分失敗補(bǔ)償仍是正式版本必須解決的工程問(wèn)題。系列導(dǎo)航第 30 篇 / 共 50 篇。上一篇《一次改期與 24 小時(shí)過(guò)期》下一篇《舉報(bào)去重與治理狀態(tài)機(jī)》。