API网关与社保模块

news2024/11/16 19:42:05

API网关与社保模块

理解zuul网关的作用完成zuul网关的搭建

实现社保模块的代码开发

zuul网关

在学习完前面的知识后,微服务架构已经初具雏形。但还有一些问题:不同的微服务一般会有不同的网 络地址,客户端在访问这些微服务时必须记住几十甚至几百个地址,这对于客户端方来说太复杂也难以 维护。如下图:

如果让客户端直接与各个微服务通讯,可能会有很多问题:

  • 客户端会请求多个不同的服务,需要维护不同的请求地址,增加开发难度

  • 在某些场景下存在跨域请求的问题

  • 加大身份认证的难度,每个微服务需要独立认证

因此,我们需要一个微服务网关,介于客户端与服务器之间的中间层,所有的外部请求都会先经过微服

务网关。客户端只需要与网关交互,只知道一个网关地址即可,这样简化了开发还有以下优点:

1、易于监控

2、易于认证

3、减少了客户端与各个微服务之间的交互次数

API网关是一个服务器,是系统对外的唯一入口。API网关封装了系统内部架构,为每个客户端提供 一个定制的API。API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在 网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。

什么是zuul网关

ZUUL是Netflflix开源的微服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用,Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:

  • 动态路由:动态将请求路由到不同后端集群

  • 压力测试:逐渐增加指向集群的流量,以了解性能

  • 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求静态响应处理:边缘位置进行响应,避免转发到内部集群

  • 身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求。Spring Cloud对Zuul进行了整合和增强。

Spring Cloud对Zuul进行了整合和增强

Zuul加入后的架构

不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都会经过Zuul这个 网关,然后再由网关来实现 鉴权、动态路由等等操作。Zuul就是我们服务的统一入口。

快速入门

工程搭建

创建工程 ihrm_gate ,并导入zuul网关的响应依赖


    <dependencies>
        <!--eureka客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--zuul网关-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ihrm</groupId>
            <artifactId>ihrm_common</artifactId>
            <version>1.0-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-data-jpa</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

编写启动类

在网关工程中的 com.ihrm.gate 下创建启动类 GateApplication


package com.ihrm.gate;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

//声明boot工程
@SpringBootApplication(scanBasePackages="com.ihrm")
//开启zuul网关功能
@EnableZuulProxy
public class GateApplication {

    public static void main(String[] args) {
        SpringApplication.run(GateApplication.class);
    }
}

@EnableZuulProxy注解开启Zuul网关功能

编写配置文件

#注册到eureka的服务地址
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:6868/eureka/
  instance:
    preferIpAddress : true
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
#服务配置
server:
  port: 9090
#spring配置
spring:
  #1.应用配置
  application:
    name: ihrm-gate #指定服务名
  redis:
    host: 127.0.0.1
    port: 6379
