20 changed files with 695 additions and 6 deletions
			
			
		@ -0,0 +1,139 @@ | 
				
			|||
/** | 
				
			|||
 * Copyright (c) 2018 人人开源 All rights reserved. | 
				
			|||
 * | 
				
			|||
 * https://www.renren.io
 | 
				
			|||
 * | 
				
			|||
 * 版权所有,侵权必究! | 
				
			|||
 */ | 
				
			|||
 | 
				
			|||
package com.elink.esua.epdc.filter; | 
				
			|||
 | 
				
			|||
import com.alibaba.fastjson.JSON; | 
				
			|||
import com.elink.esua.epdc.commons.tools.constant.Constant; | 
				
			|||
import com.elink.esua.epdc.commons.tools.exception.RenException; | 
				
			|||
import com.elink.esua.epdc.commons.tools.security.user.UserDetail; | 
				
			|||
import com.elink.esua.epdc.commons.tools.utils.Result; | 
				
			|||
import com.elink.esua.epdc.feign.ResourceFeignClient; | 
				
			|||
import com.elink.esua.epdc.utils.jwt.JwtTokenUtils; | 
				
			|||
import io.jsonwebtoken.Claims; | 
				
			|||
import org.apache.commons.lang3.StringUtils; | 
				
			|||
import org.springframework.beans.factory.annotation.Autowired; | 
				
			|||
import org.springframework.boot.context.properties.ConfigurationProperties; | 
				
			|||
import org.springframework.cloud.gateway.filter.GatewayFilterChain; | 
				
			|||
import org.springframework.cloud.gateway.filter.GlobalFilter; | 
				
			|||
import org.springframework.context.annotation.Configuration; | 
				
			|||
import org.springframework.core.io.buffer.DataBuffer; | 
				
			|||
import org.springframework.http.HttpHeaders; | 
				
			|||
import org.springframework.http.HttpStatus; | 
				
			|||
import org.springframework.http.MediaType; | 
				
			|||
import org.springframework.http.server.reactive.ServerHttpRequest; | 
				
			|||
import org.springframework.util.AntPathMatcher; | 
				
			|||
import org.springframework.web.server.ServerWebExchange; | 
				
			|||
import reactor.core.publisher.Flux; | 
				
			|||
import reactor.core.publisher.Mono; | 
				
			|||
 | 
				
			|||
import java.nio.charset.StandardCharsets; | 
				
			|||
import java.util.List; | 
				
			|||
 | 
				
			|||
/** | 
				
			|||
 * 权限过滤器 | 
				
			|||
 * | 
				
			|||
 * @author Mark sunlightcs@gmail.com | 
				
			|||
 * @since 1.0.0 | 
				
			|||
 */ | 
				
			|||
@Configuration | 
				
			|||
@ConfigurationProperties(prefix = "worklog") | 
				
			|||
public class WorkLogAuthFilter implements GlobalFilter { | 
				
			|||
 | 
				
			|||
    private final AntPathMatcher antPathMatcher = new AntPathMatcher(); | 
				
			|||
 | 
				
			|||
    @Autowired | 
				
			|||
    private ResourceFeignClient resourceFeignClient; | 
				
			|||
 | 
				
			|||
    @Autowired | 
				
			|||
    private JwtTokenUtils jwtUtils; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 拦截的工作日志接口 | 
				
			|||
     */ | 
				
			|||
    private List<String> urls; | 
				
			|||
 | 
				
			|||
    @Override | 
				
