parent
3cfe1aa378
commit
42dc475fb4
@ -1,61 +0,0 @@ |
|||||||
package com.web.controller; |
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.Entry; |
|
||||||
import com.alibaba.csp.sentinel.SphU; |
|
||||||
import com.alibaba.csp.sentinel.annotation.SentinelResource; |
|
||||||
import com.alibaba.csp.sentinel.slots.block.BlockException; |
|
||||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant; |
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; |
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; |
|
||||||
import io.swagger.annotations.Api; |
|
||||||
import org.slf4j.Logger; |
|
||||||
import org.slf4j.LoggerFactory; |
|
||||||
import org.springframework.stereotype.Controller; |
|
||||||
import org.springframework.web.bind.annotation.RequestMapping; |
|
||||||
|
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
@Controller |
|
||||||
@RequestMapping(value = "/login") |
|
||||||
@Api(value = "登录") |
|
||||||
public class LoginController { |
|
||||||
|
|
||||||
private static Logger log = LoggerFactory.getLogger(LoginController.class); |
|
||||||
|
|
||||||
@SentinelResource("HelloWorld") |
|
||||||
public void helloWorld() { |
|
||||||
// 资源中的逻辑
|
|
||||||
System.out.println("hello world"); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
public static void main(String[] args) { |
|
||||||
// 配置规则.
|
|
||||||
initFlowRules(); |
|
||||||
|
|
||||||
int i = 0; |
|
||||||
while (i <= 100) { |
|
||||||
i++; |
|
||||||
// 1.5.0 版本开始可以直接利用 try-with-resources 特性
|
|
||||||
try (Entry entry = SphU.entry("HelloWorld")) { |
|
||||||
// 被保护的逻辑
|
|
||||||
System.out.println("hello world"); |
|
||||||
} catch (BlockException ex) { |
|
||||||
// 处理被流控的逻辑
|
|
||||||
System.out.println("blocked!"); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static void initFlowRules(){ |
|
||||||
List<FlowRule> rules = new ArrayList<>(); |
|
||||||
FlowRule rule = new FlowRule(); |
|
||||||
rule.setResource("HelloWorld"); |
|
||||||
rule.setGrade(RuleConstant.FLOW_GRADE_QPS); |
|
||||||
// Set limit QPS to 20.
|
|
||||||
rule.setCount(20); |
|
||||||
rules.add(rule); |
|
||||||
FlowRuleManager.loadRules(rules); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,124 @@ |
|||||||
|
package com.web.controller; |
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject; |
||||||
|
import com.hai.common.exception.ErrorCode; |
||||||
|
import com.hai.common.exception.ErrorHelp; |
||||||
|
import com.hai.common.exception.SysCode; |
||||||
|
import com.hai.common.pay.util.IOUtil; |
||||||
|
import com.hai.common.pay.util.XmlUtil; |
||||||
|
import com.hai.entity.*; |
||||||
|
import com.hai.order.service.OrderPaySuccessService; |
||||||
|
import com.hai.order.service.OrderService; |
||||||
|
import com.hai.order.type.OrderPayType; |
||||||
|
import com.hai.service.pay.NotifyService; |
||||||
|
import io.swagger.annotations.Api; |
||||||
|
import io.swagger.annotations.ApiOperation; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
import org.springframework.stereotype.Controller; |
||||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||||
|
import org.springframework.web.bind.annotation.RequestMethod; |
||||||
|
|
||||||
|
import javax.annotation.Resource; |
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
import java.io.BufferedOutputStream; |
||||||
|
import java.math.BigDecimal; |
||||||
|
import java.net.URLDecoder; |
||||||
|
import java.util.SortedMap; |
||||||
|
|
||||||
|
/** |
||||||
|
* @className: OrderPayNotifyController |
||||||
|
* @author: HuRui |
||||||
|
* @date: 2022/9/8 |
||||||
|
**/ |
||||||
|
@Controller |
||||||
|
@RequestMapping("/payNotify") |
||||||
|
@Api(value = "订单支付") |
||||||
|
public class OrderPayNotifyController { |
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(OrderPayNotifyController.class); |
||||||
|
|
||||||
|
@Resource |
||||||
|
private NotifyService notifyService; |
||||||
|
|
||||||
|
@Resource |
||||||
|
private OrderService orderService; |
||||||
|
|
||||||
|
@Resource |
||||||
|
private OrderPaySuccessService paySuccessService; |
||||||
|
|
||||||
|
@RequestMapping(value = "/wechatPay", method = RequestMethod.POST) |
||||||
|
@ApiOperation(value = "微信支付 -> 异步回调") |
||||||
|
public void wechatPay(HttpServletRequest request, HttpServletResponse response) { |
||||||
|
try { |
||||||
|
log.info("===============微信支付回调start=================="); |
||||||
|
String resXml = ""; // 反馈给微信服务器
|
||||||
|
String notifyXml = null; // 微信支付系统发送的数据(<![CDATA[product_001]]>格式)
|
||||||
|
notifyXml = IOUtil.inputStreamToString(request.getInputStream(), "UTF-8"); |
||||||
|
|
||||||
|
log.info("回调参数: " + notifyXml); |
||||||
|
SortedMap<String, String> map = XmlUtil.parseXmlToTreeMap(notifyXml, "UTF-8"); |
||||||
|
log.info("开始处理订单: " + map.get("out_trade_no")); |
||||||
|
|
||||||
|
// 处理业务
|
||||||
|
log.info("开始处理业务"); |
||||||
|
paySuccessService.orderPaySuccessHandle(map.get("out_trade_no"), |
||||||
|
OrderPayType.PAY_TYPE2, map.get("out_trade_no"), |
||||||
|
new BigDecimal(map.get("total_fee")).divide(new BigDecimal("100")), |
||||||
|
null); |
||||||
|
log.info("处理业务完成"); |
||||||
|
|
||||||
|
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream()); |
||||||
|
out.write(resXml.getBytes()); |
||||||
|
out.flush(); |
||||||
|
out.close(); |
||||||
|
} catch (Exception e) { |
||||||
|
log.error("订单处理异常", e); |
||||||
|
} finally { |
||||||
|
log.info("===============微信支付回调end=================="); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@RequestMapping(value = "/unionPay", method = RequestMethod.POST) |
||||||
|
@ApiOperation(value = "银联支付 -> 异步回调") |
||||||
|
public void unionPay(@RequestBody String params, HttpServletRequest request, HttpServletResponse response) { |
||||||
|
try { |
||||||
|
log.info("===============银联支付回调start=================="); |
||||||
|
log.info("回调参数: " + params); |
||||||
|
if (StringUtils.isNotBlank(params)) { |
||||||
|
// 参数解码
|
||||||
|
String paramsStr = URLDecoder.decode(params,"utf-8"); |
||||||
|
JSONObject body = JSONObject.parseObject(paramsStr.substring(0, paramsStr.length() - 1)); |
||||||
|
log.info("开始处理订单: " + body.getString("tradetrace")); |
||||||
|
|
||||||
|
// 查询订单信息
|
||||||
|
HighOrder order = orderService.getOrderDetailByNo(body.getString("tradetrace")); |
||||||
|
if (order == null) { |
||||||
|
throw ErrorHelp.genException(SysCode.System, ErrorCode.NOT_FOUND_ORDER, ""); |
||||||
|
} |
||||||
|
log.info("开始处理业务"); |
||||||
|
// 异步处理业务
|
||||||
|
paySuccessService.orderPaySuccessHandle(order.getOrderNo(), |
||||||
|
OrderPayType.PAY_TYPE5, |
||||||
|
body.getString("wtorderid"), |
||||||
|
order.getPayPrice(), |
||||||
|
null); |
||||||
|
log.info("处理业务完成"); |
||||||
|
} |
||||||
|
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream()); |
||||||
|
JSONObject result = new JSONObject(); |
||||||
|
result.put("resultcode", "00"); |
||||||
|
out.write(result.toJSONString().getBytes()); |
||||||
|
out.flush(); |
||||||
|
out.close(); |
||||||
|
} catch (Exception e) { |
||||||
|
log.error("订单处理异常", e); |
||||||
|
} finally { |
||||||
|
log.info("===============银联支付回调end=================="); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,234 @@ |
|||||||
|
package com.web.rocketmq.consumer; |
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject; |
||||||
|
import com.hai.common.exception.ErrorCode; |
||||||
|
import com.hai.common.exception.ErrorHelp; |
||||||
|
import com.hai.common.exception.SysCode; |
||||||
|
import com.hai.common.pay.util.XmlUtil; |
||||||
|
import com.hai.common.pay.util.sdk.WXPayConstants; |
||||||
|
import com.hai.common.utils.WxUtils; |
||||||
|
import com.hai.entity.*; |
||||||
|
import com.hai.enum_type.GasChannel; |
||||||
|
import com.hai.enum_type.GasChannelPayPlatformType; |
||||||
|
import com.hai.enum_type.MerchantStoreSourceType; |
||||||
|
import com.hai.model.HighMerchantStoreModel; |
||||||
|
import com.hai.model.ResultProfitSharing; |
||||||
|
import com.hai.order.service.OrderService; |
||||||
|
import com.hai.order.type.OrderChildGoodsType; |
||||||
|
import com.hai.order.type.OrderPayType; |
||||||
|
import com.hai.order.type.OrderProductType; |
||||||
|
import com.hai.order.type.OrderStatus; |
||||||
|
import com.hai.service.HighGasChannelConfigService; |
||||||
|
import com.hai.service.HighMerchantStoreService; |
||||||
|
import com.hai.service.HighProfitSharingRecordService; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.apache.http.HttpEntity; |
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse; |
||||||
|
import org.apache.http.client.methods.HttpPost; |
||||||
|
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; |
||||||
|
import org.apache.http.entity.StringEntity; |
||||||
|
import org.apache.http.impl.client.CloseableHttpClient; |
||||||
|
import org.apache.http.impl.client.HttpClients; |
||||||
|
import org.apache.http.ssl.SSLContexts; |
||||||
|
import org.apache.http.util.EntityUtils; |
||||||
|
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; |
||||||
|
import org.apache.rocketmq.spring.core.RocketMQListener; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
import javax.annotation.Resource; |
||||||
|
import javax.net.ssl.SSLContext; |
||||||
|
import java.io.FileInputStream; |
||||||
|
import java.math.BigDecimal; |
||||||
|
import java.security.KeyStore; |
||||||
|
import java.util.*; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
@Component |
||||||
|
@Slf4j |
||||||
|
@RocketMQMessageListener(consumerGroup = "order-profit-sharing-group", topic = "order-topic",selectorExpression = "profit-sharing") |
||||||
|
public class OrderProfitSharingConsumer implements RocketMQListener<HighOrder> { |
||||||
|
|
||||||
|
@Resource |
||||||
|
private OrderService orderService; |
||||||
|
|
||||||
|
@Resource |
||||||
|
private HighProfitSharingRecordService profitSharingRecordService; |
||||||
|
|
||||||
|
@Resource |
||||||
|
private HighMerchantStoreService merchantStoreService; |
||||||
|
|
||||||
|
@Resource |
||||||
|
private HighGasChannelConfigService gasChannelConfigService; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onMessage(HighOrder order) { |
||||||
|
String resultXml = null; |
||||||
|
if (order.getPayType().equals(OrderPayType.PAY_TYPE2.getNumber())) { |
||||||
|
List<HighChildOrder> orderList = order.getHighChildOrderList().stream() |
||||||
|
.filter(o -> o.getGoodsType().equals(OrderChildGoodsType.TYPE3.getNumber())).collect(Collectors.toList()); |
||||||
|
|
||||||
|
if (order.getProductType().equals(OrderProductType.PRODUCT_TYPE6.getNumber())) { |
||||||
|
HighMerchantStore store = merchantStoreService.getDetailById(orderList.get(0).getGoodsId()); |
||||||
|
HighGasChannelConfig gasChannelConfig; |
||||||
|
if (store.getSourceType().equals(MerchantStoreSourceType.type1.getNumber())) { |
||||||
|
gasChannelConfig = gasChannelConfigService.getConfig(GasChannel.type1, GasChannelPayPlatformType.type1); |
||||||
|
|
||||||
|
} else if (store.getSourceType().equals(MerchantStoreSourceType.type2.getNumber())) { |
||||||
|
gasChannelConfig = gasChannelConfigService.getConfig(GasChannel.type2, GasChannelPayPlatformType.type1); |
||||||
|
|
||||||
|
} else if (store.getSourceType().equals(MerchantStoreSourceType.type3.getNumber())) { |
||||||
|
gasChannelConfig = gasChannelConfigService.getConfig(GasChannel.type3, GasChannelPayPlatformType.type1); |
||||||
|
|
||||||
|
} else if (store.getSourceType().equals(MerchantStoreSourceType.type4.getNumber())) { |
||||||
|
gasChannelConfig = gasChannelConfigService.getConfig(GasChannel.type4, GasChannelPayPlatformType.type1); |
||||||
|
|
||||||
|
} else { |
||||||
|
throw ErrorHelp.genException(SysCode.System, ErrorCode.COMMON_ERROR, "未配置分账参数"); |
||||||
|
} |
||||||
|
|
||||||
|
BigDecimal rake = new BigDecimal("0.01"); |
||||||
|
// 计算微信收取的手续费 支付金额 * 0.002 注:如果与两个相邻数字的距离相等,则为上舍入的舍入模式。
|
||||||
|
BigDecimal wxHandlingFee = order.getPayRealPrice().multiply(new BigDecimal("0.002")).setScale(2, BigDecimal.ROUND_HALF_DOWN); |
||||||
|
BigDecimal price = order.getPayRealPrice().subtract(wxHandlingFee); |
||||||
|
// 计算分账金额 手续费后的价格 * 0.01 注:如果与两个相邻数字的距离相等,则为上舍入的舍入模式。
|
||||||
|
BigDecimal profitSharingAmount = price.multiply(rake).setScale(2, BigDecimal.ROUND_DOWN); |
||||||
|
|
||||||
|
resultXml = this.wechatSharingRecord( |
||||||
|
order.getExt1(), |
||||||
|
order.getAccountMerchantNum(), |
||||||
|
gasChannelConfig.getProfitSharingReceiversNo(), |
||||||
|
gasChannelConfig.getProfitSharingReceiversName(), |
||||||
|
order.getPaySerialNo(), |
||||||
|
order.getOrderNo(), |
||||||
|
profitSharingAmount); |
||||||
|
ResultProfitSharing resultProfitSharing = XmlUtil.getObjectFromXML(resultXml, ResultProfitSharing.class); |
||||||
|
HighProfitSharingRecord sharingRecord = new HighProfitSharingRecord(); |
||||||
|
sharingRecord.setOutOrderNo(order.getOrderNo()); |
||||||
|
sharingRecord.setTransactionId(order.getPaySerialNo()); |
||||||
|
sharingRecord.setOrderId(order.getId().toString()); |
||||||
|
sharingRecord.setStatus(resultProfitSharing.getResult_code()); |
||||||
|
sharingRecord.setPrice(profitSharingAmount); |
||||||
|
sharingRecord.setCreateTime(new Date()); |
||||||
|
sharingRecord.setContent(resultXml); |
||||||
|
profitSharingRecordService.insert(sharingRecord); |
||||||
|
|
||||||
|
} else { |
||||||
|
BigDecimal rake = new BigDecimal("0.01"); |
||||||
|
// 计算微信收取的手续费 支付金额 * 0.002 注:如果与两个相邻数字的距离相等,则为上舍入的舍入模式。
|
||||||
|
BigDecimal wxHandlingFee = order.getPayRealPrice().multiply(new BigDecimal("0.002")).setScale(2, BigDecimal.ROUND_HALF_DOWN); |
||||||
|
BigDecimal price = order.getPayRealPrice().subtract(wxHandlingFee); |
||||||
|
// 计算分账金额 手续费后的价格 * 0.01 注:如果与两个相邻数字的距离相等,则为上舍入的舍入模式。
|
||||||
|
BigDecimal profitSharingAmount = price.multiply(rake).setScale(2, BigDecimal.ROUND_DOWN); |
||||||
|
|
||||||
|
resultXml = this.wechatSharingRecord( |
||||||
|
order.getExt1(), |
||||||
|
order.getAccountMerchantNum(), |
||||||
|
"1603942866", |
||||||
|
"惠昕石化", |
||||||
|
order.getPaySerialNo(), |
||||||
|
order.getOrderNo(), |
||||||
|
profitSharingAmount); |
||||||
|
ResultProfitSharing resultProfitSharing = XmlUtil.getObjectFromXML(resultXml, ResultProfitSharing.class); |
||||||
|
HighProfitSharingRecord sharingRecord = new HighProfitSharingRecord(); |
||||||
|
sharingRecord.setOutOrderNo(order.getOrderNo()); |
||||||
|
sharingRecord.setTransactionId(order.getPaySerialNo()); |
||||||
|
sharingRecord.setOrderId(order.getId().toString()); |
||||||
|
sharingRecord.setStatus(resultProfitSharing.getResult_code()); |
||||||
|
sharingRecord.setPrice(profitSharingAmount); |
||||||
|
sharingRecord.setCreateTime(new Date()); |
||||||
|
sharingRecord.setContent(resultXml); |
||||||
|
profitSharingRecordService.insert(sharingRecord); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 【微信】分账 |
||||||
|
* @param appid |
||||||
|
* @param subMchId |
||||||
|
* @param receiversAccount 接收者商户号 |
||||||
|
* @param receiversAccountName 接收者商户名称 |
||||||
|
* @param transaction_id 第三方交易订单号 |
||||||
|
* @param out_order_no 交易订单号 |
||||||
|
* @param amount 分账金额 |
||||||
|
*/ |
||||||
|
public String wechatSharingRecord(String appid, String subMchId, String receiversAccount, String receiversAccountName,String transaction_id,String out_order_no, BigDecimal amount) { |
||||||
|
try { |
||||||
|
Map<String,String> param = new LinkedHashMap<>(); |
||||||
|
param.put("appid", appid); |
||||||
|
param.put("mch_id", "1289663601"); |
||||||
|
param.put("sub_mch_id" , subMchId); |
||||||
|
param.put("transaction_id" , transaction_id); |
||||||
|
param.put("out_order_no" , out_order_no); |
||||||
|
param.put("nonce_str" , WxUtils.makeNonStr()); |
||||||
|
|
||||||
|
List<Map<String,Object>> receiversList = new ArrayList<>(); |
||||||
|
Map<String,Object> receiversMap = new LinkedHashMap<>(); |
||||||
|
receiversMap.put("type", "MERCHANT_ID"); |
||||||
|
receiversMap.put("account", receiversAccount); |
||||||
|
receiversMap.put("amount", amount.multiply(new BigDecimal("100")).intValue()); |
||||||
|
receiversMap.put("description", "分给商户:" + receiversAccountName); |
||||||
|
receiversList.add(receiversMap); |
||||||
|
param.put("receivers" , JSONObject.toJSONString(receiversList)); |
||||||
|
String signStr = WxUtils.generateSignature(param, "Skufk5oi85wDFGl888i6wsRSTkdd5df5" , WXPayConstants.SignType.HMACSHA256); |
||||||
|
param.put("sign" , signStr); |
||||||
|
|
||||||
|
// 请求分账返回的结果
|
||||||
|
return this.wechatRequest(param.get("mch_id"), WxUtils.mapToXml(param)); |
||||||
|
|
||||||
|
|
||||||
|
} catch (Exception e) { |
||||||
|
log.error("CmsContentController --> getCorporateAdvertising() error!", e); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 【微信】分账请求 |
||||||
|
* @param mchId |
||||||
|
* @param data |
||||||
|
* @return |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public String wechatRequest(String mchId, String data) throws Exception { |
||||||
|
/** |
||||||
|
* 注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的 |
||||||
|
*/ |
||||||
|
KeyStore keyStore = KeyStore.getInstance("PKCS12"); |
||||||
|
//P12文件目录 证书路径,这里需要你自己修改,linux下还是windows下的根路径
|
||||||
|
FileInputStream instream = new FileInputStream("/home/project/wx_cert/1289663601_apiclient_cert.p12"); |
||||||
|
try { |
||||||
|
keyStore.load(instream, "1289663601".toCharArray());//这里写密码..默认是你的MCHID
|
||||||
|
} finally { |
||||||
|
instream.close(); |
||||||
|
} |
||||||
|
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, "1289663601".toCharArray()).build();//这里也是写密码的
|
||||||
|
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, SSLConnectionSocketFactory.getDefaultHostnameVerifier()); |
||||||
|
//小程序退款需要调用双向证书的认证
|
||||||
|
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); |
||||||
|
try { |
||||||
|
HttpPost httpost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/profitsharing"); // 设置响应头信息
|
||||||
|
httpost.addHeader("Connection", "keep-alive"); |
||||||
|
httpost.addHeader("Accept", "*/*"); |
||||||
|
httpost.addHeader("Content-Type", "text/xml"); |
||||||
|
httpost.addHeader("Host", "api.mch.weixin.qq.com"); |
||||||
|
httpost.addHeader("X-Requested-With", "XMLHttpRequest"); |
||||||
|
httpost.addHeader("Cache-Control", "max-age=0"); |
||||||
|
httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) "); |
||||||
|
httpost.setEntity(new StringEntity(data, "UTF-8")); |
||||||
|
CloseableHttpResponse response = httpClient.execute(httpost); |
||||||
|
try { |
||||||
|
HttpEntity entity = response.getEntity(); |
||||||
|
String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8"); |
||||||
|
EntityUtils.consume(entity); |
||||||
|
return jsonStr; |
||||||
|
} finally { |
||||||
|
response.close(); |
||||||
|
} |
||||||
|
} catch (Exception e){ |
||||||
|
throw new RuntimeException(e); |
||||||
|
} finally { |
||||||
|
httpClient.close(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue