Skip to content

service 服务层

service 服务层在系统或者插件下 service 文件夹下,service 的作用是实现相关业务的功能逻辑,同时为接口提供相关的数据结构。

目录结构与分层设计

Service 层采用分层架构设计,将不同端口的业务逻辑进行隔离和统一管理。在系统或插件的 service 目录下,通常包含 adminapicore 三个子目录,分别对应不同接口类型的业务逻辑实现。

分层架构设计

service/
├── admin/       # 管理后台API相关业务逻辑
├── api/         # 前端用户API相关业务逻辑  
└── core/        # 与接口无关的核心业务逻辑

各层职责划分

admin 层

  • 职责:实现管理后台接口(adminapi)的业务逻辑

  • 特点:面向管理员用户,提供系统配置、数据管理、权限控制等功能

  • 示例功能:用户管理、角色权限配置、数据统计、内容审核等

api 层

  • 职责:实现前端用户接口(api)的业务逻辑

  • 特点:面向普通用户,提供业务操作、数据查询、交互功能等

  • 示例功能:用户注册登录、商品浏览、订单创建、支付操作等

core 层

  • 职责:实现与接口无关的核心业务逻辑,为 admin 和 api 层提供底层支持

  • 特点:业务逻辑的底层实现,与具体接口类型无关,提供通用的业务功能

  • 示例功能:订单状态流转、支付处理、消息发送、数据验证等

Service 接口定义规范

Service 接口是业务逻辑的契约,定义了业务功能的对外接口。接口设计应遵循清晰、一致、可扩展的原则,确保业务功能的正确表达和系统的可维护性。

接口命名规范

接口名称

  • 采用 I{业务主题}Service 的命名格式

  • I 开头表示接口类型

  • Service 结尾表示服务层组件

  • 业务主题应与功能模块名称一致

示例:

java
ISiteService      // 站点服务接口
IUserService      // 用户服务接口
IOrderService     // 订单服务接口

系统为了管理方便,对应的 admin 以及 api 下面也是分成不同的功能子项的,比如系统下面分成 addon,member,auth,sys,user 等,包括接口也是分成功能子项,两者往往是对应的,然后功能子项里面书写功能。

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

示例

下面是关于站点相关的 service 服务实例。创建 service 接口类,命名规范是 ISiteService

java
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 里面进行数据验证

java
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;

}
java
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 下面

java
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);
    }
}
java
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

基于 MIT 协议发布