#配置路由规则
zuul:
  routes:
    #路由id,随便写
    ihrm-company:
      path: /company/** #需要映射的路径地址
      #url: http://127.0.0.1:9001 #映射路径对应的实际微服务的url路径
      serviceId: ihrm-company
      #zuul 自动的会删除请求的前缀 http://127.0.0.1:9001/company
      stripPrefix: false #不删除请求前缀

配置路由规则

通过zuul网关请求企业微服务,需要在application.yml中配置路由转发规则如下

#配置路由规则
zuul:
  routes:
    #路由id,随便写
    ihrm-company:
      path: /company/** #需要映射的路径地址
      url: http://127.0.0.1:9001 #映射路径对应的实际微服务的url路径
      #zuul 自动的会删除请求的前缀 http://127.0.0.1:9001/company
      stripPrefix: false #不删除请求前缀

骚戴理解:例如前端发的请求是http://localhost:9090/company,其中9090是zuul网关的端口号,然后就会匹配到path路径,因为符合/company/**,匹配上后会把http://localhost:9090/替换成http://127.0.0.1:9001,然后把/company拼接在后面,最终成http://127.0.0.1:9001/company,这是在配置了stripPrefix: false的情况下,如果没有配置这个,那就需要发请求的时候是http://localhost:9090/company/company,因为zuul网关会自动删除掉前缀/company

注释

代码

作用

配置路由规则

zuul:

配置Zuul网关的路由规则

路由id,随便写

ihrm-company:

设置路由的id,可以随意命名

需要映射的路径地址

path: /company/**

配置需要映射的路径地址,即客户端请求的路径地址

映射路径对应的实际微服务的url路径

url: http://127.0.0.1:9001

配置映射路径对应的实际微服务的url路径,即请求需要转发到的服务地址

zuul 自动的会删除请求的前缀 http://127.0.0.1:9001/company

stripPrefix: false

配置是否删除请求前缀,这里设置为false,表示不删除请求前缀

路由配置

在刚才的路由规则中,我们把路径对应的服务地址写死了!如果同一服务有多个实例的话,这样做显然 就不合理了。我们应该根据服务的名称,去Eureka注册中心查找服务对应的所有实例列表,然后进行动态路由才对!

添加Eureka客户端发现功能

  • 添加eureka依赖


    <dependencies>
        <!--eureka客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--zuul网关-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ihrm</groupId>
            <artifactId>ihrm_common</artifactId>
            <version>1.0-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-data-jpa</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
  • 修改启动类,开启服务发现功能


package com.ihrm.gate;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

//声明boot工程
@SpringBootApplication(scanBasePackages="com.ihrm")
//开启zuul网关功能
@EnableZuulProxy
//开启服务发现功能
@EnableDiscoveryClient
public class GateApplication {

    public static void main(String[] args) {
        SpringApplication.run(GateApplication.class);
    }
}

修改配置文件

  • 在application.xml中添加eureka服务发现的相关配置信息

#注册到eureka的服务地址
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:6868/eureka/
  instance:
    preferIpAddress : true
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
#服务配置
server:
  port: 9090
#spring配置
spring:
  #1.应用配置
  application:
    name: ihrm-gate #指定服务名
  redis:
    host: 127.0.0.1
    port: 6379
#配置路由规则
zuul:
  routes:
    #路由id,随便写
    ihrm-company:
      path: /company/** #需要映射的路径地址
      #url: http://127.0.0.1:9001 #映射路径对应的实际微服务的url路径
      serviceId: ihrm-company
      #zuul 自动的会删除请求的前缀 http://127.0.0.1:9001/company
      stripPrefix: false #不删除请求前缀

zuul网关从eureka注册中心中获取服务提供者的所有数据

骚戴理解:解释一下下面的配置内容

#注册到eureka的服务地址
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:6868/eureka/
  instance:
    preferIpAddress : true
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}

注释

代码

作用

注册到eureka的服务地址

eureka:

配置Eureka注册中心的相关信息

配置Eureka客户端的服务地址

eureka.client.service-url.defaultZone: http://127.0.0.1:6868/eureka/

配置Eureka客户端注册中心的服务地址,即Eureka Server的地址

配置Eureka客户端实例的信息

eureka.instance

配置Eureka客户端实例的信息,包括优先使用IP地址、实例ID等信息

优先使用IP地址

preferIpAddress : true

配置Eureka客户端实例优先使用IP地址

实例ID

instance-id: {spring.cloud.client.ip-address}:spring.cloud.client.ip−address:{spring.application.name}:${server.port}

配置Eureka客户端实例的ID,使用${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}表达式,其中${spring.cloud.client.ip-address}表示客户端的IP地址,${spring.application.name}表示应用名称,${server.port}表示应用端口号。

  • 修改zuul路由的映射规则

因为已经有了Eureka客户端,我们可以从Eureka获取服务的地址信息,因此映射时无需指定IP地址,而 是通过serviceId: ihrm-company来指定服务名称访问,而且Zuul已经集成了Ribbon的负载均衡功能。

zuul:
 routes:
 ihrm-company: # 这里是路由id,随意写
 path: /ihrm-company/** # 这里是映射路径
 serviceId: ihrm-company # 指定服务名称

简化配置

在刚才的配置中,我们的规则是这样的:

  • zuul.routes.<route>.path=/xxx/** : 来指定映射路径。 <route> 是自定义的路由名

  • zuul.routes.<route>.serviceId=/user-service :来指定服务名。

而大多数情况下,我们的 <route> 路由名称往往和 服务名会写成一样的。因此Zuul就提供了一种简化的配置语法: zuul.routes.<serviceId>=<path>

比方说上面我们关于user-service的配置可以简化为一条(省去了对服务名称的配置):


zuul:
 routes:
 ihrm-company: /ihrm-company/** # 这里是映射路径

在使用Zuul的过程中,上面讲述的规则已经大大的简化了配置项。但是当服务较多时,配置也是比较繁 琐的。因此Zuul就指定了默认的路由规则:

  • 默认情况下,一切服务的映射路径就是服务名本身。例如服务名为: ihrm-company ,则默认的映射路径就是: /ihrm-company/**

也就是说,刚才的映射规则我们完全不配置也是可以的

前端修改

由于使用了zuul网关统一对所有微服务进行转发,那么我们在前端系统调用的时候,只需要向网关服务 器发送请求即可。在前端框架中的 config/index.js 中修改请求URL

基于Zuul的统一鉴权

spring cloud Zuul包含了对请求的路由和过滤2个功能。路由功能负责将请求转发到具体的微服务上, 而过滤器负责对请求的处理过程进行干预,是实现权限校验、服务聚合等功能的基础。

Zuul的过滤器

ZuulFilter

ZuulFilter是过滤器的顶级父类。在这里我们看一下其中定义的4个最重要的方法:


public abstract ZuulFilter implements IZuulFilter{
 abstract public String filterType();
 abstract public int filterOrder();
 
 boolean shouldFilter();// 来自IZuulFilter
 Object run() throws ZuulException;// IZuulFilter
}

shouldFilter :返回一个 Boolean 值,判断该过滤器是否需要执行。返回true执行,返回false 不执行。

run :过滤器的具体业务逻辑。

filterType :返回字符串,代表过滤器的类型。包含以下4种:

  • pre :请求在被路由之前执行

  • routing :在路由请求时调用

  • post :在routing和errror过滤器之后调用

  • error :处理请求时发生错误调用

filterOrder :通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。

自定义过滤器

接下来我们来自定义一个过滤器,用于深入理解zuul过滤器的执行过程


package com.ihrm.gate.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * 自定义过滤器
 */
