在我这个系列中,我主要以我正在开发的云会员管理系统为例进行介绍Web API的应用,由于云会员的数据设计是支持多个商家公司,而每个公司又可以包含多个店铺的,因此一些字典型的数据需要考虑这方面的不同。如对于证件类型,收费处理状态,民族,职称等这些固定化的内容,我们可以放到全局字典里面,但是对于一些如会员相关的字典数据,如产品单位、产品类型等内容,如果也全部规定为全局的系统字典,那么就缺乏灵活性,这些数据应该可以由各自进行差异化处理。
1、云会员系统的字典数据模型
我们先来了解下基于Web API接口的云会员管理系统的总体界面效果。
由于一般的云会员系统,都是允许用户注册一个公司,然后公司层面开设多个商铺的,如系统的登陆界面如下所示。
因此数据的范围需要考虑的更广,他们的关系如下所示。
而我们原先设计的字典模型如下所示。
而在公司数据这个层次上,我们需要考虑公司层级的数据字典存储,但是我们进一步分析可以看到,虽然数据字典数据是公司层级的,但是数据字典的类型(如证件类型、产品类型等)这些是固定不变的,也就是我们如果存储公司层级的字典数据,那么也只是需要存储对应的字典项目即可。因此我们可以增加多一个和TB_DictData的数据表类似的表进行存储即可,它的数据设计如下所示。
为了方便在系统里面使用同一的字典项目内容,我们创建一了一个统一的字典项目管理模块,也就是系统字典管理界面,如下所示。
2、公司层级的字典数据存储实现
有了上面的设计模型,相信大多数人员都可以想到它的具体实现思路了。
首先我们需要以系统字典数据为参考,如默认就是取系统的字典项目数据,如果公司级别的用户修改或者删除了字典数据内容,那么对应的字典类别的字典项目就应该以修改的为准了。
但是我们不可能为新建公司账户的时候,都为每个公司自动创建一份对应类型的字典数据,那样稍显麻烦,而且一开始就创建也比较麻烦。
先建立一个公司字典的数据管理界面,它和字典数据管理界面一样,不过是存储在另外一个表里面,自动根据当前用户的公司标识进行存储的。
批量添加公司字典的数据如下所示。
一般我们在使用公司层级的字典数据或者系统公共层级的字典数据的时候,都是根据字典类型进行判断的。
因此在公司层级根据字典项目类型获取数据的时候,我们在业务接口底层做了判断,判断如果对应公司的字典项没有数据,则复制一份过去,如果公司层次有对应的数据类型,那么就获取公司层级的字典项目数据即可。
具体的代码逻辑如下所示。
/// <summary> /// 根据字典类型名称获取所有该类型的字典列表集合 /// </summary> /// <param name="dictType">字典类型名称</param> /// <param name="corpId">公司ID</param> /// <returns></returns> public List<CorpDictDataInfo> FindByDictType(string dictTypeName, string corpId) { ICorpDictData dal = baseDal as ICorpDictData; List<CorpDictDataInfo> list = dal.FindByDictType(dictTypeName, corpId); //如果公司字典没有数据,则从系统字典获取 if (list.Count == 0) { List<DictDataInfo> dict = BLLFactory<DictData>.Instance.FindByDictType(dictTypeName); foreach (DictDataInfo info in dict) { list.Add(new CorpDictDataInfo(info, corpId)); } //写入公司字典表,避免下次再去获取 foreach (CorpDictDataInfo info in list) { baseDal.Insert(info); } } return list; }
在Web API的控制器接口,还是和其他的处理一样,增加对应的参数处理即可。
/// <summary> /// 根据字典类型名称获取所有该类型的字典列表集合 /// </summary> /// <param name="dictType">字典类型名称</param> /// <param name="corpId">公司ID</param> /// <returns></returns> [HttpGet] public List<CorpDictDataInfo> FindByDictType(string dictTypeName, string corpId, string token) { //令牌检查,不通过则抛出异常 CheckResult checkResult = CheckToken(token); return BLLFactory<CorpDictData>.Instance.FindByDictType(dictTypeName, corpId); }
在Facade层定义字典的对应接口的时候,我们的代码如下所示
/// <summary> /// 根据字典类型名称获取所有该类型的字典列表集合 /// </summary> /// <param name="dictType">字典类型名称</param> /// <param name="corpId">公司ID</param> /// <returns></returns> [OperationContract] List<CorpDictDataInfo> FindByDictType(string dictTypeName, string corpId);
在基于Web API的封装调用接口,我们的调用封装类如下所示。其中token以及Web API的相关参数处理,在基类模块进行了封装,减少了很多代码的拼接。
/// </summary> /// <param name="dictType">字典类型名称</param> /// <param name="corpId">公司ID</param> /// <returns></returns> public List<CorpDictDataInfo> FindByDictType(string dictTypeName, string corpId) { var action = "FindByDictType"; string url = GetTokenUrl(action) + string.Format("&dictTypeName={0}&corpId={1}", dictTypeName, corpId); List<CorpDictDataInfo> result = JsonHelper<List<CorpDictDataInfo>>.ConvertJson(url); return result; }
然后我们在界面上的字典项目下拉列表,则可以通过扩展函数的方式进行绑定。
/// <summary> /// 初始化字典列表内容 /// </summary> private void InitDictItem() { //初始化代码 this.txtProductType.BindDictItemsByCorp("会员产品类型", LoginUserInfo.CompanyId); }
/// <summary> /// 绑定下拉列表控件为指定的数据字典列表[如果公司字典记录不存在,则使用系统字典记录,否则使用公司记录] /// </summary> /// <param name="combo">下拉列表控件</param> /// <param name="dictTypeName">数据字典类型名称</param> public static void BindDictItemsByCorp(this ComboBoxEdit combo, string dictTypeName, string corpId) { BindDictItemsByCorp(combo, dictTypeName, corpId, null); } /// <summary> /// 绑定下拉列表控件为指定的数据字典列表[如果公司字典记录不存在,则使用系统字典记录,否则使用公司记录] /// </summary> /// <param name="combo">下拉列表控件</param> /// <param name="dictTypeName">数据字典类型名称</param> /// <param name="defaultValue">控件默认值</param> public static void BindDictItemsByCorp(this ComboBoxEdit combo, string dictTypeName, string corpId, string defaultValue) { Dictionary<string, string> dict = CallerFactory<ICorpDictDataService>.Instance.GetDictByDictType(dictTypeName, corpId); List<CListItem> itemList = new List<CListItem>(); foreach (string key in dict.Keys) { itemList.Add(new CListItem(key, dict[key])); } BindDictItems(combo, itemList, defaultValue); }
以上就是一个整体性的思路,并在系统中能够顺利解决问题的做法,希望大家可以借鉴。
系列文章如下所示:
Web API应用架构在Winform混合框架中的应用(1)
Web API应用架构在Winform混合框架中的应用(2)--自定义异常结果的处理
Web API应用架构在Winform混合框架中的应用(3)--Winfrom界面调用WebAPI的过程分解