拼夕夕購物送錯(cuò)地址了,這是 bug 嗎?
大家好,我是君哥。
雖然在拼夕夕上購物不多,但每年會(huì)有幾次,每次都是看好商品后直接下單。不過這次有趣的是,訂單顯示已簽收,我才發(fā)現(xiàn)買的東西被送到了老東家的前臺(tái),這個(gè)公司是我 5 年前的公司。
為什么下單會(huì)下錯(cuò)呢?我每次購物都是直接下單,因?yàn)橐呀?jīng)很久沒有變過默認(rèn)收貨地址了,這次是因?yàn)槟J(rèn)地址被修改了才配送錯(cuò)誤。
圖片
1.默認(rèn)地址
對(duì)每個(gè)電商用戶來說,都會(huì)有幾個(gè)收貨地址,比如家庭住址、公司地址、老家地址、親戚家地址等。但都會(huì)有一個(gè)默認(rèn)地址,這個(gè)地址是最常用的一個(gè)收貨地址,一般不會(huì)修改。
那我的默認(rèn)地址為什么會(huì)被修改了呢?離開老東家已經(jīng) 5 年了,我修改默認(rèn)地址也不會(huì)改成這個(gè)地址啊。況且近兩年我每次在夕夕上買東西都是直接下單。
那一個(gè)地址該怎樣保存呢?拼夕夕上添加一個(gè)地址如下圖:
圖片
一個(gè)地址的數(shù)據(jù)大概包括:省、市、區(qū)/縣、姓名、電話、街道/詳細(xì)地址,然后這些數(shù)據(jù)需要關(guān)聯(lián)用戶id。我們可以設(shè)計(jì)一張“用戶收貨地址”表:
CREATE TABLE`user_address` (
`id`BIGINT PRIMARY KEY AUTO_INCREMENT, -- 主鍵 ID
`user_id`BIGINTNOTNULL, -- 用戶 ID
`user_name`VARCHAR(50) NOTNULL, -- 收貨人姓名
`phone`VARCHAR(20) NOTNULL, -- 收貨人電話
`country`VARCHAR(50) NOTNULL, -- 國家(支持國際化)
`province`VARCHAR(50) NOTNULL, -- 省
`city`VARCHAR(50) NOTNULL, -- 市
`district`VARCHAR(50) DEFAULTNULL, -- 區(qū)/縣
`street`VARCHAR(255) NOTNULL, -- 街道/詳細(xì)地址
`postal_code`VARCHAR(20) DEFAULTNULL, -- 郵編
`is_default`TINYINT(1) DEFAULT 0, -- 是否默認(rèn)地址(0否,1是)
`create_date` DATETIME DEFAULTCURRENT_TIMESTAMP, --創(chuàng)建時(shí)間
`update_date` DATETIME ONUPDATECURRENT_TIMESTAMP,--更新時(shí)間
`is_deleted`TINYINT(1) DEFAULT 0 -- 邏輯刪除
);
2.修改原因
上面用戶收貨地址表我們關(guān)聯(lián)了 user_id, 同時(shí)用 is_default 字段用來標(biāo)識(shí)是否是默認(rèn)地址。這樣只有當(dāng)前用戶在 app 上操作更改時(shí)才能修改這個(gè)地址。排除賬號(hào)盜用、他人操作我的賬戶,那這個(gè)地址突然被修改可能是什么原因呢?
2.1 三方應(yīng)用
有一種可能,手機(jī)上的三方應(yīng)用同步信息到拼多多,導(dǎo)致了默認(rèn)地址被修改。
2.2 數(shù)據(jù)庫同步
數(shù)據(jù)庫增加新節(jié)點(diǎn),假設(shè) MySQL 一主兩從的集群架構(gòu),集群中一個(gè)節(jié)點(diǎn)數(shù)據(jù)正在同步,但請(qǐng)求已經(jīng)發(fā)到新節(jié)點(diǎn)上,如下圖:
圖片
加入 binlog 中最新的兩條改動(dòng) SQL 如下:
--把老公司地址改為默認(rèn)地址
UPDATE user_address SET is_default = 1 WHERE user_id = {userId} AND id = 1;
--把家庭住址改為默認(rèn)地址
UPDATE user_address SET is_default = 1 WHERE user_id = {userId} AND id = 2;
這時(shí)如果剛剛同步完成第 1 條 SQL,第 2 條 SQL 還沒有同步完成,用戶請(qǐng)求過來了,這時(shí)取到的默認(rèn)地址肯定是錯(cuò)誤的。
2.3 歷史數(shù)據(jù)
假如 user_address 表設(shè)計(jì)之初沒有 is_default 這個(gè)字段,后來業(yè)務(wù)發(fā)展過程中,產(chǎn)品經(jīng)理發(fā)現(xiàn)這個(gè)默認(rèn)地址非常必要,就提出增加這個(gè)字段,而且是必輸字段。作為程序員,歷史數(shù)據(jù)怎樣處理呢?
- 隨便取表中一條記錄作為客戶的默認(rèn)地址,這個(gè)設(shè)計(jì)最簡單,但也很不負(fù)責(zé)任,選的地址可能并不是客戶想要的默認(rèn)地址;
- 取最新更新的一條記錄作為客戶的默認(rèn)地址,這個(gè)刷數(shù)策略看似比較合理,但是也有問題。插入數(shù)據(jù)時(shí) update_date 給的是系統(tǒng)時(shí)間,那 binlog 同步時(shí),多條記錄的 update_date 時(shí)間可能很接近,如果程序根據(jù)時(shí)間來判斷只精確到秒級(jí),這樣多條記錄更新時(shí)間一樣 ,只能隨機(jī)選擇一條作為默認(rèn)地址。
2.4 人為錯(cuò)誤
程序員解決別的問題時(shí),引入了 bug,把我的默認(rèn)地址給修改了。
3.其他注意
設(shè)計(jì)客戶收貨地址時(shí),還有其他幾個(gè)點(diǎn)需要注意:
- 手機(jī)號(hào)、郵政編碼合規(guī)校驗(yàn);
- 地址省、市、區(qū)/縣標(biāo)準(zhǔn)化;
- 詳細(xì)地址合法性,驗(yàn)證這個(gè)地址是否真的存在;
- 訂單表冗余地址信息,而不是關(guān)聯(lián) id,這樣可以確保收貨地址被修改后,訂單信息的地址保持不變;
- 敏感數(shù)據(jù)加密,比如傳輸加密、日志脫敏,包括:姓名、住址、手機(jī)號(hào)等;
- 地址修改做權(quán)限控制,每個(gè)用戶只能操作自己地址。
從用戶體驗(yàn)方面看,可以考慮下面幾點(diǎn):
- 對(duì)于查詢頻率高的地址,做緩存,提高查詢性能;
- 智能地址補(bǔ)全,用戶輸入體驗(yàn)更好;
- 增加地址標(biāo)簽,比如家、公司、父母家等。
4.總結(jié)
一個(gè)收貨地址設(shè)計(jì),表面看很簡單,但要考慮安全、性能、可擴(kuò)展和用戶體驗(yàn),還是有很多內(nèi)容的。