//@Component
public class LoginFilter extends ZuulFilter {

    /**
     * String类型的返回值
     *  定义过滤器类型的
     *      pre     : 在执行路由请求之前执行
     *      routing : 在路由请求时调用
     *      post    : 在routing和error过滤器之后执行
     *      error   : 处理请求出现异常的时候执行
     */
    public String filterType() {
        return "pre";
    }

    /**
     * int类型的返回值
     *  定义过滤器的优先级 : 数字越小,优先级越高
     */
    public int filterOrder() {
        return 2;
    }

    /**
     * boolean类型的返回值
     *  判断过滤器是否需要执行
     *
     */
    public boolean shouldFilter() {
        //对某些请求过滤(不执行过滤器)
        return true;
    }

    /**
     * run方法 : 过滤器中负责的具体业务逻辑
     *  使用过滤器进行jwt的鉴权
     */
    public Object run() throws ZuulException {
        //System.out.println("执行了LoginFilter的run方法");

        //1.获取请求对象request
        //1.1 获取Zuul提供的请求上下文的对象(工具类)
        RequestContext rc = RequestContext.getCurrentContext();
        //1.2 从上下文对象获取request对象
        HttpServletRequest request = rc.getRequest();
        //2.从request中获取Authorization的头信息
        String token = request.getHeader("Authorization");
        //3.判断
        if(token == null || "".equals(token)) {
            //没有传递token信息,需要登录,拦截
            rc.setSendZuulResponse(false);
            //返回错误的401状态码
            rc.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        return null;
    }

}

过滤器执行生命周期

这张是Zuul官网提供的请求生命周期图,清晰的表现了一个请求在各个过滤器的执行顺序。

正常流程:

  • 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的 服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。

异常流程:

  • 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕 后,会将请求交给POST过滤器,最后返回给用户。

  • 如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。

  • 如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求 不会再到达POST过滤器了。

所有内置过滤器列表:

使用场景

场景非常多:

  • 请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了

  • 异常处理:一般会在error类型和post类型过滤器中结合来处理。

  • 服务调用时长统计:pre和post结合使用。

统一鉴权

基于JWT的统一鉴权

在某些应用中,往往使用JWT的形式进行无状态的用户鉴权。对于JWT的鉴权,只需要使用Zuul的自定义过滤器,在过滤器中判断是否携带JWT的token信息即可。


    public Object run() throws ZuulException {
        //System.out.println("执行了LoginFilter的run方法");

        //1.获取请求对象request
        //1.1 获取Zuul提供的请求上下文的对象(工具类)
        RequestContext rc = RequestContext.getCurrentContext();
        //1.2 从上下文对象获取request对象
        HttpServletRequest request = rc.getRequest();
        //2.从request中获取Authorization的头信息
        String token = request.getHeader("Authorization");
        //3.判断
        if(token == null || "".equals(token)) {
            //没有传递token信息,需要登录,拦截
            rc.setSendZuulResponse(false);
            //返回错误的401状态码
            rc.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        return null;
    }

基于Shiro的统一鉴权

由于我们的系统使用shiro结合自定义session的形式,相当于将用户数据存储到了分布式缓存redis中。 那么只需要再zuul中使用shiro即可完成统一用户权限校验

  • 引入shiro依赖


<dependency>
     <groupId>com.ihrm</groupId>
     <artifactId>ihrm_common</artifactId>
     <version>1.0-SNAPSHOT</version>
</dependency>
  • 配置shiro


package com.ihrm.gate;

import com.ihrm.common.shiro.realm.IhrmRealm;
import com.ihrm.common.shiro.session.CustomSessionManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfiguration {

    //1.创建realm
    @Bean
    public IhrmRealm getRealm() {
        return new IhrmRealm();
    }

    //2.创建安全管理器
    @Bean
    public SecurityManager getSecurityManager(IhrmRealm realm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        //将自定义的会话管理器注册到安全管理器中
        securityManager.setSessionManager(sessionManager());
        //将自定义的redis缓存管理器注册到安全管理器中
        securityManager.setCacheManager(cacheManager());
        return securityManager;
    }

    //3.配置shiro的过滤器工厂

    /**
     * 再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制
     *
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        //1.创建过滤器工厂
        ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
        //2.设置安全管理器
        filterFactory.setSecurityManager(securityManager);
        //3.通用配置(跳转登录页面,未授权跳转的页面)
        filterFactory.setLoginUrl("/autherror?code=1");//跳转url地址
        filterFactory.setUnauthorizedUrl("/autherror?code=2");//未授权的url
        //4.设置过滤器集合
        Map<String,String> filterMap = new LinkedHashMap<>();
        //anon -- 匿名访问
        filterMap.put("/sys/login","anon");
        filterMap.put("/autherror","anon");
        //注册
        //authc -- 认证之后访问(登录)
        filterMap.put("/**","authc");
        //perms -- 具有某中权限 (使用注解配置授权)
        filterFactory.setFilterChainDefinitionMap(filterMap);

        return filterFactory;
    }


    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;

    /**
     * 1.redis的控制器,操作redis
     */
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        return redisManager;
    }

    /**
     * 2.sessionDao
     */
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO sessionDAO = new RedisSessionDAO();
        sessionDAO.setRedisManager(redisManager());
        return sessionDAO;
    }

    /**
     * 3.会话管理器
     */
    public DefaultWebSessionManager sessionManager() {
        CustomSessionManager sessionManager = new CustomSessionManager();
        //sessionManager.setSessionIdCookieEnabled(false);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }

    /**
     * 4.缓存管理器
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }


    //开启对shior注解的支持
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

由于再zuul网关中已经进行了统一的权限校验,那么其它微服务的权限校验就可以关闭了。

传递敏感header

经过测试可以发现在网关中明明已经具备了权限,可以在具体的微服务中还是会告知权限不足或者没有 找到相关用户。这是因为在Zuul进行请求转发的时候,会把header清空,为了传递原始的header信息 到最终的微服务,在配置加上:


zuul.routes.xxx.sentiviteHeaders:	#将指定路由的敏感头设置为空
zuul.routes.xxx.customSensitiveHeaders: true #对指定路由开启自定义敏感头

完整的gate配置文件

#注册到eureka的服务地址
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:6868/eureka/
  instance:
    preferIpAddress : true
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
#服务配置
server:
  port: 9090
#spring配置
spring:
  #1.应用配置
  application:
    name: ihrm-gate #指定服务名
  redis:
    host: 127.0.0.1
    port: 6379
#配置路由规则
zuul:
  routes:
    #路由id,随便写
    ihrm-company:
      path: /company/** #需要映射的路径地址
      #url: http://127.0.0.1:9001 #映射路径对应的实际微服务的url路径
      serviceId: ihrm-company
      #zuul 自动的会删除请求的前缀 http://127.0.0.1:9001/company
      stripPrefix: false #不删除请求前缀
      #处理敏感头信息
      sentiviteHeaders:   #将指定路由的敏感头设置为空
      customSensitiveHeaders: true #对指定路由开启自定义敏感头
    ihrm-system: #系统
      path: /sys/** #配置请求URL的请求规则
      serviceId: ihrm-system #指定Eureka注册中心中的服务id
      stripPrefix: false
      sentiviteHeaders:   #将指定路由的敏感头设置为空
      customSensitiveHeaders: true #对指定路由开启自定义敏感头
    ihrm-employee: #员工
      path: /employees/** #配置请求URL的请求规则
      serviceId: ihrm-employee #指定Eureka注册中心中的服务id
      stripPrefix: false
      sentiviteHeaders:   #将指定路由的敏感头设置为空
      customSensitiveHeaders: true #对指定路由开启自定义敏感头
    ihrm-social-securitys: #社保
      path: /social_securitys/** #配置请求URL的请求规则
      serviceId: ihrm-social-securitys #指定Eureka注册中心中的服务id
      stripPrefix: false
      sentiviteHeaders:   #将指定路由的敏感头设置为空
      customSensitiveHeaders: true #对指定路由开启自定义敏感头
    ihrm-atte-cfg: #社保
      path: /cfg/** #配置请求URL的请求规则
      serviceId: ihrm-attendance #指定Eureka注册中心中的服务id
      stripPrefix: false
      sentiviteHeaders:   #将指定路由的敏感头设置为空
      customSensitiveHeaders: true #对指定路由开启自定义敏感头
    ihrm-atte-attendances: #社保
      path: /attendances/** #配置请求URL的请求规则
      serviceId: ihrm-attendance #指定Eureka注册中心中的服务id
      stripPrefix: false
      sentiviteHeaders:   #将指定路由的敏感头设置为空
      customSensitiveHeaders: true #对指定路由开启自定义敏感头

社保管理

需求分析

完成社保模板相关代码开发:

  • 企业员工参保设置

  • 企业月度社保明细

  • 企业社保归档数据

数据库表

  • 社保归档表


CREATE TABLE `ss_archive` (
`id` varchar(40) NOT NULL COMMENT 'id',
`company_id` varchar(40) NOT NULL COMMENT '企业id',
`years_month` varchar(255) NOT NULL COMMENT '年月',
`creation_time` date NOT NULL COMMENT '创建时间',
`enterprise_payment` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '企业缴纳',
`personal_payment` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '个人缴纳',
`total` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '合计', PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='社保-归档表'
  • 社保-归档详情表


CREATE TABLE `ss_archive_detail` (
`id` varchar(40) NOT NULL COMMENT 'id',
`archive_id` varchar(40) NOT NULL COMMENT '归档id',
`user_id` varchar(40) DEFAULT NULL COMMENT '用户id',
`username` varchar(255) DEFAULT NULL COMMENT '用户名称',
`time_of_entry` varchar(255) DEFAULT NULL COMMENT '入职时间',
`mobile` varchar(255) DEFAULT NULL COMMENT '手机号',
`id_number` varchar(255) DEFAULT NULL COMMENT '身份证号',
`the_highest_degree_of_education` varchar(255) DEFAULT NULL COMMENT '学历',
`opening_bank` varchar(255) DEFAULT NULL COMMENT '开户行',
`bank_card_number` varchar(255) DEFAULT NULL COMMENT '银行卡号',
`first_level_department` varchar(255) DEFAULT NULL COMMENT '一级部门',
`two_level_department` varchar(255) DEFAULT NULL COMMENT '二级部门',
`working_city` varchar(255) DEFAULT NULL COMMENT '工作城市',
`social_security_computer_number` varchar(255) DEFAULT NULL COMMENT '社保电脑号',
`provident_fund_account` varchar(255) DEFAULT NULL COMMENT '公积金账号',
`leave_date` varchar(255) DEFAULT NULL COMMENT '离职时间',
`household_registration_type` varchar(255) DEFAULT NULL COMMENT '户籍类型',
`participating_in_the_city` varchar(255) DEFAULT NULL COMMENT '参保城市',
`social_security_month` varchar(255) DEFAULT NULL COMMENT '社保月份',
`social_security_base` decimal(10,2) DEFAULT NULL COMMENT '社保基数',
`social_security` decimal(10,2) DEFAULT NULL COMMENT '社保合计',
`social_security_enterprise` decimal(10,2) DEFAULT NULL COMMENT '社保企业',
`social_security_individual` decimal(10,2) DEFAULT NULL COMMENT '社保个人',
`provident_fund_city` varchar(255) DEFAULT NULL COMMENT '公积金城市',
`provident_fund_month` varchar(255) DEFAULT NULL COMMENT '公积金月份',
`provident_fund_base` decimal(10,2) DEFAULT NULL COMMENT '公积金基数',
`accumulation_fund_enterprise_base` decimal(10,2) DEFAULT NULL COMMENT '公积金企业基数',
`proportion_of_provident_fund_enterprises` decimal(10,2) DEFAULT NULL COMMENT '公积金企业比例',
`individual_base_of_provident_fund` decimal(10,2) DEFAULT NULL COMMENT '公积金
个人基数',
`personal_ratio_of_provident_fund` decimal(10,2) DEFAULT NULL COMMENT '公积金个人比例',
`total_provident_fund` decimal(10,2) DEFAULT NULL COMMENT '公积金合计',
`provident_fund_enterprises` decimal(10,2) DEFAULT NULL COMMENT '公积金企业',
`provident_fund_individual` decimal(10,2) DEFAULT NULL COMMENT '公积金个人',
`pension_enterprise_base` decimal(10,2) DEFAULT NULL COMMENT '养老企业基数',
`proportion_of_pension_enterprises` decimal(10,2) DEFAULT NULL COMMENT '养老企业比例',
`pension_enterprise` decimal(10,2) DEFAULT NULL COMMENT '养老企业',
`personal_pension_base` decimal(10,2) DEFAULT NULL COMMENT '养老个人基数',
`personal_pension_ratio` decimal(10,2) DEFAULT NULL COMMENT '养老个人比例',
`old_age_individual` decimal(10,2) DEFAULT NULL COMMENT '养老个人',
`unemployment_enterprise_base` decimal(10,2) DEFAULT NULL COMMENT '失业企业基数',
`proportion_of_unemployed_enterprises` decimal(10,2) DEFAULT NULL COMMENT '失业企业比例',
`unemployed_enterprise` decimal(10,2) DEFAULT NULL COMMENT '失业企业',
`the_number_of_unemployed_individuals` decimal(10,2) DEFAULT NULL COMMENT '失业个人基数',
`percentage_of_unemployed_individuals` decimal(10,2) DEFAULT NULL COMMENT '失业个人比例',
`unemployed_individual` decimal(10,2) DEFAULT NULL COMMENT '失业个人',
`medical_enterprise_base` decimal(10,2) DEFAULT NULL COMMENT '医疗企业基数',
`proportion_of_medical_enterprises` decimal(10,2) DEFAULT NULL COMMENT '医疗企业比例',
`medical_enterprise` decimal(10,2) DEFAULT NULL COMMENT '医疗企业',
`medical_personal_base` decimal(10,2) DEFAULT NULL COMMENT '医疗个人基数',
`medical_personal_ratio` decimal(10,2) DEFAULT NULL COMMENT '医疗个人比例',
`medical_individual` decimal(10,2) DEFAULT NULL COMMENT '医疗个人',
`base_of_industrial_injury_enterprises` decimal(10,2) DEFAULT NULL COMMENT '工伤企业基数',
`proportion_of_industrial_injury_enterprises` decimal(10,2) DEFAULT NULL COMMENT '工伤企业比例',
`industrial_injury_enterprise` decimal(10,2) DEFAULT NULL COMMENT '工伤企业',
`fertility_enterprise_base` decimal(10,2) DEFAULT NULL COMMENT '生育企业基数',
`proportion_of_fertility_enterprises` decimal(10,2) DEFAULT NULL COMMENT '生育企业比例',
`childbearing_enterprise` decimal(10,2) DEFAULT NULL COMMENT '生育企业',
`base_of_serious_illness` decimal(10,2) DEFAULT NULL COMMENT '大病企业基数',
`proportion_of_seriously_ill_enterprises` decimal(10,2) DEFAULT NULL COMMENT '大病企业比例',
`big_disease_enterprise` decimal(10,2) DEFAULT NULL COMMENT '大病企业',
`personal_base_of_serious_illness` decimal(10,2) DEFAULT NULL COMMENT '大病个人基数',
`personal_proportion_of_serious_illness` decimal(10,2) DEFAULT NULL COMMENT '大病个人比例',
`a_person_of_great_disease` decimal(10,2) DEFAULT NULL COMMENT '大病个人',
`provident_fund_notes` text COMMENT '公积金备注',
`social_security_notes` text COMMENT '社保备注', PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='社保-归档详情'
  • 社保-城市与缴费项目关联表


CREATE TABLE `ss_city_payment_item` (
`id` varchar(40) NOT NULL,
`city_id` varchar(40) NOT NULL COMMENT '城市id',
`payment_item_id` varchar(40) NOT NULL COMMENT '缴费项目id',
`switch_company` tinyint(1) NOT NULL COMMENT '企业是否缴纳开关,0为禁用,1为启用',
`scale_company` decimal(6,2) DEFAULT NULL COMMENT '企业比例',
`switch_personal` tinyint(1) NOT NULL COMMENT '个人是否缴纳开关,0为禁用,1为启用',
`scale_personal` decimal(6,2) DEFAULT NULL COMMENT '个人比例', PRIMARY KEY (`id`),
UNIQUE KEY `UK_CID_PIID` (`city_id`,`payment_item_id`) USING BTREE COMMENT '城市id与缴费项目id组合唯一'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='社保-城市与缴费项目关联表'
  • 社保-企业设置信息


CREATE TABLE `ss_company_settings` (
`company_id` varchar(40) NOT NULL COMMENT '企业id',
`is_settings` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0是未设置,1是已设置',
`data_month` varchar(40) NOT NULL COMMENT '当前显示报表月份', PRIMARY KEY (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='社保-企业设置信息'
  • 社保-缴费项目


CREATE TABLE `ss_payment_item` (
`id` varchar(40) NOT NULL COMMENT 'id',
`name` varchar(255) NOT NULL COMMENT '缴费项目名称',
`switch_company` tinyint(1) NOT NULL DEFAULT '0' COMMENT '企业是否缴纳开关,0为禁用,1为启用',
`scale_company` decimal(6,2) NOT NULL DEFAULT '0.00' COMMENT '企业比例',
`switch_personal` tinyint(1) NOT NULL DEFAULT '0' COMMENT '个人是否缴纳开关,0为禁用,1为启用',
`scale_personal` decimal(6,2) NOT NULL DEFAULT '0.00' COMMENT '个人比例', PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='社保-缴费项目'
  • 社保-用户社保信息表


CREATE TABLE `ss_user_social_security` (
`user_id` varchar(40) NOT NULL COMMENT '用户id',
`enterprises_pay_social_security_this_month` tinyint(1) NOT NULL DEFAULT '0' COMMENT '本月是否缴纳社保 0为不缴纳 1为缴纳',
`enterprises_pay_the_provident_fund_this_month` tinyint(1) NOT NULL DEFAULT '0' COMMENT '本月是否缴纳公积金 0为不缴纳 1为缴纳',
`participating_in_the_city_id` varchar(40) NOT NULL COMMENT '参保城市id',
`social_security_type` tinyint(1) NOT NULL DEFAULT '2' COMMENT '参保类型 1为首次开户 2为非首次开户',
`household_registration_type` tinyint(1) NOT NULL COMMENT '户籍类型 1为本市城镇 2
为本市农村 3为外埠城镇 4为外埠农村',
`social_security_base` int(8) NOT NULL COMMENT '社保基数',
`industrial_injury_ratio` decimal(6,2) DEFAULT NULL COMMENT '工伤比例',
`social_security_notes` varchar(300) DEFAULT NULL COMMENT '社保备注',
`provident_fund_city_id` varchar(40) NOT NULL COMMENT '公积金城市id',
`provident_fund_base` int(8) NOT NULL COMMENT '公积金基数',
`enterprise_proportion` decimal(6,2) NOT NULL COMMENT '公积金企业比例',
`personal_proportion` decimal(6,2) NOT NULL COMMENT '公积金个人比例',
`enterprise_provident_fund_payment` decimal(8,2) NOT NULL COMMENT '公积金企业缴纳数额',
`personal_provident_fund_payment` decimal(8,2) NOT NULL COMMENT '公积金个人缴纳数额',
`provident_fund_notes` varchar(300) DEFAULT NULL COMMENT '公积金备注',
`create_time` datetime NOT NULL COMMENT '创建时间',
`last_modify_time` datetime NOT NULL COMMENT '最后修改时间',
`social_security_switch_update_time` datetime NOT NULL COMMENT '社保是否缴纳变更时间',
`provident_fund_switch_update_time` datetime NOT NULL COMMENT '公积金是否缴纳变更时间',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='社保-用户社保信息表'

搭建环境

  1. 使用代码生成工具,根据数据库表生成最基本的实体类,dao接口和service层代码。 略

  1. 创建社保管理模块ihrm_social_securitys,并将自动生成的代码依次copy到响应的包下。

  1. 修改zuul网关,添加社保相关的请求转发


ihrm-social-securitys: #企业
path: /social_securitys/** #配置请求URL的请求规则
serviceId: ihrm-social-securitys #指定Eureka注册中心中的服务id strip-prefix: false
sentiviteHeaders: customSensitiveHeaders: true

社保列表

企业社保设置

企业第一次进入社保页面,会要求输入制作社保记录的日期


/**
* 获取企业是否设置社保
* @return
*/
@RequestMapping(value = "/settings", method = RequestMethod.GET)
public Result getSettings() throws Exception {
 CompanySettings companySettings =
companySettingsService.findById(companyId);
 return new Result(ResultCode.SUCCESS, companySettings);
}
/**
* 保存企业社保设置
*/
@RequestMapping(value = "/settings", method = RequestMethod.POST)
public Result saveSettings(@RequestBody CompanySettings companySettings){
 companySettings.setCompanyId(companyId);
 companySettingsService.save(companySettings);
 return new Result(ResultCode.SUCCESS);
}

查询所有参保人员数据列表

社保设置

获取不同城市的社保缴费项目


findPaymentItemByCityId

展示员工社保数据


findById

设置员工参保数据


/**
* 保存用户社保信息
*/
@ApiOperation(value=" 保 存 用 户 社 保 信 息 ",httpMethod = "PUT") @RequestMapping(value = "/{userId}", method = RequestMethod.PUT)
public Result save(@RequestBody UserSocialSecurity userSocialSecurity) { userSocialService.save(userSocialSecurity);
return new Result(ResultCode.SUCCESS);
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1110574.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

​iOS上架App Store的全攻略

第一步&#xff1a;申请开发者账号 在开始将应用上架到App Store之前&#xff0c;你需要申请一个开发者账号。 1.1 打开苹果开发者中心网站&#xff1a;Apple Developer 1.2 使用Apple ID和密码登录&#xff08;如果没有账号则需要注册&#xff09;&#xff0c;要确保使用与公…

【Unity3D编辑器拓展】Unity3D的IMGUI、GUI、GUILayout、EditorGUI、EditorGUILayout、OnGUI【全面总结】

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 在开发中&#xff0c;常常会遇到要使用OnGUI的地方。 也会遇到…

4090今天被下架了

刚看了一下基本知识&#xff0c;我来给他们简单普及一下它。 4090网吧还有Ai公司都很常用&#xff0c;比如我们公司就用他来训练数字人。 4090经测试&#xff0c;训练的速度是3060的8倍。也就是说&#xff0c;我训练一个数字人要1天&#xff0c;但是3060需要8天。这个成本显而…

统计学习方法 感知机

文章目录 统计学习方法 感知机模型定义学习策略学习算法原始算法对偶算法 学习算法的收敛性 统计学习方法 感知机 读李航的《统计机器学习》时&#xff0c;关于感知机的笔记。 感知机&#xff08;perceptron&#xff09;是一种二元分类的线性分类模型&#xff0c;属于判别模型…

1024程序员节特辑 | OKR VS KPI谁更合适?

专栏集锦&#xff0c;赶紧收藏以备不时之需 Spring Cloud实战专栏&#xff1a;https://blog.csdn.net/superdangbo/category_9270827.html Python 实战专栏&#xff1a;https://blog.csdn.net/superdangbo/category_9271194.html Logback 详解专栏&#xff1a;https://blog.…

预测性维护为何能够帮助企业降低设备维护成本?

预测性维护在企业设备管理中扮演着重要的角色&#xff0c;它通过实时监测设备状态和数据分析&#xff0c;能够提前预测潜在故障&#xff0c;并采取相应的维修措施&#xff0c;从而帮助企业降低设备维护成本。本文将介绍预测性维护的作用&#xff0c;探讨造成设备维护成本高的原…

板带纠偏控制系统伺服比例阀放大器

板带纠偏控制系统是集光、机、电、液四方面有机结合在一起的全闭环电液伺服系统&#xff0c;是用途广泛的机电一体化高新技术产品。 板带纠偏控制系统可广泛地应用于机械、冶金、造纸、橡胶、织带、纺织印染、电镀、塑膜胶片等诸多行业的不同种类的带材生产线的在线纠偏。 板…

UE5 ChaosVehicles载具换皮 (连载二)

1.导出SKM_SportsCar&#xff0c;删除模型&#xff0c;保留骨骼层级 2.导入新的汽车模型并合并成一个整体模型&#xff0c;调整上图红框中的四个快乐轮子对应的骨骼位置&#xff08;大致在快乐轮子的中心&#xff09;&#xff0c;骨骼角度归零。绑定并设置权重&#xff0c;快乐…

多线程进阶篇

多线程进阶篇 文章目录 多线程进阶篇1、常见的锁策略1) 乐观锁 vs 悲观锁2) 重量级锁 vs 轻量级锁3) 自旋锁 vs 挂起等待锁4) 读写锁 vs 互斥锁5) 公平锁 vs 非公平锁6) 可重入锁 vs 不可重入锁 2、死锁1) 死锁的三种典型情况:2) 如何解决死锁问题3) 死锁产生的必要条件 3、Syn…

