記一次業餘項目的敏捷開發實踐

  • 2020 年 3 月 15 日
  • 筆記

      本次是在原有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  }

詳細設計

登入接口詳細設計

  • 流程
    image
  • 在線用戶管理
    • 在線用戶管理接口類
    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字符串到對象的轉換。

登出接口詳細設計

  • 流程

image

五、數據字典

  • 在線用戶信息

image

  • 用戶

image