(续前文)
Service类提供业务的实现逻辑,其调用Dao类的方法进行数据存取,并为Controller类提供方法。类似于Dao的接口类,服务层使用接口类,便于代码实现层面的扩展。对象的CRUD的Service接口类的命名为XXXManService,其中"Man"表示"Management",即XXX对象管理服务,以区别于对象的其它服务类。如用户管理服务接口类的名称为UserManService。 Service类常规的CRUD,将考虑下列接口方法: 1)新增单个对象; 2)批量新增对象; 3)编辑单个对象; 4)批量修改对象; 5)删除单个对象(包括禁用/启用切换); 6)批量删除对象; 7)分页查询对象列表; 8)查询指定key的对象; 9)查询对象列表; 10)刷新对象(存在则修改,否则新增); 11)Excel导入对象列表; 12)Excel导出对象列表。 为了减少方法注释的维护开销,接口方法的参数注释在服务接口类上,实现类不再提供参数注释,Controller类调用处也只进行引用,这样在修改接口参数时,便于注释的维护,免得多处注释的不一致。
新增单个对象的方法名为addItem,其形式如下所示:
/** * @methodName : addItem * @description : 新增一个用户对象 * @param request : request对象 * @param item : XXX对象 * @return : 新增的XXX对象key * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * yyyy/mm/dd 1.0.0 author 初版 * */ public Map<String,Object> addItem(HttpServletRequest request, XXX item);
参数request的作用,是为了获取操作人员的账号信息,如果为null,表示内部调用(下同)。如果key为非自增ID或全局ID,而是用户指定,返回值类型可以为void。 新增单个对象的示例代码如下:
/** * @methodName : addItem * @description : 新增一个用户对象 * @param request : request对象 * @param item : 用户对象 * @return : 新增的用户对象key * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2022/05/17 1.0.0 sheng.zheng 初版 * */ public Map<String,Object> addItem(HttpServletRequest request, User item);
批量新增对象的方法名为addItems,其形式如下所示:
/** * @methodName : addItems * @description : 批量新增XXX对象 * @param request : request对象 * @param itemList : XXX对象列表 * @return : 批量记录的最后一条记录ID * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * yyyy/mm/dd 1.0.0 author 初版 * */ public Map<String,Object> addItem(HttpServletRequest request, List<XXX> itemList);
如果key为非自增ID或全局ID,而是用户指定,返回值类型可以为void。 新增对象列表的示例代码如下:
/** * @methodName : addItems * @description : 批量新增用户对象 * @param request : request对象 * @param itemList : 用户对象列表 * @return : 批量记录的最后一条记录ID * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2022/05/17 1.0.0 sheng.zheng 初版 * */ public Map<String,Object> addItems(HttpServletRequest request, List<User> itemList);
编辑单个对象的方法名为editItem,其形式如下所示:
/** * @methodName : editItem * @description : 根据key修改一个XXX象 * @param request : request对象 * @param params : XXX对象相关字段字典,至少需要一个修改字段,修改字段均可选;key字段必选: * { * "keyPropName1" : 0, // keyPropName1说明,必选 * "keyPropName2" : 0, // keyPropName2说明,必选 * ...... * } * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * yyyy/mm/dd 1.0.0 author 初版 * */ public void editItem(HttpServletRequest request, Map<String, Object> params);
编辑对象的示例代码如下:
/** * @methodName : editItem * @description : 根据key修改一个用户对象 * @param request : request对象 * @param params : 用户对象相关字段字典,至少需要一个修改字段,修改字段均可选;key字段必选: * { * "userId": "0", // 用户ID,必选 * } * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2022/05/17 1.0.0 sheng.zheng 初版 * */ public void editItem(HttpServletRequest request, Map<String, Object> params);
如果为关系对象,如用户角色关系,只有key字段,则无需编辑对象方法。
批量修改对象的方法名为updateItems,其形式如下所示:
/** * @methodName : updateItems * @description : 根据条件批量修改用户对象 * @param request : request对象 * @param params : 用户对象相关字段字典,至少需要一个修改字段和一个条件字段,修改字段和条件字段均可选; * { * 修改字段: * "setPropName1" : 0, // setPropName1字段说明,可选 * ...... * 条件字段: * "condPropName1" : 0, // condPropName1字段说明,可选 * ...... * } * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * yyyy/mm/dd 1.0.0 author 初版 * */ public void updateItems(HttpServletRequest request, Map<String, Object> params);
批量修改对象的示例代码如下:
/** * @methodName : updateItems * @description : 根据条件批量修改用户对象 * @param request : request对象 * @param params : 用户对象相关字段字典,至少需要一个修改字段和一个条件字段,修改字段和条件字段均可选; * { * 修改字段集如下: * "orgId" : 0, // 组织机构ID,可选 * "userType" : 3, // 用户类型,1-系统管理员、2-公司内部用户、3-外部用户,可选 * "deleteFlag" : 0, // 记录删除标记,0-正常、1-禁用,可选 * "operatorName" : "", // 操作人账号,可选 * 条件字段如下: * "userIdList" : [], // 用户ID列表,list,可选 * "userName" : "", // 用户名,可选 * "phoneNumber" : "", // 手机号码,可选 * } * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2022/05/17 1.0.0 sheng.zheng 初版 * */ public void updateItems(HttpServletRequest request, Map<String, Object> params);
删除单个对象的方法名为deleteItem。如为逻辑删除,则为禁用/启用对象;如为物理删除,则为删除对象。形式如下所示:
/** * @methodName : deleteItem * @description : 根据key禁用/启用一个XXX对象 * @param request : request对象 * @param params : XXX对象的key字段集 * { * "keyPropName1" : 0, // keyPropName1说明,必选 * "keyPropName2" : 0, // keyPropName2说明,必选 * ...... * "deleteFlag" : 1, // 记录删除标记,0-正常,1-删除,默认为1,可选 * } * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * yyyy/mm/dd 1.0.0 author 初版 * */ public void deleteItem(HttpServletRequest request, Map<String, Object> params);
如为物理删除,则无deleteFlag参数。 删除(禁用/启用)对象的示例代码如下:
/** * @methodName : deleteItem * @description : 根据key禁用/启用一个用户对象 * @param request : request对象 * @param params : 用户对象的key字段集 * { * "userId" : "0", // 用户ID,必选 * "deleteFlag" : 1, // 记录删除标记,0-正常,1-删除,默认为1,可选 * } * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2022/05/17 1.0.0 sheng.zheng 初版 * */ public void deleteItem(HttpServletRequest request, Map<String, Object> params);
批量删除对象的方法名为deleteItems,该方法只支持物理删除。形式如下所示:
/** * @methodName : deleteItems * @description : 根据条件删除多个XXX对象 * @param request : request对象 * @param params : 相关条件字段字典,形式如下: * { * "condPropName1" : 0, // condPropName1字段说明,可选 * ...... * } * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * yyyy/mm/dd 1.0.0 author 初版 * */ public void deleteItems(HttpServletRequest request, Map<String, Object> params);
示例代码如下:
/** * @methodName : deleteItems * @description : 根据条件删除多个用户和角色关系对象 * @param request : request对象 * @param params : 相关条件字段字典,形式如下: * { * "roleId" : 0, // 角色ID,可选 * "userId" : 0L, // 用户ID,可选 * } * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2021/01/21 1.0.0 sheng.zheng 初版 * */ public void deleteItems(HttpServletRequest request, Map<String, Object> params);
查询单个对象,为根据key获取对象,方法名为getItem,形式如下所示:
/** * @methodName : getItem * @description : 根据key获取一个XXX对象 * @param request : request对象 * @param params : 请求参数,形式如下: * { * "keyPropName1" : 0, // keyPropName1说明,必选 * "keyPropName2" : 0, // keyPropName2说明,必选 * ...... * } * @return : 用户对象 * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * yyyy/mm/dd 1.0.0 author 初版 * */ public XXX getItem(HttpServletRequest request, Map<String, Object> params);
示例代码如下:
/** * @methodName : getItem * @description : 根据key获取一个用户对象 * @param request : request对象 * @param params : 请求参数,形式如下: * { * "userId": "0", // 用户ID,必选 * } * @return : 用户对象 * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2022/05/17 1.0.0 sheng.zheng 初版 * */ public User getItem(HttpServletRequest request, Map<String, Object> params);
为了内部调用方便,也可以增加一个接口方法形式,如下所示:
/** * @methodName : getItem * @description : 根据key获取一个XXX对象 * @param request : request对象 * @param keyPropName1 : 对象xxx的key1属性字段 * .... : * @param keyPropNameN : 对象xxx的keyn属性字段,如果只有一个key字段,则无此参数,数据类型依据具体key而定 * @return : 用户对象 * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * yyyy/mm/dd 1.0.0 author 初版 * */ public XXX getItem(HttpServletRequest request, keyDatatype1 keyPropName1,...,keyDatatypeN keyPropNameN);
如果需要根据唯一键值获取对象,也可以增加方法,但不属于常规接口。
分页查询对象的方法名为queryItems,形式如下所示:
/** * @methodName : queryItems * @description : 根据条件分页查询用户对象列表 * @param request : request对象 * @param params : 查询参数,形式如下: * { * "condPropName1" : 0, // condPropName1字段说明,可选 * ...... * "condPropNameN" : 0, // condPropNameN字段说明,可选 * "sortList" : [], // 排序选项,SortField对象列表,只支持数据表字段属性,可选 * "pagenum" : 1, // 当前页码,可选 * "pagesize" : 10, // 每页记录数,可选 * } * @return : 用户对象分页列表 * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * yyyy/mm/dd 1.0.0 author 初版 * */ public PageInfo<XXX> queryItems(HttpServletRequest request, Map<String, Object> params);
条件字段的参数名同dao的selectItems,示例代码如下:
/** * @methodName : queryItems * @description : 根据条件分页查询用户对象列表 * @param request : request对象 * @param params : 查询参数,形式如下: * { * "userId" : "0", // 用户ID,可选 * "userIdList" : [], // 用户ID列表,list,可选 * "userName" : "", // 用户名,可选 * "userNameLike" : "", // 用户名,like,可选 * "userType" : 3, // 用户类型,1-系统管理员、2-公司内部用户、3-外部用户,可选 * "userTypeList" : [], // 用户类型,1-系统管理员、2-公司内部用户、3-外部用户列表,list,可选 * "sex" : 1, // 性别,1-无值、2-男、3-女、4-其它,可选 * "deleteFlag" : 0, // 记录删除标记,0-正常、1-禁用,可选 * "phoneNumber" : "", // 手机号码,可选 * "phoneNumberLike" : "", // 手机号码,like,可选 * "realNameLike" : "", // 真实姓名,like,可选 * "email" : "", // Email,可选 * "emailLike" : "", // Email,like,可选 * "birthGte" : "yyyy-MM-dd", // 生日起始值,gte,可选 * "birthLte" : "yyyy-MM-dd", // 生日终止值,lte,可选 * "orgId" : 0, // 组织ID,可选 * "orgIdList" : [], // 组织ID列表,list,可选 * "openId" : "", // 微信小程序的openid,可选 * "woaOpenid" : "", // 微信公众号openid,可选 * "sortList" : [], // 排序选项,SortField对象列表,只支持数据表字段属性,可选 * "pagenum" : 1, // 当前页码,可选 * "pagesize" : 10, // 每页记录数,可选 * } * @return : 用户对象分页列表 * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2022/05/17 1.0.0 sheng.zheng 初版 * */ public PageInfo<User> queryItems(HttpServletRequest request, Map<String, Object> params);
查询对象列表的方法名为getItems,形式如下所示:
/** * @methodName : getItems * @description : 根据条件查询XXX对象列表 * @param request : request对象 * @param params : 查询参数,形式如下: * { * "condPropName1" : 0, // condPropName1字段说明,可选 * ...... * "condPropNameN" : 0, // condPropNameN字段说明,可选 * "offset" : 0, // limit记录偏移量,可选 * "rows" : 20, // limit最大记录条数,可选 * "sortList" : [], // 排序选项,SortField对象列表,只支持数据表字段属性,可选 * } * @return : 用户对象列表 * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * yyyy/mm/dd 1.0.0 author 初版 * */ public List<XXX> getItems(HttpServletRequest request, Map<String, Object> params);
条件字段的参数名同dao的selectItems,示例代码如下:
/** * @methodName : getItems * @description : 根据条件查询用户对象列表 * @param request : request对象 * @param params : 查询参数,形式如下: * { * "userId" : "0", // 用户ID,可选 * "userIdList" : [], // 用户ID列表,list,可选 * "userName" : "", // 用户名,可选 * "userNameLike" : "", // 用户名,like,可选 * "userType" : 3, // 用户类型,1-系统管理员、2-公司内部用户、3-外部用户,可选 * "userTypeList" : [], // 用户类型,1-系统管理员、2-公司内部用户、3-外部用户列表,list,可选 * "sex" : 1, // 性别,1-无值、2-男、3-女、4-其它,可选 * "deleteFlag" : 0, // 记录删除标记,0-正常、1-禁用,可选 * "phoneNumber" : "", // 手机号码,可选 * "phoneNumberLike" : "", // 手机号码,like,可选 * "realNameLike" : "", // 真实姓名,like,可选 * "email" : "", // Email,可选 * "emailLike" : "", // Email,like,可选 * "birthGte" : "yyyy-MM-dd", // 生日起始值,gte,可选 * "birthLte" : "yyyy-MM-dd", // 生日终止值,lte,可选 * "orgId" : 0, // 组织机构ID,可选 * "orgIdList" : [], // 组织机构ID列表,list,可选 * "openId" : "", // 微信小程序的openid,可选 * "woaOpenid" : "", // 微信公众号openid,可选 * "offset" : 0, // limit记录偏移量,可选 * "rows" : 20, // limit最大记录条数,可选 * "sortList" : [], // 排序选项,SortField对象列表,只支持数据表字段属性,可选 * } * @return : 用户对象列表 * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2022/05/17 1.0.0 sheng.zheng 初版 * */ public List<User> getItems(HttpServletRequest request, Map<String, Object> params);
getItems与queryItems方法的区别是getItems不使用pagehelper实现分页查询,getItems方法可用于内部服务的调用,也可以为前端接口调用(如下拉框的项)。需要注意的是,如果结果集过于庞大,可能会导致内存溢出。因此,如果预计结果集很大时,应使用offset和rows参数,进行分批查询,确保最大记录数不会导致内存溢出。
导入数据时,没有记录ID值,此时如果对象存在(根据唯一键确定),则修改;如果不存在,则新增。方法名为flushItem,形式如下所示:
/** * * @methodName : flushItem * @description : 对象存在则修改,否则新增,用于内部调用 * @param request : request对象 * @param item : XXX对象,无key信息 * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * yyyy/mm/dd 1.0.0 author 初版 * */ public void flushItem(HttpServletRequest request,XXX item);
示例代码如下:
/** * * @methodName : flushItem * @description : 对象存在则修改,否则新增,用于内部调用 * @param request : request对象 * @param item : 用户对象,无key信息 * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2022/05/17 1.0.0 sheng.zheng 初版 * */ public void flushItem(HttpServletRequest request,User item);
根据条件查询并导出Excel文件,方法名为exportExcelFile,形式如下所示:
/** * * @methodName : exportExcelFile * @description : 根据查询条件导出XXX对象数据Excel文件 * @param request : request对象 * @param response : response对象 * @param params : 请求参数,形式如下: * { * "condPropName1" : 0, // condPropName1字段说明,可选 * ...... * "condPropNameN" : 0, // condPropNameN字段说明,可选 * "sortList" : [], // 排序选项,SortField对象列表,只支持数据表字段属性,可选 * } * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * yyyy/mm/dd 1.0.0 author 初版 * */ public void exportExcelFile(HttpServletRequest request,HttpServletResponse response, Map<String,Object> params);
示例代码如下:
/** * * @methodName : exportExcelFile * @description : 根据查询条件导出用户对象数据Excel文件 * @param request : request对象 * @param response : response对象 * @param params : 请求参数,形式如下: * { * "userId" : "0", // 用户ID,可选 * "userIdList" : [], // 用户ID列表,list,可选 * "userName" : "", // 用户名,可选 * "userNameLike" : "", // 用户名,like,可选 * "userType" : 3, // 用户类型,1-系统管理员、2-公司内部用户、3-外部用户,可选 * "userTypeList" : [], // 用户类型,1-系统管理员、2-公司内部用户、3-外部用户列表,list,可选 * "sex" : 1, // 性别,1-无值、2-男、3-女、4-其它,可选 * "deleteFlag" : 0, // 记录删除标记,0-正常、1-禁用,可选 * "phoneNumber" : "", // 手机号码,可选 * "phoneNumberLike" : "", // 手机号码,like,可选 * "realNameLike" : "", // 真实姓名,like,可选 * "email" : "", // Email,可选 * "emailLike" : "", // Email,like,可选 * "birthGte" : "yyyy-MM-dd", // 生日起始值,gte,可选 * "birthLte" : "yyyy-MM-dd", // 生日终止值,lte,可选 * "orgId" : 0, // 组织机构ID,可选 * "orgIdList" : [], // 组织机构ID列表,list,可选 * "openId" : "", // 微信小程序的openid,可选 * "woaOpenid" : "", // 微信公众号openid,可选 * "sortList" : [], // 排序选项,SortField对象列表,只支持数据表字段属性,可选 * } * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2022/05/17 1.0.0 sheng.zheng 初版 * */ public void exportExcelFile(HttpServletRequest request,HttpServletResponse response, Map<String,Object> params);
导出的Excel文件,常规格式使用sheet0,第一行为标题行,后面是数据行。如果标题需要特别排版,则属于定制需求。
导入Excel文件,方法名为importExcelFile,常规形式如下所示:
/** * * @methodName : importExcelFile * @description : 导入XXX对象数据Excel文件 * @param request : request对象 * @param upfile : 上传文件对象 * @param params : 请求参数,形式如下: * { * "param1" : 0, // 参数字段1说明,可选 * "param2" : 0, // 参数字段2说明,可选 * ...... * } * @return : 导入异常提示信息列表 * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * yyyy/mm/dd 1.0.0 author 初版 * */ public List<String> importExcelFile(HttpServletRequest request, MultipartFile upfile, Map<String, Object> params);
导入Excel文件,常常携带附加参数,如导致指定组织的用户记录。 示例代码如下:
/** * * @methodName : importExcelFile * @description : 导入用户对象数据Excel文件 * @param request : request对象 * @param upfile : 上传文件对象 * @param params : 请求参数,形式如下: * { * "orgId" : 0,// 组织机构ID,必选 * } * @return : 导入异常提示信息列表 * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2022/05/17 1.0.0 sheng.zheng 初版 * */ public List<String> importExcelFile(HttpServletRequest request, MultipartFile upfile, Map<String, Object> params);
导人的Excel文件,常规格式使用sheet0,第一行为标题行,后面是数据行。不要求从A0位置开始,列次序不要求严格固定。 如果导入的数据量很大时,或者导入处理比较费时时,可能会发生HTTP连接超时。此时,需要使用异步导入。 关于异步请求框架,参见:[https://www.cnblogs.com/alabo1999/p/16607827.html](Spring Boot异步请求处理框架)。 异步导入的方法:
/** * @methodName : importExcelFile * @description : 导入XXX对象Excel数据文件 * @param request : request对象 * @param upfile : 上传文件对象 * @param params : 请求参数,形式如下: * { * "param1" : 0, // 参数字段1说明,可选 * "param2" : 0, // 参数字段2说明,可选 * ...... * } * @return : 任务ID,形式如下: * { * "taskId" : 1, // 任务ID * } * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * yyyy/mm/dd 1.0.0 author 初版 * */ public Map<String, Object> importExcelFile(HttpServletRequest request, MultipartFile upfile, Map<String, Object> params); /** * * @methodName : getImpProcInfo * @description : 获取导入XXX处理信息 * @param request : request对象 * @param params : 请求参数,形式如下: * { * "taskId" : 1, // 任务ID,必选 * } * @return : JSON对象,形式如下: * { * "taskId" : 1, // 任务ID,必选 * "procStatus": 2, // 处理状态,0-未处理,1-处理中,2-处理结束,必选 * "progress" : 0.0, // 处理进度百分比 * "logList" : [, // 处理日志,必选,形式如下: * { * "2022-08-17 14:25:27.917 INFO 1 导入XXX --- 开始处理任务...", * "2022-08-17 14:25:27.920 INFO 1 导入XXX --- 处理步骤1", * } * ], * "resultCode": 0, // 异常返回码 * "message" : "", // 异常信息 * "result" : [, // 导入提示信息,处理结束时有此信息 * ] * } * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * yyyy/mm/dd 1.0.0 author 初版 * */ public Map<String, Object> getImpProcInfo(HttpServletRequest request, Map<String, Object> params);
异步导入时,前端调用importExcelFile,获取任务ID,根据这个任务ID,进行轮询。可以得到中间处理日志和导入结果。后端在处理异步导入时,可以使用多线程处理。
(未完待续...)