数据结构:一篇拿捏十大排序(超详细版)

排序的概念&#xff1a; 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&#xff0c;若经…

浅谈AI人体姿态识别技术的先进性及安防视频监控应用场景

随着计算机视觉技术和安防监控技术的不断发展&#xff0c;基于AI算法的人体姿态识别技术也得到了广泛的应用。然而&#xff0c;传统的安防监控系统通常只局限于简单的视频监控等功能&#xff0c;无法准确地识别人体的姿态&#xff0c;使得一些安防监控存在着一定的漏洞和不足之…

中国人民大学与加拿大女王大学金融硕士项目——开启全球金融视野,锻造未来领袖

你是否曾梦想过站在国际金融的巅峰&#xff0c;洞察全球经济的脉搏&#xff0c;引领企业的创新与发展&#xff1f;如果你对金融充满热情&#xff0c;渴望在全球化的环境中锻炼自己&#xff0c;那么&#xff0c;中国人民大学与加拿大女王大学金融硕士项目便是你的转折点。 中国…

Confluence使用教程

1、如何创建空间 可以把空间理解成一个gitlab仓库&#xff0c;空间之间相互独立&#xff0c;一般建议按照部门&#xff08;小组的人太少&#xff0c;没必要创建空间&#xff09;或者按照项目分别创建空 2、confluence可以创建两种类型的文档&#xff1a;页面和博文 从内容上来…

SICP-- 元语言抽象--Scheme的变形--惰性求值

正则序和应用序 应用序&#xff1a;在过程应用时&#xff0c;提供给Scheme的所有参数都需要完成求值 正则序&#xff1a;将把对过程参数的求值延后到需要这些实际参数的值的时候。 将过程参数的求值拖延到最后的可能时刻被称为 惰性求值 如果在某个参数还没有完成求值之前就…

Apache Jmeter测压工具快速入门

Jmeter测压工具快速入门 一、Jmeter介绍二、Jmeter On Mac2.1 下载2.2 安装2.2.1 环境配置2.2.2 初始化设置 2.3 测试2.3.1 创建JDBC Connection Configuration2.3.2 创建线程组2.3.3 创建JDBC Request2.3.4 创建结果监控2.3.4 运行结果 2.4 问题记录2.4.1 VM option UseG1GC异…

查询企业信息的四种方法

在工作中或者对于找工作的求职人来说&#xff0c;怎么查看企业的信息呢&#xff1f;可能很多人会想到各种查查类软件&#xff0c;但是这类软件需要会员或者付费才能查看&#xff0c;对于没有会员的人来说&#xff0c;有没有其他查询企业的方法呢&#xff1f;答案肯定是有的&…

AI也可以​算命和占卜?一定要试试这个模型