			|||
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { | 
				
			|||
        ServerHttpRequest request = exchange.getRequest(); | 
				
			|||
        String requestUri = request.getPath().pathWithinApplication().value(); | 
				
			|||
 | 
				
			|||
        //请求放行,无需验证权限
 | 
				
			|||
        if(pathMatcher(requestUri)){ | 
				
			|||
            return chain.filter(exchange); | 
				
			|||
        } | 
				
			|||
 | 
				
			|||
        //获取AccessToken 并进行验证
 | 
				
			|||
        String accessToken = request.getHeaders().getFirst(Constant.ACCESS_TOKEN); | 
				
			|||
        if(StringUtils.isBlank(accessToken)){ | 
				
			|||
            // 表示请求信息中没有携带AccessToken,前端需要修改上送数据
 | 
				
			|||
            throw new RenException("AccessToken为空"); | 
				
			|||
        } | 
				
			|||
 | 
				
			|||
        Claims claims = jwtUtils.getClaimByToken(accessToken); | 
				
			|||
 | 
				
			|||
        if (claims == null) { | 
				
			|||
            throw new RenException("AccessToken验证不通过"); | 
				
			|||
        } | 
				
			|||
        //验证结束
 | 
				
			|||
 | 
				
			|||
        //获取用户token
 | 
				
			|||
        String token = request.getHeaders().getFirst(Constant.TOKEN_HEADER); | 
				
			|||
        if(StringUtils.isBlank(token)){ | 
				
			|||
            token = request.getHeaders().getFirst(Constant.AUTHORIZATION_HEADER); | 
				
			|||
            if (StringUtils.isBlank(token)) { | 
				
			|||
                token = request.getQueryParams().getFirst(Constant.TOKEN_HEADER); | 
				
			|||
            } | 
				
			|||
        } | 
				
			|||
 | 
				
			|||
        //资源访问权限
 | 
				
			|||
        String language = request.getHeaders().getFirst(HttpHeaders.ACCEPT_LANGUAGE); | 
				
			|||
        Result<UserDetail> result = resourceFeignClient.resource(language, token, requestUri, request.getMethod().toString()); | 
				
			|||
        //没权限访问,直接返回
 | 
				
			|||
        if(!result.success()){ | 
				
			|||
            return response(exchange, result); | 
				
			|||
        } | 
				
			|||
 | 
				
			|||
        //获取用户信息
 | 
				
			|||
        UserDetail userDetail = result.getData(); | 
				
			|||
        if(userDetail != null){ | 
				
			|||
            //当前登录用户userId,添加到header中
 | 
				
			|||
            ServerHttpRequest build = exchange.getRequest().mutate().header(Constant.USER_KEY, userDetail.getId()+"").build(); | 
				
			|||
            return chain.filter(exchange.mutate().request(build).build()); | 
				
			|||
        } | 
				
			|||
 | 
				
			|||
        return chain.filter(exchange); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    private Mono<Void> response(ServerWebExchange exchange, Object object) { | 
				
			|||
        String json = JSON.toJSONString(object); | 
				
			|||
        DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(json.getBytes(StandardCharsets.UTF_8)); | 
				
			|||
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8); | 
				
			|||
        exchange.getResponse().setStatusCode(HttpStatus.OK); | 
				
			|||
        return exchange.getResponse().writeWith(Flux.just(buffer)); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    private boolean pathMatcher(String requestUri){ | 
				
			|||
 | 
				
			|||
        for (String url : urls) { | 
				
			|||
            if(antPathMatcher.match(url, requestUri)){ | 
				
			|||
                return false; | 
				
			|||
            } | 
				
			|||
        } | 
				
			|||
        return true; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    public List<String> getUrls() { | 
				
			|||
        return urls; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    public void setUrls(List<String> urls) { | 
				
			|||
        this.urls = urls; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
} | 
				
			|||
@ -0,0 +1,33 @@ | 
				
			|||
/** | 
				
			|||
 * Copyright (c) 2018 人人开源 All rights reserved. | 
				
			|||
 * | 
				
			|||
 * https://www.renren.io
 | 
				
			|||
 * | 
				
			|||
 * 版权所有,侵权必究! | 
				
			|||
 */ | 
				
			|||
 | 
				
			|||
package com.elink.esua.epdc.utils.jwt; | 
				
			|||
 | 
				
			|||
import org.springframework.boot.context.properties.ConfigurationProperties; | 
				
			|||
import org.springframework.context.annotation.Configuration; | 
				
			|||
 | 
				
			|||
/** | 
				
			|||
 * Jwt | 
				
			|||
 * | 
				
			|||
 * @author Mark sunlightcs@gmail.com | 
				
			|||
 * @since 1.0.0 | 
				
			|||
 */ | 
				
			|||
@Configuration | 
				
			|||
@ConfigurationProperties(prefix = "jwt.token") | 
				
			|||
public class JwtTokenProperties { | 
				
			|||
    private String secret; | 
				
			|||
 | 
				
			|||
    public String getSecret() { | 
				
			|||
        return secret; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    public void setSecret(String secret) { | 
				
			|||
        this.secret = secret; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
} | 
				
			|||
@ -0,0 +1,71 @@ | 
				
			|||
/** | 
				
			|||
 * Copyright (c) 2018 人人开源 All rights reserved. | 
				
			|||
 * <p> | 
				
			|||
 * https://www.renren.io
 | 
				
			|||
 * <p> | 
				
			|||
 * 版权所有,侵权必究! | 
				
			|||
 */ | 
				
			|||
 | 
				
			|||
package com.elink.esua.epdc.utils.jwt; | 
				
			|||
 | 
				
			|||
import io.jsonwebtoken.Claims; | 
				
			|||
import io.jsonwebtoken.Jwts; | 
				
			|||
import io.jsonwebtoken.SignatureAlgorithm; | 
				
			|||
import org.slf4j.Logger; | 
				
			|||
import org.slf4j.LoggerFactory; | 
				
			|||
import org.springframework.beans.factory.annotation.Autowired; | 
				
			|||
import org.springframework.stereotype.Component; | 
				
			|||
 | 
				
			|||
import java.util.Date; | 
				
			|||
import java.util.Map; | 
				
			|||
 | 
				
			|||
/** | 
				
			|||
 * Jwt工具类 | 
				
			|||
 * | 
				
			|||
 * @author Mark sunlightcs@gmail.com | 
				
			|||
 * @since 1.0.0 | 
				
			|||
 */ | 
				
			|||
@Component | 
				
			|||
public class JwtTokenUtils { | 
				
			|||
    private static final Logger logger = LoggerFactory.getLogger(JwtTokenUtils.class); | 
				
			|||
 | 
				
			|||
    @Autowired | 
				
			|||
    private JwtTokenProperties jwtProperties; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 生成上报接口accessToken | 
				
			|||
     * | 
				
			|||
     * @param claims | 
				
			|||
     * @return java.lang.String | 
				
			|||
     * @author Liuchuang | 
				
			|||
     * @since 2020/9/7 14:11 | 
				
			|||
     */ | 
				
			|||
    public String getEpmetAccessToken(Map<String,Object> claims){ | 
				
			|||
        return Jwts.builder() | 
				
			|||
                .setHeaderParam("typ", "JWT") | 
				
			|||
                .setClaims(claims) | 
				
			|||
                .signWith(SignatureAlgorithm.HS512, jwtProperties.getSecret()) | 
				
			|||
                .compact(); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    public Claims getClaimByToken(String token) { | 
				
			|||
        try { | 
				
			|||
            return Jwts.parser() | 
				
			|||
                    .setSigningKey(jwtProperties.getSecret()) | 
				
			|||
                    .parseClaimsJws(token) | 
				
			|||
                    .getBody(); | 
				
			|||
        } catch (Exception e) { | 
				
			|||
            logger.debug("validate is token error, token = " + token, e); | 
				
			|||
            return null; | 
				
			|||
        } | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * token是否过期 | 
				
			|||
     * | 
				
			|||
     * @return true:过期 | 
				
			|||
     */ | 
				
			|||
    public boolean isTokenExpired(Date expiration) { | 
				
			|||
        return expiration.before(new Date()); | 
				
			|||
    } | 
				
			|||
} | 
				
			|||
@ -0,0 +1,36 @@ | 
				
			|||
package com.elink.esua.epdc.modules.worklog; | 
				
			|||
 | 
				
			|||
import com.elink.esua.epdc.commons.tools.utils.Result; | 
				
			|||
import org.springframework.beans.factory.annotation.Value; | 
				
			|||
import org.springframework.web.bind.annotation.GetMapping; | 
				
			|||
import org.springframework.web.bind.annotation.RequestMapping; | 
				
			|||
import org.springframework.web.bind.annotation.RestController; | 
				
			|||
 | 
				
			|||
/** | 
				
			|||
 * @Description 获取customID | 
				
			|||
 * @Author songyunpeng | 
				
			|||
 * @Date 2020/2/10 16:06 | 
				
			|||
 */ | 
				
			|||
@RestController | 
				
			|||
@RequestMapping("workLog") | 
				
			|||
public class WorkLogAnalysisController { | 
				
			|||
 | 
				
			|||
    @Value("${epmet.config.customerId}") | 
				
			|||
    private String customId; | 
				
			|||
 | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * @Description  获取customId | 
				
			|||
     * @Author songyunpeng | 
				
			|||
     * @Date  2021/1/27 | 
				
			|||
     * @Param [] | 
				
			|||
     * @return com.elink.esua.epdc.commons.tools.utils.Result | 
				
			|||
     **/ | 
				
			|||
    @GetMapping("getCustomId") | 
				
			|||
    public Result<String> getCustomId(){ | 
				
			|||
        return new Result<String>().ok(customId); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
} | 
				
			|||
@ -0,0 +1,20 @@ | 
				
			|||
package com.elink.esua.epdc.dto.result; | 
				
			|||
 | 
				
			|||
import com.elink.esua.epdc.dto.DeptOption; | 
				
			|||
import lombok.Data; | 
				
			|||
 | 
				
			|||
import java.util.List; | 
				
			|||
 | 
				
			|||
/** | 
				
			|||
 * @author songyunpeng | 
				
			|||
 * @Description | 
				
			|||
 * @create 2021-02-05 | 
				
			|||
 */ | 
				
			|||
@Data | 
				
			|||
public class EpdcAppWorkLogUserResult { | 
				
			|||
 | 
				
			|||
 | 
				
			|||
    private DeptOption deptOption; | 
				
			|||
 | 
				
			|||
    private List<Long> deptIds; | 
				
			|||
} | 
				
			|||
@ -0,0 +1,96 @@ | 
				
			|||
/** | 
				
			|||
 * Copyright (c) 2018 人人开源 All rights reserved. | 
				
			|||
 * <p> | 
				
			|||
 * https://www.renren.io
 | 
				
			|||
 * <p> | 
				
			|||
 * 版权所有,侵权必究! | 
				
			|||
 */ | 
				
			|||
 | 
				
			|||
package com.elink.esua.epdc.dto.result; | 
				
			|||
 | 
				
			|||
import lombok.Data; | 
				
			|||
 | 
				
			|||
import java.io.Serializable; | 
				
			|||
 | 
				
			|||
/** | 
				
			|||
 * 工作日志 用户信息 | 
				
			|||
 * | 
				
			|||
 * @author Mark sunlightcs@gmail.com | 
				
			|||
 * @since 1.0.0 | 
				
			|||
 */ | 
				
			|||
@Data | 
				
			|||
public class EpdcWorkLogUserDetailDTO implements Serializable { | 
				
			|||
 | 
				
			|||
    private static final long serialVersionUID = 3908231797102233188L; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 用户ID | 
				
			|||
     */ | 
				
			|||
    private String userId; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 微信昵称 | 
				
			|||
     */ | 
				
			|||
    private String nickname; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 头像 | 
				
			|||
     */ | 
				
			|||
    private String profile; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 真实姓名 | 
				
			|||
     */ | 
				
			|||
    private String realName; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 客户Id | 
				
			|||
     */ | 
				
			|||
    private String customerId; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 客户名称 | 
				
			|||
     */ | 
				
			|||
    private String customerName; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 所属组织Id | 
				
			|||
     */ | 
				
			|||
    private String agencyId; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 所属组织Id路径 | 
				
			|||
     */ | 
				
			|||
    private String agencyIdPath; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 所属组织名称 | 
				
			|||
     */ | 
				
			|||
    private String agencyName; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 所属组织名称路径 | 
				
			|||
     */ | 
				
			|||
    private String agencyNamePath; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 所在网格Id(工作人员最一次访问的网格) | 
				
			|||
     */ | 
				
			|||
    private String gridId; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 所在网格名称 | 
				
			|||
     */ | 
				
			|||
    private String gridName; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 是否总管理员,1是0否 | 
				
			|||
     */ | 
				
			|||
    private String adminFlag; | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * 手机号 | 
				
			|||
     */ | 
				
			|||
    private String mobile; | 
				
			|||
 | 
				
			|||
} | 
				
			|||
@ -0,0 +1,62 @@ | 
				
			|||
package com.elink.esua.epdc.controller; | 
				
			|||
 | 
				
			|||
import com.elink.esua.epdc.commons.tools.utils.Result; | 
				
			|||
import com.elink.esua.epdc.dto.result.EpdcAppWorkLogUserResult; | 
				
			|||
import com.elink.esua.epdc.dto.result.EpdcWorkLogUserDetailDTO; | 
				
			|||
import com.elink.esua.epdc.service.AdminService; | 
				
			|||
import org.springframework.beans.factory.annotation.Autowired; | 
				
			|||
import org.springframework.web.bind.annotation.GetMapping; | 
				
			|||
import org.springframework.web.bind.annotation.PostMapping; | 
				
			|||
import org.springframework.web.bind.annotation.RequestMapping; | 
				
			|||
import org.springframework.web.bind.annotation.RestController; | 
				
			|||
 | 
				
			|||
/** | 
				
			|||
 * 工作日志相关 | 
				
			|||
 * | 
				
			|||
 * @author songyunpeng | 
				
			|||
 * @date 2021/1/27 10:30 | 
				
			|||
 */ | 
				
			|||
@RestController | 
				
			|||
@RequestMapping("plugins/workLog") | 
				
			|||
public class ApiWorkLogController { | 
				
			|||
    @Autowired | 
				
			|||
    private AdminService adminService; | 
				
			|||
    /** | 
				
			|||
     * @Description  获取用户信息 | 
				
			|||
     * @Author songyunpeng | 
				
			|||
     * @Date  2021/1/27 | 
				
			|||
     * @Param [] | 
				
			|||
     * @return com.elink.esua.epdc.commons.tools.utils.Result<com.elink.esua.epdc.dto.result.EpdcWorkLogUserDetailDTO> | 
				
			|||
     **/ | 
				
			|||
    @PostMapping("getUserInfo") | 
				
			|||
    public Result<EpdcWorkLogUserDetailDTO> getUserInfo(){ | 
				
			|||
        return adminService.getUserInfo(); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
     /** | 
				
			|||
     * @Description  获取用户部门权限 | 
				
			|||
     * @Author songyunpeng | 
				
			|||
     * @Date  2021/1/27 | 
				
			|||
     * @Param [] | 
				
			|||
     * @return com.elink.esua.epdc.commons.tools.utils.Result<com.elink.esua.epdc.dto.result.EpdcWorkLogUserDetailDTO> | 
				
			|||
     **/ | 
				
			|||
    @PostMapping("getUserDeptOptionByUserId") | 
				
			|||
    public Result<EpdcAppWorkLogUserResult> getUserDeptOptionByUserId(){ | 
				
			|||
        return adminService.getUserDeptOptionByUserId(); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * @Description  获取customId | 
				
			|||
     * @Author songyunpeng | 
				
			|||
     * @Date  2021/1/27 | 
				
			|||
     * @Param [] | 
				
			|||
     * @return com.elink.esua.epdc.commons.tools.utils.Result | 
				
			|||
     **/ | 
				
			|||
    @GetMapping("getCustomId") | 
				
			|||
    public Result<String> getCustomId(){ | 
				
			|||
        return adminService.getCustomId(); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
} | 
				
			|||
@ -0,0 +1,30 @@ | 
				
			|||
package com.elink.esua.epdc.feign; | 
				
			|||
 | 
				
			|||
import com.elink.esua.epdc.commons.tools.constant.ServiceConstant; | 
				
			|||
import com.elink.esua.epdc.commons.tools.utils.Result; | 
				
			|||
import com.elink.esua.epdc.feign.fallback.AnalysisFeignClientFallback; | 
				
			|||
import org.springframework.cloud.openfeign.FeignClient; | 
				
			|||
import org.springframework.http.MediaType; | 
				
			|||
import org.springframework.web.bind.annotation.GetMapping; | 
				
			|||
 | 
				
			|||
/** | 
				
			|||
 * 数据分析模块调用 | 
				
			|||
 * | 
				
			|||
 * @Author:liuchuang | 
				
			|||
 * @Date:2020/9/9 15:41 | 
				
			|||
 */ | 
				
			|||
@FeignClient(name = ServiceConstant.EPDC_ANALYSIS_SERVER, fallback = AnalysisFeignClientFallback.class) | 
				
			|||
public interface AnalysisFeignClient { | 
				
			|||
 | 
				
			|||
    /** | 
				
			|||
     * @Description  组织机构编码获取 | 
				
			|||
     * @Author songyunpeng | 
				
			|||
     * @Date  2021/1/7 | 
				
			|||
     * @Param [] | 
				
			|||
     * @return com.elink.esua.epdc.commons.tools.utils.Result | 
				
			|||
     **/ | 
				
			|||
    @GetMapping(value = "analysis/workLog/getCustomId", consumes = MediaType.APPLICATION_JSON_VALUE) | 
				
			|||
    Result<String> getCustomId(); | 
				
			|||
 | 
				
			|||
 | 
				
			|||
} | 
				
			|||
@ -0,0 +1,20 @@ | 
				
			|||
package com.elink.esua.epdc.feign.fallback; | 
				
			|||
 | 
				
			|||
import com.elink.esua.epdc.commons.tools.constant.ServiceConstant; | 
				
			|||
import com.elink.esua.epdc.commons.tools.utils.ModuleUtils; | 
				
			|||
import com.elink.esua.epdc.commons.tools.utils.Result; | 
				
			|||
import com.elink.esua.epdc.feign.AnalysisFeignClient; | 
				
			|||
import org.springframework.stereotype.Component; | 
				
			|||
 | 
				
			|||
/** | 
				
			|||
 * @Author:songyunpeg | 
				
			|||
 * @Date:2020/9/9 15:42 | 
				
			|||
 */ | 
				
			|||
@Component | 
				
			|||
public class AnalysisFeignClientFallback implements AnalysisFeignClient { | 
				
			|||
 | 
				
			|||
    @Override | 
				
			|||
    public Result getCustomId() { | 
				
			|||
        return ModuleUtils.feignConError(ServiceConstant.EPDC_ANALYSIS_SERVER, "getCustomId"); | 
				
			|||
    } | 
				
			|||
} | 
				
			|||
					Loading…
					
					
				
		Reference in new issue