附件上传sftp
This commit is contained in:
parent
43afeb2e94
commit
cea473d2f2
|
@ -1,8 +1,10 @@
|
||||||
package shkd.fi.fi.task.impl;
|
package shkd.fi.fi.task.impl;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.jcraft.jsch.ChannelSftp;
|
||||||
import kd.bos.context.RequestContext;
|
import kd.bos.context.RequestContext;
|
||||||
import kd.bos.dataentity.entity.DynamicObject;
|
import kd.bos.dataentity.entity.DynamicObject;
|
||||||
|
import kd.bos.fileservice.FileServiceFactory;
|
||||||
import kd.bos.logging.Log;
|
import kd.bos.logging.Log;
|
||||||
import kd.bos.logging.LogFactory;
|
import kd.bos.logging.LogFactory;
|
||||||
import kd.bos.orm.query.QCP;
|
import kd.bos.orm.query.QCP;
|
||||||
|
@ -12,6 +14,7 @@ import kd.bos.servicehelper.BusinessDataServiceHelper;
|
||||||
import kd.bos.servicehelper.operation.SaveServiceHelper;
|
import kd.bos.servicehelper.operation.SaveServiceHelper;
|
||||||
import kd.bos.util.HttpClientUtils;
|
import kd.bos.util.HttpClientUtils;
|
||||||
import kd.bos.util.StringUtils;
|
import kd.bos.util.StringUtils;
|
||||||
|
import kd.bos.web.actions.utils.FilePathUtil;
|
||||||
import org.apache.axis2.AxisFault;
|
import org.apache.axis2.AxisFault;
|
||||||
import shkd.fi.fi.common.AppflgConstant;
|
import shkd.fi.fi.common.AppflgConstant;
|
||||||
import shkd.fi.fi.common.cosmic.accesstoken.AccessTokenResult;
|
import shkd.fi.fi.common.cosmic.accesstoken.AccessTokenResult;
|
||||||
|
@ -21,8 +24,13 @@ import shkd.fi.fi.oa.ModeDateServiceStub;
|
||||||
import shkd.fi.fi.util.DateUtils;
|
import shkd.fi.fi.util.DateUtils;
|
||||||
import shkd.fi.fi.util.LogBillUtils;
|
import shkd.fi.fi.util.LogBillUtils;
|
||||||
import shkd.fi.fi.util.ParamsUtils;
|
import shkd.fi.fi.util.ParamsUtils;
|
||||||
|
import shkd.fi.fi.util.SFTPConnectUtil;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.rmi.RemoteException;
|
import java.rmi.RemoteException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -49,6 +57,8 @@ public class OAInvoiceImpl {
|
||||||
}
|
}
|
||||||
Map<String, String> paramsMap = ParamsUtils.getParamsMap();
|
Map<String, String> paramsMap = ParamsUtils.getParamsMap();
|
||||||
|
|
||||||
|
//
|
||||||
|
ChannelSftp sftp = SFTPConnectUtil.connectSFTP("10.13.11.210", 3777, "root", "w@4G##4S#66#");
|
||||||
|
|
||||||
StringBuffer result = new StringBuffer();
|
StringBuffer result = new StringBuffer();
|
||||||
Map<Object, DynamicObject> gl_voucher = BusinessDataServiceHelper.loadFromCache("shkd_invoicereceipt", new QFilter[]{filter3.and(filter4)});
|
Map<Object, DynamicObject> gl_voucher = BusinessDataServiceHelper.loadFromCache("shkd_invoicereceipt", new QFilter[]{filter3.and(filter4)});
|
||||||
|
@ -57,7 +67,7 @@ public class OAInvoiceImpl {
|
||||||
for (Map.Entry<Object, DynamicObject> inoice : gl_voucher.entrySet()) {
|
for (Map.Entry<Object, DynamicObject> inoice : gl_voucher.entrySet()) {
|
||||||
count = count + 1;
|
count = count + 1;
|
||||||
DynamicObject inoiceObj = inoice.getValue();
|
DynamicObject inoiceObj = inoice.getValue();
|
||||||
result.append("第").append(count).append("张单据,编号:").append(inoiceObj.getString("billno")).append(synData(paramsMap,inoiceObj,result));
|
result.append("第").append(count).append("张单据,编号:").append(inoiceObj.getString("billno")).append(synData(paramsMap,inoiceObj,result,sftp));
|
||||||
}
|
}
|
||||||
if(gl_voucher.isEmpty()){
|
if(gl_voucher.isEmpty()){
|
||||||
result.append("没有需要同步的数据");
|
result.append("没有需要同步的数据");
|
||||||
|
@ -66,7 +76,7 @@ public class OAInvoiceImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public StringBuffer synData(Map<String, String> paramsMap,DynamicObject invoiceBill,StringBuffer result) {
|
public StringBuffer synData(Map<String, String> paramsMap,DynamicObject invoiceBill,StringBuffer result,ChannelSftp sftp) {
|
||||||
DynamicObject shkd_refbill = invoiceBill.getDynamicObject("shkd_refbill");
|
DynamicObject shkd_refbill = invoiceBill.getDynamicObject("shkd_refbill");
|
||||||
DynamicObject ap_finapbill = BusinessDataServiceHelper.loadSingle(shkd_refbill.getPkValue(), "ap_finapbill");
|
DynamicObject ap_finapbill = BusinessDataServiceHelper.loadSingle(shkd_refbill.getPkValue(), "ap_finapbill");
|
||||||
DynamicObject org = ap_finapbill.getDynamicObject("org");
|
DynamicObject org = ap_finapbill.getDynamicObject("org");
|
||||||
|
@ -86,64 +96,31 @@ public class OAInvoiceImpl {
|
||||||
String ssoDownloadUrl =domainContextUrl+"/ierp/accessTokenLogin.do?access_token=";
|
String ssoDownloadUrl =domainContextUrl+"/ierp/accessTokenLogin.do?access_token=";
|
||||||
String access_token="";
|
String access_token="";
|
||||||
String appToken="";
|
String appToken="";
|
||||||
Map<String, String> header = new HashMap<>();
|
|
||||||
JSONObject body = new JSONObject();
|
|
||||||
body.put("appId",paramsMap.get(AppflgConstant.appId));
|
|
||||||
body.put("appSecret",paramsMap.get(AppflgConstant.appSecret));
|
|
||||||
body.put("tenantid",paramsMap.get(AppflgConstant.tenantid));
|
|
||||||
body.put("accountId",paramsMap.get(AppflgConstant.accountId));
|
|
||||||
body.put("language","zh_CN");
|
|
||||||
try {
|
|
||||||
String postjson = HttpClientUtils.postjson(getAppTokenUrl, header, body.toJSONString());
|
|
||||||
AppTokenResult getAppTokenResult = JSONObject.parseObject(postjson, AppTokenResult.class);
|
|
||||||
if(getAppTokenResult.getStatus()){
|
|
||||||
Data data = getAppTokenResult.getData();
|
|
||||||
appToken = data.getApp_token();
|
|
||||||
|
|
||||||
body = new JSONObject();
|
|
||||||
body.put("user",paramsMap.get(AppflgConstant.user));
|
|
||||||
body.put("apptoken",appToken);
|
|
||||||
body.put("tenantid",paramsMap.get(AppflgConstant.tenantid));
|
|
||||||
body.put("accountId",paramsMap.get(AppflgConstant.accountId));
|
|
||||||
body.put("usertype","Mobile");
|
|
||||||
String res = HttpClientUtils.postjson(getAccessTokenUrl, header, body.toJSONString());
|
|
||||||
AccessTokenResult accessTokenResult = JSONObject.parseObject(res, AccessTokenResult.class);
|
|
||||||
if(accessTokenResult.getStatus()){
|
|
||||||
shkd.fi.fi.common.cosmic.accesstoken.Data accessTokenResultData = accessTokenResult.getData();
|
|
||||||
access_token = accessTokenResultData.getAccess_token();
|
|
||||||
// ssoDownloadUrl= ssoDownloadUrl+access_token+"&redirect=";
|
|
||||||
// ssoDownloadUrl=URLEncoder.encode(ssoDownloadUrl);
|
|
||||||
log.info("ssoDownloadUrl:"+ssoDownloadUrl);
|
|
||||||
}else{
|
|
||||||
result.append("获取AccessToken失败,不进行同步");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
result.append("获取AppToken失败,不进行同步");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
String url = "";
|
String url = "";
|
||||||
String filename="";
|
String filename="";
|
||||||
for (Map map : list) {
|
for (Map map : list) {
|
||||||
url = map.get("url").toString();
|
url = map.get("url").toString();
|
||||||
filename = map.get("name").toString();
|
filename = invoiceBill.getString("billno")+"_"+map.get("name").toString();
|
||||||
log.info("附件下载url:"+url);
|
log.info("附件下载url:"+url);
|
||||||
}
|
}
|
||||||
if(StringUtils.isNotEmpty(url)){
|
if(StringUtils.isNotEmpty(url)){
|
||||||
if(url.contains("/ierp")){
|
String path = url.substring(url.indexOf("path=")+"path=".length());
|
||||||
url = url.substring(url.indexOf("/ierp"));
|
try {
|
||||||
url = domainContextUrl+":80"+url;
|
path = URLDecoder.decode(path,"UTF-8");
|
||||||
|
path = FilePathUtil.dealPath(path,"attach");
|
||||||
|
InputStream inputStream = FileServiceFactory.getAttachmentFileService().getInputStream(path);
|
||||||
|
FileInputStream fileInputStream = (FileInputStream) inputStream;
|
||||||
|
boolean flag = SFTPConnectUtil.uploadFile("/usr/local/nginx/html/", fileInputStream, filename, sftp);
|
||||||
|
|
||||||
|
if(flag){
|
||||||
|
String filenameEn = URLEncoder.encode(filename);
|
||||||
|
url = domainContextUrl+"/"+filenameEn;
|
||||||
log.info("域名下载:"+url);
|
log.info("域名下载:"+url);
|
||||||
}
|
}
|
||||||
url= access_token+"&redirect="+url;//OA说参数后后面的值需要转义
|
|
||||||
log.info("url参数转义钱:"+url);
|
|
||||||
url = URLEncoder.encode(url);
|
|
||||||
log.info("url参数转义后:"+url);
|
log.info("url参数转义后:"+url);
|
||||||
url= ssoDownloadUrl+url;
|
} catch (UnsupportedEncodingException e) {
|
||||||
log.info(url);
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuffer xml = new StringBuffer();
|
StringBuffer xml = new StringBuffer();
|
||||||
|
|
|
@ -0,0 +1,322 @@
|
||||||
|
package shkd.fi.fi.util;
|
||||||
|
|
||||||
|
import com.jcraft.jsch.*;
|
||||||
|
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||||
|
import kd.bos.logging.Log;
|
||||||
|
import kd.bos.logging.LogFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* ClassName: SFTPConnectUtil
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Description: 连接SFTP
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class SFTPConnectUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志
|
||||||
|
*/
|
||||||
|
private final static Log logger = LogFactory.getLog(SFTPConnectUtil.class);
|
||||||
|
/**
|
||||||
|
* 连接SFTP服务器
|
||||||
|
*
|
||||||
|
* @param host
|
||||||
|
* 主机
|
||||||
|
* @param port
|
||||||
|
* 端口
|
||||||
|
* @param username
|
||||||
|
* 用户名
|
||||||
|
* @param password
|
||||||
|
* 密码
|
||||||
|
* @return ChannelSftp
|
||||||
|
*/
|
||||||
|
public static ChannelSftp connectSFTP(String host, int port,
|
||||||
|
String username, String password) {
|
||||||
|
// 声明ChannelSftp对象
|
||||||
|
ChannelSftp sftp = null;
|
||||||
|
try {
|
||||||
|
// 创建JSch对象
|
||||||
|
JSch jsch = new JSch();
|
||||||
|
// 根据用户名,主机IP,端口获取一个Session对象
|
||||||
|
Session sshSession = jsch.getSession(username, host, port);
|
||||||
|
if (!isEmpty(password)) {
|
||||||
|
sshSession.setPassword(password);
|
||||||
|
}
|
||||||
|
// 新建属性类
|
||||||
|
Properties sshConfig = new Properties();
|
||||||
|
/*
|
||||||
|
* 连接新主机时,不进行公钥确认;
|
||||||
|
* 在首次连接服务器时,会弹出公钥确认的提示。这会导致某些自动化任务,由于初次连接服务器而导致自动化任务中断。
|
||||||
|
*/
|
||||||
|
sshConfig.put("StrictHostKeyChecking", "no");
|
||||||
|
// 配置连接设置
|
||||||
|
sshSession.setConfig(sshConfig);
|
||||||
|
// 建立连接
|
||||||
|
sshSession.connect();
|
||||||
|
// 打开SFTP通道
|
||||||
|
Channel channel = sshSession.openChannel("sftp");
|
||||||
|
// 建立SFTP通道的连接
|
||||||
|
channel.connect();
|
||||||
|
// 强转ChannelSftp对象
|
||||||
|
sftp = (ChannelSftp) channel;
|
||||||
|
} catch (JSchException e) {
|
||||||
|
logger.error("connectSFTP:" + e.toString());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return sftp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件
|
||||||
|
*
|
||||||
|
* @param directory
|
||||||
|
* 上传的目录
|
||||||
|
* @param uploadFile
|
||||||
|
* 要上传的文件
|
||||||
|
* @param sftp
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static boolean uploadFile(String directory, File uploadFile,
|
||||||
|
ChannelSftp sftp) {
|
||||||
|
try {
|
||||||
|
// 进入指定目录
|
||||||
|
sftp.cd(directory);
|
||||||
|
sftp.put(new FileInputStream(uploadFile), uploadFile.getName());
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
logger.error("uploadFile -- FileNotFoundException:" + e.toString());
|
||||||
|
return false;
|
||||||
|
} catch (SftpException e) {
|
||||||
|
logger.error("uploadFile -- SftpException:" + e.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传附件
|
||||||
|
* @param directory
|
||||||
|
* @param fileInputStream
|
||||||
|
* @param fileName
|
||||||
|
* @param sftp
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean uploadFile(String directory, FileInputStream fileInputStream,String fileName,
|
||||||
|
ChannelSftp sftp) {
|
||||||
|
try {
|
||||||
|
// 进入指定目录
|
||||||
|
sftp.cd(directory);
|
||||||
|
sftp.put(fileInputStream, fileName);
|
||||||
|
} catch (SftpException e) {
|
||||||
|
logger.error("uploadFile -- SftpException:" + e.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 下载文件
|
||||||
|
*
|
||||||
|
* @param directory
|
||||||
|
* 下载目录
|
||||||
|
* @param downloadFile
|
||||||
|
* 下载的文件
|
||||||
|
* @param saveFile
|
||||||
|
* 存在本地的路径
|
||||||
|
* @param sftp
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static boolean downloadFile(String directory, String downloadFile,
|
||||||
|
File saveFile, ChannelSftp sftp) {
|
||||||
|
try {
|
||||||
|
// 进入指定目录
|
||||||
|
sftp.cd(directory);
|
||||||
|
// 下载文件到指定路径
|
||||||
|
sftp.get(downloadFile, new FileOutputStream(saveFile));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
logger.error("downloadFile -- FileNotFoundException:"
|
||||||
|
+ e.toString());
|
||||||
|
return false;
|
||||||
|
} catch (SftpException e) {
|
||||||
|
logger.error("downloadFile -- SftpException:" + e.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文件
|
||||||
|
*
|
||||||
|
* @param directory
|
||||||
|
* 要删除文件所在目录
|
||||||
|
* @param deleteFile
|
||||||
|
* 要删除的文件
|
||||||
|
* @param sftp
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static boolean deleteFile(String directory, String deleteFile,
|
||||||
|
ChannelSftp sftp) {
|
||||||
|
try {
|
||||||
|
// 进入指定目录
|
||||||
|
sftp.cd(directory);
|
||||||
|
// 删除文件
|
||||||
|
sftp.rm(deleteFile);
|
||||||
|
} catch (SftpException e) {
|
||||||
|
logger.error("deleteFile -- SftpException:" + e.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建文件夹
|
||||||
|
*
|
||||||
|
* @param directory
|
||||||
|
* 目标目录
|
||||||
|
* @param creatDir
|
||||||
|
* 要创建的文件
|
||||||
|
* @param sftp
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static boolean createDir(String directory, String creatDir,
|
||||||
|
ChannelSftp sftp) {
|
||||||
|
try {
|
||||||
|
// 进入指定目录
|
||||||
|
sftp.cd(directory);
|
||||||
|
// 创建文件夹
|
||||||
|
sftp.mkdir(creatDir);
|
||||||
|
} catch (SftpException e) {
|
||||||
|
logger.error("deleteFile -- SftpException:" + e.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文件夹
|
||||||
|
*
|
||||||
|
* @param directory
|
||||||
|
* 目标目录
|
||||||
|
* @param deleteDir
|
||||||
|
* 要删除的文件
|
||||||
|
* @param sftp
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static boolean deleteDir(String directory, String deleteDir,
|
||||||
|
ChannelSftp sftp) {
|
||||||
|
try {
|
||||||
|
// 进入指定目录
|
||||||
|
sftp.cd(directory);
|
||||||
|
// 删除文件夹
|
||||||
|
sftp.rmdir(deleteDir);
|
||||||
|
} catch (SftpException e) {
|
||||||
|
logger.error("deleteFile -- SftpException:" + e.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列出目录下的文件
|
||||||
|
*
|
||||||
|
* @param directory
|
||||||
|
* 要列出的目录
|
||||||
|
* @param sftp
|
||||||
|
* @return Vector<LsEntry>
|
||||||
|
* @throws SftpException
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static Vector<LsEntry> getListFiles(String directory,
|
||||||
|
ChannelSftp sftp) {
|
||||||
|
Vector<LsEntry> lsVec = null;
|
||||||
|
try {
|
||||||
|
// 列出指定目录下的所有文件和子目录
|
||||||
|
lsVec = sftp.ls(directory);
|
||||||
|
} catch (SftpException e) {
|
||||||
|
logger.error("getListFiles -- SftpException:" + e.toString());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return lsVec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除上传与下载临时目录的文件
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* 要删除的文件
|
||||||
|
*/
|
||||||
|
public static void deleteFileByTemp(File file) {
|
||||||
|
// 目录是否存在
|
||||||
|
if (file.exists()) {
|
||||||
|
// 获取当前路径的文件列表
|
||||||
|
File[] listFiles = file.listFiles();
|
||||||
|
// 遍历文件列表
|
||||||
|
for (File files : listFiles) {
|
||||||
|
// 如果是文件夹,递归调用此方法
|
||||||
|
if (files.isDirectory()) {
|
||||||
|
deleteFileByTemp(files);
|
||||||
|
// 如果是文件,直接删除
|
||||||
|
} else if (files.isFile()) {
|
||||||
|
logger.info(files.getName() + "---" + files.delete());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 删除根目录文件
|
||||||
|
logger.info(file.getName() + "---" + file.delete());
|
||||||
|
} else {
|
||||||
|
logger.info("当前路径不存在!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断字符串是否为空
|
||||||
|
*
|
||||||
|
* @param str
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static boolean isEmpty(String str) {
|
||||||
|
if (null == str || "".equals(str)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断对象是否为空
|
||||||
|
*
|
||||||
|
* @param str
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public static boolean isEmpty(Object obj) {
|
||||||
|
if (null == obj || "".equals(obj)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置上传文件名
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
public static String setFileName() {
|
||||||
|
return new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置上传文件夹名
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
public static String setDirName() {
|
||||||
|
return new SimpleDateFormat("yyyyMMdd").format(new Date());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue