service 服务层
service 服务层在系统或者插件下 service 文件夹下,service 的作用是实现相关业务的功能逻辑,同时为接口提供相关的数据结构。
目录结构与分层设计
Service 层采用分层架构设计,将不同端口的业务逻辑进行隔离和统一管理。在系统或插件的 service 目录下,通常包含 admin、api 和 core 三个子目录,分别对应不同接口类型的业务逻辑实现。

分层架构设计
service/
├── admin/ # 管理后台API相关业务逻辑
├── api/ # 前端用户API相关业务逻辑
└── core/ # 与接口无关的核心业务逻辑
各层职责划分
admin 层
-
职责:实现管理后台接口(adminapi)的业务逻辑
-
特点:面向管理员用户,提供系统配置、数据管理、权限控制等功能
-
示例功能:用户管理、角色权限配置、数据统计、内容审核等
api 层
-
职责:实现前端用户接口(api)的业务逻辑
-
特点:面向普通用户,提供业务操作、数据查询、交互功能等
-
示例功能:用户注册登录、商品浏览、订单创建、支付操作等
core 层
-
职责:实现与接口无关的核心业务逻辑,为 admin 和 api 层提供底层支持
-
特点:业务逻辑的底层实现,与具体接口类型无关,提供通用的业务功能
-
示例功能:订单状态流转、支付处理、消息发送、数据验证等
Service 接口定义规范
Service 接口是业务逻辑的契约,定义了业务功能的对外接口。接口设计应遵循清晰、一致、可扩展的原则,确保业务功能的正确表达和系统的可维护性。
接口命名规范
接口名称
-
采用
I{业务主题}Service的命名格式 -
以
I开头表示接口类型 -
以
Service结尾表示服务层组件 -
业务主题应与功能模块名称一致
示例:
ISiteService // 站点服务接口
IUserService // 用户服务接口
IOrderService // 订单服务接口
系统为了管理方便,对应的 admin 以及 api 下面也是分成不同的功能子项的,比如系统下面分成 addon,member,auth,sys,user 等,包括接口也是分成功能子项,两者往往是对应的,然后功能子项里面书写功能。

在功能子项里面书写相关的逻辑功能,这里与其他 springboot 项目相同,定义 service 接口,命名规范:I+主题+service,实现是 impl,service 的接收对象命名为 param 或者实体类,service 处理后的返回数据可以是实体类,也可以是视图对象 Vo,放在 vo 文件夹下面。

