1. 程式人生 > >記一次業餘專案的敏捷開發實踐

記一次業餘專案的敏捷開發實踐

      本次是在原有ApiTemplate專案之上,增加一個使用者登入許可權控制模組,用於驗證ApiTemplate專案在面對一些簡單問題時,如何抽象並支援未來的擴充套件。使用者登入許可權控制模組看上去很簡單,但由於業餘時間總是有限的。所以藉助此機會實踐一次使用者敏捷開發。首先拆分模組,本次只實現使用者登入和登出。

apitemplate專案地址:https://github.com/cqhaibin/ApiTemplate

一、總結放前面

最小化任務範圍

  • 本次任務只限定在了《使用者名稱+密碼登入》這個任務上,並且不包含資料的持久化, 這樣在做的時候反覆考查自己,不讓自己超出範圍。所以
  • 查詢使用者註冊資訊、線上使用者儲存相關介面只做定義和模擬實現,不做具體的儲存實現
  • 考慮到業務邏輯是穩定的,而儲存是可變的,所以資料庫實體物件與業務實體物件分離

給任務一個期限

像本次就只列出了任務的期限,而沒有列出每個子階段的期限,如:一個需求必須要經過需求分析、模組設計、程式碼實現等階段。這些子階段也需要給出具體的期限。

從外向裡逐層推進

  • 定義UI/服務層介面
    因為UI介面有多種提供方式(如:rest api, rpc等),所以基本以服務層介面為標準,UI介面層只是做了一次簡單轉換和呼叫。其中UI/服務層介面輸入/輸出引數的Moddel也隨之定義(兩層共享Model)
  • 實現服務層介面
    此步實現服務層介面,你會發現還需要依賴線上使用者管理模組,以及資料庫層(查詢註冊使用者資訊),在這裡我只定義了查詢註冊使用者資訊的介面,而暫不做具體的實現。然後進入第三步
  • 定義線上使用者模組的介面
    此步包含:線上使用者管理實體介面、線上使用者實體介面。定義好後先不實現。完善服務層實現中對此模組的依賴呼叫,在這裡你可能會反覆調整線上使用者模組的方法輸入/輸出引數的Model,以達到與服務層的融合
  • 實現線上使用者模組的介面
    此步實現 線上使用者管理實體介面、線上使用者實體介面。此時我們發現還要依賴線上使用者儲存介面(只定義,不做實現)

二、使用者需求

實現根據使用者名稱的登入、登出介面。

三、需求分析

  • 使用者名稱:支援英文、數字、漢字、以及特殊字元;使用者名稱不區分大小寫
  • 密碼:支援英文、數字、特殊字元,區分大小寫
  • 提示:使用者不存在與密碼錯誤要區分提示
  • 此階段不考慮資料持久化,因為要快速驗證原型的可行性

四、系統設計

介面設計

介面統一使用rest api, 實現登入、登出兩個介面

  • 登入介面
    • 介面名:PostLogin
    • 請求型別:post
    • 輸入引數
    {
        userName<string>, //使用者名稱
        password<string> //密碼
    }
    • 返回引數
    {
        isSuccess<bool>, //請求是否成功
        resultCode<number>, //請求狀態Code 200006:賬號不存在;200001:賬號被禁用;200002:密碼錯誤
        data<object>:{
            token<string> //登入成功後,返回的token
            user<object>:{ //使用者物件
                realName<string>, //使用者名稱
                userName<string>, //登入名
                id<int>, //使用者Id
                config<string>, //使用者擴充套件資訊,json字串
                mobilePhone<string>, //電話號碼
            }
        }
    }
  • 登出介面
    • 介面名稱:LoginOut
    • 請求型別:get
    • 輸入引數
      通過url, header, cookie的順序獲取token
    • 返回引數
    {
        isSuccess<bool>, //請求是否成功
        resultCode<number>, //請求狀態Code
    }

詳細設計

登入介面詳細設計

  • 流程
  • 線上使用者管理
    • 線上使用者管理介面類
    class IOnlineUserMgr{
        /// <summary>
        /// 將使用者新增到線上使用者列表,此方法需要對登入資訊持久化
        /// </summary>
        /// <param name="entity"></param>
        void Add(IUserEntity entity);
        /// <summary>
        /// 根據token移除對應的使用者,此方法需要對登出資訊持久化
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        bool Remove(string token);
        /// <summary>
        /// 根據使用者Id移除使用者,此方法需要對登出資訊持久化
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        bool Remove(int id);
        /// <summary>
        /// 從持久化層恢復線上使用者
        /// </summary>
        void Load();
        /// <summary>
        /// 獲取所有線上使用者
        /// </summary>
        IList<IUserEntity>  GetAll();
    
        IUserEntity Get(int userId);
    }
    • 使用者實體介面類
    class IUserEntity{
        UserInfo UserInfo { get; }
    
        string Token { get; }
    
        /// <summary>
        /// 客戶端資訊
        /// </summary>
        RequestClientInfo ClientInfo { get; }
    
        DateTime LoginTime { get; }
    
        DateTime ExpiredTime { get; }
        /// <summary>
        /// 使用者登入配置
        /// </summary>
        UserAuthOption Option { get; }
    
        TokenEntity GetTokenEntity();
    }
  • 說明
    • token生成規則
      使用者key = token_UserId_UserName_IP_OS_Time,然後將使用者key通過MD5計算出的值作為token
    • UAParser
      實現UserAgent字串到物件的轉換。

登出介面詳細設計

  • 流程

五、資料字典

  • 線上使用者資訊

  • 使用者