记一次业余项目的敏捷开发实践

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

posted @ 2020-03-15 19:33  小龙女先生  阅读(960)  评论(3编辑  收藏  举报