示例
下面是关于站点相关的 service 服务实例。创建 service 接口类,命名规范是 ISiteService
package com.niu.core.service.admin.site;
import com.niu.core.common.domain.PageResult;
import com.niu.core.entity.addon.Addon;
import com.niu.core.entity.site.Site;
import com.niu.core.entity.site.SiteGroup;
import com.niu.core.service.admin.site.param.SiteAddParam;
import com.niu.core.service.admin.site.param.SiteEditParam;
import com.niu.core.service.admin.site.param.SiteParam;
import com.niu.core.service.admin.site.param.SiteSearchParam;
import com.niu.core.common.domain.PageParam;
import com.niu.core.service.admin.site.vo.SiteInfoVo;
import com.niu.core.service.admin.site.vo.SiteListVo;
import java.util.List;
import java.util.Map;
/**
* 站点服务接口
*/
public interface ISiteService {
/**
* 站点列表
* @param pageParam 分页参数
* @param searchParam 搜索参数
* @return PageResult<SiteListVo>
*/
PageResult<SiteListVo> list(PageParam pageParam, SiteSearchParam searchParam);
/**
* 站点详情
* @param id 主键ID
* @return SiteInfoVo
*/
SiteInfoVo info(Integer id);
/**
* 站点添加
* @param addParam 添加参数
*/
void add(SiteAddParam addParam);
/**
* 站点编辑
* @param id 主键
* @param editParam 编辑参数
*/
void edit(Integer id, SiteEditParam editParam);
/**
* 站点删除
* @param id 主键ID
*/
void del(Integer id);
/**
* 关闭站点
* @param siteId
*/
void closeSite(Integer siteId);
/**
* 开启站点
* @param siteId
*/
void openSite(Integer siteId);
/**
* 获取站点的菜单列表
* @param siteId
* @param status
* @return
*/
Map<String, List<String>> getSiteApiList(Integer siteId, Integer status);
/**
* 通过条件检索 站点数量
* @param siteSearchParam
* @return
*/
Integer getSiteCountByCondition(SiteSearchParam siteSearchParam);
/**
* 获取站点的插件
* @return
*/
List<Addon> getSiteAddons();
/**
*
*
* @param site
* @param siteGroup
*/
void siteAddonChange(Site site, SiteGroup siteGroup);
}
站点创建,站点查询相关参数放在 param 下面,同时在 param 里面进行数据验证
package com.niu.core.service.admin.site.param;
import lombok.Data;
import java.io.Serializable;
/**
* 站点参数
*/
@Data
public class SiteSearchParam implements Serializable {
private static final long serialVersionUID = 1L;
private String keywords;
private Integer status;
private Integer groupId;
private String[] createTime;
private String[] expireTime;
private String app;
private String siteDomain;
private String appType;
}
package com.niu.core.service.admin.site.param;
import lombok.Data;
import java.io.Serializable;
import javax.validation.constraints.*;
/**
* 站点参数
*/
@Data
public class SiteParam implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull(message = "siteId参数缺失")
private Integer siteId;
@NotNull(message = "siteName参数缺失")
private String siteName;
@NotNull(message = "groupId参数缺失")
private Integer groupId;
@NotNull(message = "keywords参数缺失")
private String keywords;
@NotNull(message = "appType参数缺失")
private String appType;
@NotNull(message = "logo参数缺失")
private String logo;
@NotNull(message = "desc参数缺失")
private String desc;
@NotNull(message = "status参数缺失")
private Integer status;
@NotNull(message = "latitude参数缺失")
private String latitude;
@NotNull(message = "longitude参数缺失")
private String longitude;
@NotNull(message = "provinceId参数缺失")
private Integer provinceId;
@NotNull(message = "cityId参数缺失")
private Integer cityId;
@NotNull(message = "districtId参数缺失")
private Integer districtId;
@NotNull(message = "address参数缺失")
private String address;
@NotNull(message = "fullAddress参数缺失")
private String fullAddress;
@NotNull(message = "phone参数缺失")
private String phone;
@NotNull(message = "businessHours参数缺失")
private String businessHours;
@NotNull(message = "expireTime参数缺失")
private Long expireTime;
@NotNull(message = "frontEndName参数缺失")
private String frontEndName;
@NotNull(message = "frontEndLogo参数缺失")
private String frontEndLogo;
@NotNull(message = "frontEndIcon参数缺失")
private String frontEndIcon;
@NotNull(message = "icon参数缺失")
private String icon;
@NotNull(message = "memberNo参数缺失")
private Integer memberNo;
@NotNull(message = "app参数缺失")
private String app;
@NotNull(message = "addons参数缺失")
private String addons;
@NotNull(message = "initalledAddon参数缺失")
private String initalledAddon;
@NotNull(message = "siteDomain参数缺失")
private String siteDomain;
}
站点列表,站点详情相关数据结构放在 vo 下面
package com.niu.core.service.admin.site.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.niu.core.common.domain.BeanJsonSerializer;
import com.niu.core.enums.member.StatusEnum;
import com.niu.core.enums.site.SiteStatusEnum;
import lombok.Data;
import java.io.Serializable;
/**
* Site列表视图
*/
@Data
public class SiteListVo implements Serializable {
private static final long serialVersionUID = 1L;
/** 主键 */
private Integer siteId;
/** 站点名称 */
private String siteName;
/** 分组ID(0:不限制) */
private Integer groupId;
/** 站点套餐名称 */
private String groupName;
/** 关键字 */
private String keywords;
/** 站点类型 */
private String appType;
/** 站点logo */
private String logo;
/** 简介 */
private String desc;
/** 状态 1-正常 0-体验期 2-已到期 */
private Integer status;
/** 纬度 */
private String latitude;
/** 经度 */
private String longitude;
/** 省 */
private Integer provinceId;
/** 市 */
private Integer cityId;
/** 区 */
private Integer districtId;
/** 详细地址 */
private String address;
/** 完整地址 */
private String fullAddress;
/** 客服电话 */
private String phone;
/** 营业时间 */
private String businessHours;
/** 创建时间 */
@JsonSerialize(using = BeanJsonSerializer.LongDateToStringSerializer.class)
private Long createTime;
/** 到期时间(如果是0 无限期) */
@JsonSerialize(using = BeanJsonSerializer.LongDateToStringSerializer.class)
private Long expireTime;
/** 前台名称*/
private String frontEndName;
/** 前台logo */
private String frontEndLogo;
/** 前台icon */
private String frontEndIcon;
/** 网站图标 */
private String icon;
/** 最大会员码值 */
private String memberNo;
/** 站点应用 */
@JsonSerialize(using = BeanJsonSerializer.StringToJSONObjectSerializer.class)
private String app;
/** 站点包含的插件 */
@JsonSerialize(using = BeanJsonSerializer.StringToJSONObjectSerializer.class)
private String addons;
/** 站点已执行初始化方法的插件 */
private String initalledAddon;
/** 站点域名 */
private String siteDomain;
/** 站点管理员 */
private SiteAdminVo admin;
/** 状态名称 */
private String statusName;
public String getStatusName() {
return SiteStatusEnum.getNameByCode(this.status);
}
}
package com.niu.core.service.admin.site.vo;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.niu.core.common.domain.BeanJsonSerializer;
import com.niu.core.entity.addon.Addon;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* Site视图
*/
@Data
public class SiteInfoVo implements Serializable {
private static final long serialVersionUID = 1L;
private Integer siteId; // 主键
private String siteName; // 站点名称
private Integer groupId; // 分组ID(0:不限制)
private String groupName; //分组名称
private String keywords; // 关键字
private String appType; // 站点类型
private String logo; // 站点logo
private String desc; // 简介
private Integer status; // 状态 1-正常 0-体验期 2-已到期
private String latitude; // 纬度
private String longitude; // 经度
private Integer provinceId; // 省
private Integer cityId; // 市
private Integer districtId; // 区
private String address; // 详细地址
private String fullAddress; // 完整地址
private String phone; // 客服电话
private String businessHours; // 营业时间
private Long expireTime; // 到期时间(如果是0 无限期)
private String frontEndName; // 前台名称
private String frontEndLogo; // 前台logo
private String frontEndIcon; // 前台icon
private String icon; // 网站图标
private String memberNo; // 最大会员码值
@JsonSerialize(using = BeanJsonSerializer.StringToJsonSerializer.class)
private String app; // 站点应用
@JsonSerialize(using = BeanJsonSerializer.StringToJsonSerializer.class)
private String addons; // 站点包含的插件
private List<Addon> siteAddons;
private List<Addon> apps;
private List<String> addonKeys;
private String initalledAddon; // 站点已执行初始化方法的插件
private String siteDomain; // 站点域名
}
最后站点实现类,在 impl 下面命名 SiteServiceImpl