xiugai
parent
78de340044
commit
777ab4ac11
|
@ -0,0 +1,32 @@
|
|||
package com.api.xuanran.wang.eny.export_excel.controller;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* <h1>安永 建模自定义导出excel</h1>
|
||||
*
|
||||
* @author xuanran.wang
|
||||
* @date 2023/6/7 11:01
|
||||
*/
|
||||
@Path("/wxr/eny/excel")
|
||||
public class ExportExcelController {
|
||||
|
||||
@POST
|
||||
@Path("/cus_port")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public Response exportExcel(@Context HttpServletRequest request,
|
||||
@Context HttpServletResponse response){
|
||||
return null;
|
||||
// return Response.ok(streamingOutput, MediaType.APPLICATION_OCTET_STREAM).type("application/zip")
|
||||
// .header("Content-Disposition", "attachment;filename=" +
|
||||
// new String(packageName.toString().getBytes("GBK"), StandardCharsets.ISO_8859_1) + ".zip").build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.api.xuanran.wang.eny.export_excel.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* <h1>建模自定义导出excel 明细配置对象</h1>
|
||||
*
|
||||
* @author xuanran.wang
|
||||
* @date 2023/6/7 11:12
|
||||
*/
|
||||
@Data
|
||||
public class CusExportExcelConfigDetail {
|
||||
private String excel_row;
|
||||
private String model_field;
|
||||
private String get_value_type;
|
||||
private String value_context;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.api.xuanran.wang.eny.export_excel.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <h1>建模自定义导出excel 主表配置对象</h1>
|
||||
*
|
||||
* @author xuanran.wang
|
||||
* @date 2023/6/7 11:12
|
||||
*/
|
||||
@Data
|
||||
public class CusExportExcelConfigMain {
|
||||
private String model_table;
|
||||
private String template_path;
|
||||
private String file_name;
|
||||
private List<CusExportExcelConfigDetail> configDetailList;
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package com.api.xuanran.wang.eny.export_excel.mapper;
|
||||
|
||||
import aiyh.utils.annotation.recordset.*;
|
||||
import com.api.xuanran.wang.eny.export_excel.entity.CusExportExcelConfigDetail;
|
||||
import com.api.xuanran.wang.eny.export_excel.entity.CusExportExcelConfigMain;
|
||||
import com.api.xuanran.wang.traffic_bank.email.entity.EmailOutConfigDetail;
|
||||
import com.api.xuanran.wang.traffic_bank.email.entity.EmailOutConfigMain;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <h1>建模自定义导出excel mapper</h1>
|
||||
*
|
||||
* @author xuanran.wang
|
||||
* @date 2023/6/7 11:21
|
||||
*/
|
||||
@SqlMapper
|
||||
public interface ExportExcelMapper {
|
||||
@Select("select * from uf_mod_export_excel where model_table = #{modelTable}")
|
||||
@CollectionMappings({
|
||||
@CollectionMapping(property = "configDetailList",
|
||||
column = "id",
|
||||
id = @Id(value = Integer.class, methodId = 1))
|
||||
})
|
||||
CusExportExcelConfigMain selectConfigByModelId(@ParamMapper("modelTable") int modelTable);
|
||||
|
||||
/**
|
||||
* <h1>查询配置表明细表信息</h1>
|
||||
* @author xuanran.wang
|
||||
* @dateTime 2023/3/1 16:39
|
||||
* @param mainId 主表数据id
|
||||
* @return 配置集合
|
||||
**/
|
||||
@Select("select a.*,b.fieldname from uf_mod_export_excel_dt1 a " +
|
||||
"left join workflow_field_table_view b " +
|
||||
"on a.model_field = b.id " +
|
||||
"where mainid = #{mainId}")
|
||||
@CollectionMethod(1)
|
||||
List<CusExportExcelConfigDetail> selectConfigDetail(@ParamMapper("mainId") int mainId);
|
||||
|
||||
@Select(custom = true)
|
||||
String selectCustomerSql(@SqlString String sql, Map<String, Object> map);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package com.api.xuanran.wang.eny.export_excel.service;
|
||||
|
||||
import aiyh.utils.Util;
|
||||
import com.api.xuanran.wang.eny.export_excel.entity.CusExportExcelConfigMain;
|
||||
import com.api.xuanran.wang.eny.export_excel.mapper.ExportExcelMapper;
|
||||
|
||||
/**
|
||||
* <h1>自定义导出excel</h1>
|
||||
*
|
||||
* @author xuanran.wang
|
||||
* @date 2023/6/7 11:11
|
||||
*/
|
||||
public class ExportExcelServiceImpl {
|
||||
private final ExportExcelMapper exportExcelMapper = Util.getMapper(ExportExcelMapper.class);
|
||||
|
||||
/**
|
||||
* <h1>根据模块id查询配置</h1>
|
||||
* @author xuanran.wang
|
||||
* @dateTime 2023/6/7 11:28
|
||||
* @param modelId 模块id
|
||||
* @return 配置对象
|
||||
**/
|
||||
public CusExportExcelConfigMain getConfig(int modelId){
|
||||
return exportExcelMapper.selectConfigByModelId(modelId);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -44,12 +44,18 @@ public class OtherSystemToOAController {
|
|||
}
|
||||
// 获取重定向地址和secret
|
||||
Map<String, String> redirectUrlAndCorpsecret = service.getRedirectUrlAndCorpsecret(appId);
|
||||
// 重定向地址
|
||||
String redirectUrl = Util.null2DefaultStr(redirectUrlAndCorpsecret.get("REDIRECTURL"),"");
|
||||
if(StringUtils.isBlank(redirectUrl)){
|
||||
throw new CustomerException("redirectUrl is null! " + JSONObject.toJSONString(redirectUrlAndCorpsecret));
|
||||
}
|
||||
int userId = service.getUserFromOtherSys(code, redirectUrlAndCorpsecret);
|
||||
SessionUtil.createSession(String.valueOf(userId), request, response);
|
||||
// 是否来自统一待办
|
||||
String tempUrl = checkUrlFromTodoTask(request, userId);
|
||||
if(StringUtils.isNotBlank(tempUrl)){
|
||||
redirectUrl = tempUrl;
|
||||
}
|
||||
log.info("successSendRedirectUrl : " + redirectUrl);
|
||||
response.sendRedirect(redirectUrl);
|
||||
}catch (Exception e){
|
||||
|
@ -63,4 +69,35 @@ public class OtherSystemToOAController {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h1>校验请求是否来自与统一待办推送</h1>
|
||||
* @author xuanran.wang
|
||||
* @dateTime 2023/6/5 11:14
|
||||
* @param request 浏览器请求对象
|
||||
* @param userId 从接口获取到到userId
|
||||
* @return 跳转url
|
||||
**/
|
||||
private String checkUrlFromTodoTask(HttpServletRequest request, int userId){
|
||||
String redirectUrl = "";
|
||||
// 统一待办传过来的user
|
||||
int todoUrlUser = Util.getIntValue(request.getParameter("user"),-1);
|
||||
String requestId = Util.null2DefaultStr(request.getParameter("requestId"),"");
|
||||
if(todoUrlUser > 0 && StringUtils.isNotBlank(requestId)){
|
||||
if(todoUrlUser != userId){
|
||||
throw new CustomerException("统一待办链接参数user与接口获取转换成OA用户id不一致!当前接口参数userId : " + todoUrlUser + " 接口获取到转换成OA人员userId : " + userId);
|
||||
}
|
||||
// 1是来自移动端
|
||||
String mobile = Util.null2DefaultStr(request.getParameter("mobile"), "");
|
||||
if("1".equals(mobile)){
|
||||
redirectUrl = ShBigDataUtil.getPropertiesValByKey("taskMobileUrl");
|
||||
}else {
|
||||
redirectUrl = ShBigDataUtil.getPropertiesValByKey("taskPcUrl");
|
||||
}
|
||||
redirectUrl = redirectUrl
|
||||
.replace("{requestId}", requestId)
|
||||
.replace("userId", String.valueOf(userId));
|
||||
}
|
||||
return redirectUrl;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ public class CusData2OA {
|
|||
String updateSql = "";
|
||||
|
||||
for (Map<String, Object> param : params) {
|
||||
log.info("写入日志参数 : " + com.alibaba.fastjson.JSONObject.toJSONString(param));
|
||||
if(StringUtils.isNotBlank(uniqueSql)){
|
||||
modelDataId = commonMapper.selectCustomerSql(uniqueSql, param);
|
||||
}
|
||||
|
@ -72,6 +73,7 @@ public class CusData2OA {
|
|||
modelDataList.add(modelDataId);
|
||||
}
|
||||
try {
|
||||
log.info("updateSql => " + updateSql);
|
||||
if (!commonMapper.updateModelInfoList(updateSql, params)) {
|
||||
throw new CustomerException("update model data sql execute error!");
|
||||
}
|
||||
|
|
|
@ -68,7 +68,6 @@ public class WorkflowToSapServiceImpl implements WorkflowToSapService {
|
|||
params.put("responseCode","");
|
||||
params.put("sapResponse", response);
|
||||
params.put("error", error);
|
||||
log.info("写入日志参数 : " + com.alibaba.fastjson.JSONObject.toJSONString(params));
|
||||
CusData2OA.writeToModel(modelId, "select id from #{tableName} where reqId = " + requestId, params);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package weaver.xuanran.wang.eighty_five_degreec.sap.util;
|
||||
|
||||
import aiyh.utils.excention.CustomerException;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.google.common.base.Strings;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -137,7 +138,10 @@ public class ReadConfigUtil extends ToolUtil {
|
|||
switch (getValueType){
|
||||
// 流程字段
|
||||
case "0":{
|
||||
String fieldName = detailRequestConfig.getFieldName();
|
||||
String fieldName = Util.null2String(detailRequestConfig.getFieldName());
|
||||
if(StringUtils.isBlank(fieldName)){
|
||||
break;
|
||||
}
|
||||
if("0".equals(detailId)){
|
||||
value = Util.null2String(mainRs.getString(fieldName));
|
||||
}else{
|
||||
|
@ -345,6 +349,7 @@ public class ReadConfigUtil extends ToolUtil {
|
|||
// 主表
|
||||
if ("0".equals(dataSource)) {
|
||||
mainXml = getMainXml(requestConfigList, mainRs, detailRs);
|
||||
detailXml = getDetailXml(requestConfigList, mainRs, detailRs, "-1", tableName, mainId);
|
||||
} else if("1".equals(dataSource)) {
|
||||
// 仅明细
|
||||
detailXml = getDetailXml(requestConfigList, mainRs, detailRs, config.getDetailIndex(), tableName, mainId);
|
||||
|
@ -445,6 +450,7 @@ public class ReadConfigUtil extends ToolUtil {
|
|||
StringBuilder detailSb = new StringBuilder();
|
||||
|
||||
for (String detailId : detailIdArr) {
|
||||
// 集合数据的根节点名称
|
||||
String nodeName;
|
||||
List<DetailRequestConfig> nodeList = requestConfigList.stream()
|
||||
.filter(item -> "0".equals(item.getNodeType())).collect(Collectors.toList());
|
||||
|
@ -453,6 +459,7 @@ public class ReadConfigUtil extends ToolUtil {
|
|||
} else {
|
||||
nodeName = "";
|
||||
}
|
||||
// 集合数据
|
||||
List<DetailRequestConfig> detailCollect = requestConfigList.stream()
|
||||
.filter(item -> {
|
||||
if(StringUtils.isNotBlank(nodeName)){
|
||||
|
@ -461,18 +468,68 @@ public class ReadConfigUtil extends ToolUtil {
|
|||
return "detailRecord".equals(item.getParentName());
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
String detailSql = "select * from " + tableName + "_dt" + detailId + " where mainid = ?";
|
||||
if (detailRs.executeQuery(detailSql, mainId)) {
|
||||
log.info("detailCollect : " + JSONObject.toJSONString(detailCollect));
|
||||
if("-1".equals(detailId)){
|
||||
Set<DetailRequestConfig> configs = new HashSet<>(detailCollect);
|
||||
log.info("configs => " + JSONObject.toJSONString(configs));
|
||||
Map<String, Long> countMap = configs.stream()
|
||||
.collect(Collectors.groupingBy(DetailRequestConfig::getParamName, Collectors.counting()));
|
||||
log.info("countMap => " + JSONObject.toJSONString(countMap));
|
||||
// 出现一次的数据
|
||||
List<DetailRequestConfig> uniqueConfigs = configs.stream()
|
||||
.filter(config -> countMap.get(config.getParamName()) == 1)
|
||||
.collect(Collectors.toList());
|
||||
log.info("出现一次的数据 : " + JSONObject.toJSONString(uniqueConfigs));
|
||||
// 出现多次的数据
|
||||
List<DetailRequestConfig> collect = configs.stream().filter(item -> !uniqueConfigs.contains(item)).collect(Collectors.toList());
|
||||
log.info("出现多次的数据 : " + JSONObject.toJSONString(collect));
|
||||
// 出现多次的key
|
||||
Set<String> set = new HashSet<>();
|
||||
for (DetailRequestConfig config : collect) {
|
||||
set.add(config.getParamName());
|
||||
}
|
||||
log.info("出现多次的key : " + JSONObject.toJSONString(set));
|
||||
List<List<DetailRequestConfig>> lists = new ArrayList<>();
|
||||
// 已经添加过的数据
|
||||
List<DetailRequestConfig> has = new ArrayList<>();
|
||||
long maxValue = Collections.max(countMap.values());
|
||||
for (int i = 0; i < maxValue; i++) {
|
||||
List<DetailRequestConfig> list = new ArrayList<>(uniqueConfigs);
|
||||
for (String param : set) {
|
||||
List<DetailRequestConfig> add = collect.stream().filter(item -> param.equals(item.getParamName()) && !has.contains(item)).collect(Collectors.toList());
|
||||
if(CollectionUtils.isEmpty(add)){
|
||||
continue;
|
||||
}
|
||||
list.add(add.get(0));
|
||||
has.add(add.get(0));
|
||||
}
|
||||
lists.add(list);
|
||||
}
|
||||
int count = 1;
|
||||
while (detailRs.next()) {
|
||||
log.info("lists : " + JSONObject.toJSONString(lists));
|
||||
for (List<DetailRequestConfig> list : lists) {
|
||||
if(StringUtils.isNotBlank(nodeName)){
|
||||
detailSb.append("<").append(nodeName).append(">\n");
|
||||
}
|
||||
appendXml(detailSb, detailCollect, mainRs, detailRs, count++);
|
||||
appendXml(detailSb, list, mainRs, detailRs, count++);
|
||||
if(StringUtils.isNotBlank(nodeName)){
|
||||
detailSb.append("</").append(nodeName).append(">\n");
|
||||
}
|
||||
}
|
||||
}else {
|
||||
String detailSql = "select * from " + tableName + "_dt" + detailId + " where mainid = ?";
|
||||
if (detailRs.executeQuery(detailSql, mainId)) {
|
||||
int count = 1;
|
||||
while (detailRs.next()) {
|
||||
if(StringUtils.isNotBlank(nodeName)){
|
||||
detailSb.append("<").append(nodeName).append(">\n");
|
||||
}
|
||||
appendXml(detailSb, detailCollect, mainRs, detailRs, count++);
|
||||
if(StringUtils.isNotBlank(nodeName)){
|
||||
detailSb.append("</").append(nodeName).append(">\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return detailSb.toString();
|
||||
|
|
|
@ -74,10 +74,18 @@ public class SendTodoTaskUtil {
|
|||
todoTask.setAgentid(agentId);
|
||||
todoTask.setTaskName(obj.getRequestnamenew());
|
||||
todoTask.setTaskDesc(obj.getRequestnamenew());
|
||||
todoTask.setLinkUrl(Util.null2DefaultStr(ShBigDataUtil.getPropertiesValByKey("taskPcUrl"), oaAddress) + "/spa/workflow/static4form/index.html?#/main/workflow/req?requestid="+requestId);
|
||||
todoTask.setMobileLinkUrl(Util.null2DefaultStr(ShBigDataUtil.getPropertiesValByKey("taskMobileUrl"), oaAddress) + "/spa/workflow/static4mobileform/index.html?#/req?requestid="+requestId);
|
||||
todoTask.setSender(getConvertHrm(0, obj, obj.getCreator().getUID() + ""));
|
||||
todoTask.setReceiver(getConvertHrm(1, obj,userId + ""));
|
||||
String todoSSOCallBackUrl = ShBigDataUtil.getPropertiesValByKey("todoSSOCallBackUrl");
|
||||
StringBuilder sb = new StringBuilder(todoSSOCallBackUrl);
|
||||
sb.append("?user=")
|
||||
.append(userId)
|
||||
.append("&requestId=")
|
||||
.append(requestId);
|
||||
// todoTask.setLinkUrl(Util.null2DefaultStr(ShBigDataUtil.getPropertiesValByKey("taskPcUrl"), oaAddress) + "/spa/workflow/static4form/index.html?#/main/workflow/req?requestid="+requestId);
|
||||
// todoTask.setMobileLinkUrl(Util.null2DefaultStr(ShBigDataUtil.getPropertiesValByKey("taskMobileUrl"), oaAddress) + "/spa/workflow/static4mobileform/index.html?#/req?requestid="+requestId);
|
||||
todoTask.setLinkUrl(sb.toString());
|
||||
todoTask.setMobileLinkUrl(sb.append("&mobile=1").toString());
|
||||
todoTask.setSender(getConvertHrm(0, obj, String.valueOf(obj.getCreator().getUID())));
|
||||
todoTask.setReceiver(getConvertHrm(1, obj, String.valueOf(userId)));
|
||||
res.add(todoTask);
|
||||
}
|
||||
return res;
|
||||
|
|
|
@ -56,7 +56,7 @@ public class SendTodoTaskServiceImpl implements SendTodoTaskService {
|
|||
try {
|
||||
CusSuccess cusSuccess = CusSuccess.builder()
|
||||
.successField("code")
|
||||
.successValue(200)
|
||||
.successValue(0)
|
||||
.errorMsg("msg")
|
||||
.dataKey("")
|
||||
.build();
|
||||
|
@ -104,7 +104,7 @@ public class SendTodoTaskServiceImpl implements SendTodoTaskService {
|
|||
try {
|
||||
CusSuccess cusSuccess = CusSuccess.builder()
|
||||
.successField("code")
|
||||
.successValue(200)
|
||||
.successValue(0)
|
||||
.errorMsg("msg")
|
||||
.dataKey("")
|
||||
.build();
|
||||
|
|
|
@ -21,11 +21,13 @@ hrmSenderConvertRuleSql=select lastname from hrmresource where id in (${ids})
|
|||
# 与上面同理
|
||||
hrmReceiveConvertRuleSql=select lastname from hrmresource where id in (${ids})
|
||||
# 统一待办pc端链接地址
|
||||
taskPcUrl=
|
||||
taskPcUrl=http://127.0.0.1/spa/workflow/static4form/index.html?#/main/workflow/req?requestid={requestId}
|
||||
# 统一待办移动端链接地址
|
||||
taskMobileUrl=
|
||||
taskMobileUrl=http://127.0.0.1/spa/workflow/static4mobileform/index.html?#/req?requestid={requestId}
|
||||
# oa token缓存对象提前过期时间
|
||||
expiryBeforeTime=5
|
||||
# 统一待办单点接口地址
|
||||
todoSSOCallBackUrl=http://127.0.0.1/api/wxr/sh_big_data/sso/login/dhajhdsajdhsajdaskdlakdl
|
||||
|
||||
# ================ 组织架构同步新增 ================
|
||||
# 分部最大的层级
|
||||
|
@ -51,4 +53,8 @@ loginErrorSendRedirectUrl=/login/login.jsp
|
|||
getUserIdDebug=
|
||||
#debug key
|
||||
getUserIdDebugOutKey=
|
||||
# sso校验时 oa取值字段
|
||||
ssoOaCompareField=
|
||||
# sso校验时 接口取值字段
|
||||
ssoInterfaceCompareField=
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package xuanran.wang.eny;
|
||||
|
||||
import basetest.BaseTest;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.api.xuanran.wang.eny.export_excel.entity.CusExportExcelConfigMain;
|
||||
import com.api.xuanran.wang.eny.export_excel.service.ExportExcelServiceImpl;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* <h1></h1>
|
||||
*
|
||||
* @author xuanran.wang
|
||||
* @date 2023/6/7 11:29
|
||||
*/
|
||||
public class ExcelExportTest extends BaseTest {
|
||||
|
||||
private final ExportExcelServiceImpl exportExcelService = new ExportExcelServiceImpl();
|
||||
|
||||
@Test
|
||||
public void testA(){
|
||||
CusExportExcelConfigMain config = exportExcelService.getConfig(126);
|
||||
System.out.println("config : " + JSONObject.toJSONString(config));
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package xuanran.wang.shyl.dataasync;
|
||||
|
||||
import aiyh.utils.Util;
|
||||
import basetest.BaseTest;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.api.meeting.util.FieldUtil;
|
||||
|
@ -8,8 +9,8 @@ import com.api.xuanran.wang.shyl.service.MeetingService;
|
|||
import org.junit.Test;
|
||||
import weaver.file.ImageFileManager;
|
||||
import weaver.general.TimeUtil;
|
||||
import weaver.general.Util;
|
||||
import weaver.systeminfo.SystemEnv;
|
||||
import weaver.xuanran.wang.shyl_mq.mapper.ConsumerMapper;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
|
@ -26,6 +27,8 @@ import java.util.*;
|
|||
public class MapperTest extends BaseTest {
|
||||
|
||||
private final MeetingService meetingService = new MeetingService();
|
||||
|
||||
protected final ConsumerMapper consumerMapper = Util.getMapper(ConsumerMapper.class);
|
||||
@Test
|
||||
public void testA(){
|
||||
List<MeetingCusFieldConfigMain> meetingFieldCusConfig = meetingService.getMeetingFieldCusConfig();
|
||||
|
@ -72,4 +75,8 @@ public class MapperTest extends BaseTest {
|
|||
cusSql = cusSql.replace("${docIds}", "( " + docIds + " )");
|
||||
System.out.println("cus => " + cusSql);
|
||||
}
|
||||
@Test
|
||||
public void testE(){
|
||||
System.out.println( consumerMapper.updateHrmLoginIdById("868", "F121323132"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue