记一次业余项目的敏捷开发实践
程序员文章站
2022-05-29 08:42:23
本次是在原有ApiTemplate项目之上,增加一个用户登录权限控制模块,用于验证ApiTemplate项目在面对一些简单问题时,如何抽象并支持未来的扩展。用户登录权限控制模块看上去很简单,但由于业余时间总是有限的。所以借助此机会实践一次用户敏捷开发。首先拆分模块,本次只实现用户登录和登出。apit ......
本次是在原有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字符串到对象的转换。
- token生成规则
登出接口详细设计
- 流程
五、数据字典
- 在线用户信息
- 用户