01 模型介绍 Mistral Trismegistus 7B&#xff1a;专为玄学、神秘学、超自然和灵异感兴趣的人设计的模型。 专门用于处理与神秘学、灵性、超自然、占卜、炼金术、宗教、冥想等相关的问题和任务。该数据集包含大约35000个指令响应对&#xff0c;覆盖了数百个与神秘学有关的子…

安装visual studio报错“无法安装msodbcsql“

在安装visual studio2022时安装完成后提示无法安装msodbcsql, 查看日志文件详细信息提示&#xff1a;指定账户已存在。 未能安装包“msodbcsql,version17.2.30929.1,chipx64,languagezh-CN”。 搜索 URL https://aka.ms/VSSetupErrorReports?qPackageIdmsodbcsql;PackageActi…

[ Windows ] ping IP + Port 测试 ip 和 端口是否通畅

开发过程中经常会黑窗口中手动测试一下计划请求的目标ip和端口是否通畅&#xff0c;测试方式如下&#xff1a; 一、单纯测试ip是否能够 ping 通&#xff0c;这个比较熟悉了&#xff0c;运行 cmd 打开黑窗口 输入如下指令&#xff0c;能够如下提示信息&#xff0c;表示端口是通…

五分钟学会搭建悟空CRM内网穿透,实现公网访问企业内网,提升工作效率!

文章目录 前言1. 无需公网IP&#xff0c;使用cpolar实现悟空CRM远程访问2. 通过公网来访问公司内网悟空CRM3. 设置固定连接公网地址 前言 悟空CRM是一款开源的客户关系管理系统&#xff0c;以"客户关系一对一理论"为基础&#xff0c;通过对企业业务流程的重组来整合…