商户端增商户订单ID生成二维码功能

master
yuan 7 years ago
parent f592cb4a7a
commit f3f6af80f6

@ -466,3 +466,28 @@ insert into `royalpay_production`.`sys_configs` ( `config_key`, `config_value`)
insert into `royalpay_production`.`sys_configs` ( `config_key`, `config_value`) values ( 'risk_total_amout', '5000');
CREATE TABLE `pmt_directed_bill_code` (
`bill_code_id` varchar(50) NOT NULL DEFAULT '',
`client_id` int(15) not NULL,
`client_order_id` varchar(50) DEFAULT NULL,
`status` int(10) DEFAULT '1' COMMENT '账单状态 1可用2关闭 3:完成',
`customer_id` varchar(200) DEFAULT NULL COMMENT 'open_id',
`create_time` datetime not NULL DEFAULT now() comment '账单创建时间',
`currency` varchar(10) not NULL,
`remark` varchar(200) DEFAULT NULL,
`cancle_time` datetime DEFAULT NULL,
`channel` varchar(10) DEFAULT NULL,
`nickname` varchar(80) DEFAULT NULL,
`headimg` varchar(200) DEFAULT NULL,
`code_url` varchar(200) DEFAULT '',
`order_id` varchar(50) DEFAULT '',
`order_amount` decimal(10,4) not NULL comment '账单金额',
`pay_amount` decimal(10,4) DEFAULT NULL comment '订单支付金额',
`pay_time` datetime DEFAULT NULL comment '支付时间',
`order_status` int(10) DEFAULT NULL COMMENT '订单状态(0-New,1-OrderFailed,2-WaitingForPayment,3-OrderCanceled,4-PaymentFailed,5-PaymentSuccess,6-PartialRefund,7-FullRefund)',
PRIMARY KEY (`bill_code_id`),
KEY `client_id` (`client_id`),
KEY `order_id` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

@ -0,0 +1,77 @@
package au.com.royalpay.payment.manage.billqrcode.bean;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
public class NewBillQrCodeBean {
private BigDecimal order_amount;
private String currency = "AUD";
private String remark;
private String cancle_time;
private String client_order_id;
public JSONObject toJson(){
JSONObject record = new JSONObject();
if(order_amount != null){
record.put("order_amount",order_amount);
}
if(StringUtils.isNotEmpty(remark)){
record.put("remark",remark);
}
if(StringUtils.isNotEmpty(client_order_id)){
record.put("client_order_id",client_order_id);
}
if (StringUtils.isNotEmpty(cancle_time)) {
record.put("cancle_time",cancle_time);
}
record.put("currency",currency);
return record;
}
public BigDecimal getOrder_amount() {
return order_amount;
}
public void setOrder_amount(BigDecimal order_amount) {
this.order_amount = order_amount;
}
public String getCurrency() {
return currency;
}
public void setCurrency(String currency) {
this.currency = currency;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getCancle_time() {
return cancle_time;
}
public void setCancle_time(String cancle_time) {
this.cancle_time = cancle_time;
}
public String getClient_order_id() {
return client_order_id;
}
public void setClient_order_id(String client_order_id) {
this.client_order_id = client_order_id;
}
}

@ -0,0 +1,46 @@
package au.com.royalpay.payment.manage.billqrcode.bean;
import com.alibaba.fastjson.JSONObject;
import com.sun.tools.hat.internal.model.JavaObject;
import org.apache.commons.lang3.StringUtils;
/**
* Created by yuan on 2018/5/9.
*/
public class QueryBillBean {
private int limit = 10;
private int page = 1;
private String status;
public JSONObject toJson(){
JSONObject jason = new JSONObject();
if(StringUtils.isNotEmpty(status)){
jason.put("status",status);
}
return jason;
}
public int getLimit() {
return limit;
}
public void setLimit(int limit) {
this.limit = limit;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}

@ -0,0 +1,20 @@
package au.com.royalpay.payment.manage.billqrcode.core;
import au.com.royalpay.payment.manage.billqrcode.bean.NewBillQrCodeBean;
import au.com.royalpay.payment.manage.billqrcode.bean.QueryBillBean;
import com.alibaba.fastjson.JSONObject;
import java.util.List;
/**
* Created by yuan on 2018/5/3.
*/
public interface PartnerBillService {
String saveBill(JSONObject partner, NewBillQrCodeBean newBillQrCodeBean);
List<JSONObject> listBills(JSONObject partner, QueryBillBean queryBillBean);
void dailyCheckDirectedBillCode();
void disableBills(JSONObject partner,String bill_code_id);
}

@ -0,0 +1,128 @@
package au.com.royalpay.payment.manage.billqrcode.core.impl;
import au.com.royalpay.payment.manage.billqrcode.bean.NewBillQrCodeBean;
import au.com.royalpay.payment.manage.billqrcode.bean.QueryBillBean;
import au.com.royalpay.payment.manage.billqrcode.core.PartnerBillService;
import au.com.royalpay.payment.manage.mappers.billqrcode.DirectedBillCodeMapper;
import au.com.royalpay.payment.manage.mappers.system.ClientMapper;
import au.com.royalpay.payment.manage.merchants.core.ClientManager;
import au.com.royalpay.payment.tools.env.PlatformEnvironment;
import au.com.royalpay.payment.tools.exceptions.BadRequestException;
import au.com.royalpay.payment.tools.exceptions.NotFoundException;
import au.com.royalpay.payment.tools.utils.QRCodeUtils;
import com.alibaba.fastjson.JSONObject;
import com.github.miemiedev.mybatis.paginator.domain.Order;
import com.github.miemiedev.mybatis.paginator.domain.PageBounds;
import javafx.scene.chart.PieChart;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
/**
* Created by yuan on 2018/5/3.
*/
@Service
public class PartnerBillServiceImpl implements PartnerBillService {
private org.slf4j.Logger logger = LoggerFactory.getLogger(getClass());
@Resource
private ClientMapper clientMapper;
@Resource
private ClientManager clientManager;
@Resource
private DirectedBillCodeMapper directedBillCodeMapper;
@Override
@Transactional
public String saveBill(JSONObject partner, NewBillQrCodeBean newBillQrCodeBean) {
JSONObject client = clientMapper.findClientByMoniker(partner.getString("client_moniker"));
if (client == null) {
throw new NotFoundException("client info not found");
}
if(StringUtils.isNotEmpty(newBillQrCodeBean.getClient_order_id())){
JSONObject bill = directedBillCodeMapper.findOne(newBillQrCodeBean.getClient_order_id());
if(bill != null){
if(bill.getIntValue("status") == 2){
throw new BadRequestException("该订单已关闭,请确认");
}
if(bill.getIntValue("status") == 3){
throw new BadRequestException("该订单已完成,请确认");
}
if(bill.getIntValue("status") == 1){
if(bill.getBigDecimal("order_amount") == newBillQrCodeBean.getOrder_amount()){
return QRCodeUtils.qrcodeImageCode(bill.getString("code_url"), 250, false);
}
throw new BadRequestException("已有相同订单号正在被支付,详情请查看订单列表");
}
}
}
JSONObject record = newBillQrCodeBean.toJson();
if(record.getString("client_order_id") == null){
record.put("client_order_id","Bill_" + DateFormatUtils.format(new Date(), "yyyyMMddHHmmss") + "_" + RandomStringUtils.random(5, true, true).toUpperCase());
}
record.put("client_id",partner.getIntValue("client_id"));
record.put("create_time", new Date());
record.put("status",1);
directedBillCodeMapper.save(record);
String code_url = getQRCodeImg(record);
record.put("code_url",code_url);
directedBillCodeMapper.update(record);
return QRCodeUtils.qrcodeImageCode(code_url, 250, false);
}
private String getQRCodeImg(JSONObject bill){
return PlatformEnvironment.getEnv().concatUrl("api/v1.0/business/bills/"+bill.getString("bill_code_id"));
}
@Override
public List<JSONObject> listBills(JSONObject partner, QueryBillBean queryBillBean) {
int client_id = partner.getInteger("client_id");
JSONObject client = clientManager.getClientInfo(client_id);
if (client == null) {
throw new NotFoundException("client info not found");
}
JSONObject params = queryBillBean.toJson();
params.put("client_id",client_id);
List<JSONObject> bills = directedBillCodeMapper.findByClientId(params,new PageBounds(queryBillBean.getPage(),queryBillBean.getLimit(), Order.formString("create_time.desc")));
bills.stream().filter(t->t.getString("code_url")!= null).forEach(t->t.put("code_url",QRCodeUtils.qrcodeImageCode(t.getString("code_url"), 250, false)));
return bills;
}
@Override
public void disableBills(JSONObject partner, String bill_code_id) {
int client_id = partner.getInteger("client_id");
JSONObject client = clientManager.getClientInfo(client_id);
if (client == null) {
throw new NotFoundException("client info not found");
}
JSONObject bill = directedBillCodeMapper.findOneByBillCodeId(bill_code_id);
if(bill.getIntValue("status")==1){
if(bill.getString("order_id") != null){
bill.put("status",2);
directedBillCodeMapper.update(bill);
}
}
}
@Override
public void dailyCheckDirectedBillCode() {
List<JSONObject> listAllBill = directedBillCodeMapper.listAllBills();
for (JSONObject bill: listAllBill) {
if(DateUtils.addDays(bill.getDate("cancle_time"),1).before(new Date())){
bill.put("status",2);
directedBillCodeMapper.update(bill);
}
}
}
}

@ -0,0 +1,37 @@
package au.com.royalpay.payment.manage.billqrcode.web;
import au.com.royalpay.payment.manage.billqrcode.bean.NewBillQrCodeBean;
import au.com.royalpay.payment.manage.billqrcode.bean.QueryBillBean;
import au.com.royalpay.payment.manage.billqrcode.core.PartnerBillService;
import au.com.royalpay.payment.manage.permission.manager.PartnerMapping;
import au.com.royalpay.payment.tools.CommonConsts;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* Created by yuan on 2018/5/3.
*/
@RestController
@RequestMapping("/partner/qrcode")
public class PartnerBillController {
@Resource
private PartnerBillService partnerBillService;
@PartnerMapping(value = "/bills", method = RequestMethod.POST)
public String addBill(@ModelAttribute(CommonConsts.PARTNER_STATUS) JSONObject partner, @RequestBody NewBillQrCodeBean newBillQrCodeBean) {
return partnerBillService.saveBill(partner, newBillQrCodeBean);
}
@PartnerMapping(value = "", method = RequestMethod.GET)
public List<JSONObject> listBills(@ModelAttribute(CommonConsts.PARTNER_STATUS) JSONObject partner, QueryBillBean queryBillBean) {
return partnerBillService.listBills(partner,queryBillBean);
}
@PartnerMapping(value = "/{bill_code_id}", method = RequestMethod.DELETE)
public void disableBills(@ModelAttribute(CommonConsts.PARTNER_STATUS) JSONObject partner,@PathVariable String bill_code_id) {
partnerBillService.disableBills(partner,bill_code_id);
}
}

@ -0,0 +1,33 @@
package au.com.royalpay.payment.manage.mappers.billqrcode;
import cn.yixblog.support.mybatis.autosql.annotations.AdvanceSelect;
import cn.yixblog.support.mybatis.autosql.annotations.AutoMapper;
import cn.yixblog.support.mybatis.autosql.annotations.AutoSql;
import cn.yixblog.support.mybatis.autosql.annotations.SqlType;
import com.alibaba.fastjson.JSONObject;
import com.github.miemiedev.mybatis.paginator.domain.PageBounds;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@AutoMapper(tablename = "pmt_directed_bill_code", pkName = "bill_code_id")
public interface DirectedBillCodeMapper {
@AutoSql(type = SqlType.INSERT)
int save(JSONObject record);
@AutoSql(type = SqlType.SELECT)
JSONObject findOne(@Param("client_order_id") String client_order_id);
@AutoSql(type = SqlType.SELECT)
JSONObject findOneByBillCodeId(@Param("bill_code_id") String bill_code_id);
@AutoSql(type = SqlType.UPDATE)
int update(JSONObject record);
@AutoSql(type = SqlType.SELECT)
List<JSONObject> findByClientId(JSONObject params, PageBounds pageBounds);
@AutoSql(type = SqlType.SELECT)
@AdvanceSelect(addonWhereClause = "status=1")
List<JSONObject> listAllBills();
}

@ -7,6 +7,7 @@ import au.com.royalpay.payment.manage.mappers.client.ClientSubMerchantIdMapper;
import au.com.royalpay.payment.manage.mappers.payment.CommonSubMerchantIdMapper;
import au.com.royalpay.payment.manage.mappers.system.ClientMapper;
import au.com.royalpay.payment.manage.merchantid.core.MerchantIdManageService;
import au.com.royalpay.payment.tools.env.PlatformEnvironment;
import au.com.royalpay.payment.tools.env.SysConfigManager;
import au.com.royalpay.payment.tools.exceptions.BadRequestException;
import au.com.royalpay.payment.tools.exceptions.ServerErrorException;
@ -144,19 +145,14 @@ public class MerchantIdManageServiceImpl implements MerchantIdManageService {
@Override
public JSONObject getClientQrCodeImg(JSONObject manager,String sub_merchant_id) {
List<JSONObject> clientMonikers = showClientMoniker(manager,sub_merchant_id);
if(clientMonikers.isEmpty()){
throw new BadRequestException("当前子商户号下暂无商户");
}
String partner_code = clientMonikers.get(0).getString("client_moniker");
String partner_code = "PINE";
String orderId = "Merchant" + DateFormatUtils.format(new Date(), "yyyyMMddHHmmss") + "_" + RandomStringUtils.random(5, true, true).toUpperCase();
JSONObject param = new JSONObject();
param.put("price", 10);
param.put("price", 1);
param.put("description", "order");
param.put("operator", "web");
param.put("currency","CNY");
String createUrl= "https://mpay.royalpay.com.au/api/v1.0/gateway/partners/" + partner_code + "/orders/" + orderId + "?" + queryParams(partner_code);
param.put("sub_merchant_id",sub_merchant_id);
String createUrl= PlatformEnvironment.getEnv().concatUrl("api/v1.0/gateway/partners/" + partner_code + "/orders/" + orderId +"/manager/test"+"?" + queryParams(partner_code));
HttpRequestResult result = null;
try {
result = new HttpRequestGenerator(createUrl, RequestMethod.PUT).setJSONEntity(param).execute();

@ -0,0 +1,23 @@
package au.com.royalpay.payment.manage.task;
import au.com.royalpay.payment.manage.billqrcode.core.PartnerBillService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* Created by yuan on 2018/5/4.
*/
@Component
@ConditionalOnProperty(value = "app.run-tasks", havingValue = "true")
public class DirectedBillCodeStatusDailyCheck {
@Resource
private PartnerBillService partnerBillService;
@Scheduled(cron = "0 0 6 * * ?")
public void statusDailyCheck(){
partnerBillService.dailyCheckDirectedBillCode();
}
}

@ -888,6 +888,11 @@ margin-bottom: 10%;"/>
<!--<i class="fa fa-users" aria-hidden="true"></i> -->Cashiers Management
</a>
</li>
<li ng-if="currentUser.client.client_moniker=='PINE'">
<a ui-sref="bill_qrcode">
Bill QR Code
</a>
</li>
</ul>
</li>
<li ng-if="([1,2]|withRole)">

@ -75,7 +75,8 @@ var modules = [
{path: 'static/application/clientAuthentication', module: 'clientAuthentication', roles: [1]},
{path: 'static/cashback/partner-cashback', module: 'cashbackApp', roles: [1,2,3]},
{path: 'static/integralmall/coupon_cancellation', module: 'couponCancellation', roles: [1,2]},
{path: 'static/invoice/invoice_assistant', module: 'partnerInvoice', roles: [1]}
{path: 'static/invoice/invoice_assistant', module: 'partnerInvoice', roles: [1]},
{path: 'static/payment/billqrcode/bill-qrcode-manage', module: 'billQrCodeManagement', roles: [1,2,3]}
];
require(['angular', 'jquery'], function (angular, $) {

@ -0,0 +1,85 @@
define(['angular', 'static/commons/commons', 'uiBootstrap', 'uiRouter', 'ngBootSwitch', 'ngFileUpload'], function (angular) {
'use strict';
var app = angular.module('billQrCodeManagement', ['ui.bootstrap', 'ui.router', 'frapontillo.bootstrap-switch', 'ngFileUpload']);
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider.state('bill_qrcode', {
url: '/bill_qrcode',
templateUrl: '/static/payment/billqrcode/templates/bill_qrcode.html',
controller: 'billQrCodeCtrl'
})
}]);
app.controller('billQrCodeCtrl', ['$scope', '$http', 'commonDialog','$filter','orderService', function ($scope, $http, commonDialog,$filter,orderService) {
$scope.pagination = {};
$scope.params = {};
$scope.new_bill = {};
$scope.today = new Date();
$scope.loadBills = function (page) {
var params = angular.copy($scope.params);
params.page = page || $scope.pagination.page || 1;
$http.get('/partner/qrcode', {params: params}).then(function (resp) {
$scope.bills = resp.data;
$scope.pagination = resp.data.pagination;
});
};
$scope.loadBills(1);
$scope.generateBill = function () {
var params = angular.copy($scope.new_bill);
if(!params.client_order_id){
alert("client order id为空是否后台自动生成");
}
if(!params.order_amount){
alert("order amount不可为空");
return;
}
if(params.cancle_time){
params.cancle_time = $filter('date')(params.cancle_time, 'yyyy-MM-dd HH:mm:ss')
}else {
alert("Expire Date 不可为空!");
}
$http.post('/partner/qrcode/bills',params).then(function (resp) {
commonDialog.alert({title: 'Success', content: 'Success', type: 'success'});
$scope.code_url = resp.data;
$scope.loadBills(1);
},function (resp) {
commonDialog.alert({title: 'Error', content: resp.data.message, type: 'error'});
});
};
$scope.disableBill = function (bill) {
commonDialog.confirm(
{
title: 'Delete Bill',
content: 'Are you sure to delete this bill?'
}).then(function () {
$http.delete('/partner/qrcode/'+bill.bill_code_id).then(function(){
$scope.loadBills(1);
});
})
};
$scope.clearBill = function () {
$scope.new_bill = {};
$scope.code_url = "";
};
$scope.showTradeDetail = function (order) {
orderService.clientOrderDetail(order)
};
}]);
app.filter('billStatus', function () {
return function (status) {
switch (status + '') {
case '1':
return 'wait for payment';
case '2':
return 'disabled';
case '3':
return 'payment success'
}
}
});
return app;
});

@ -0,0 +1,145 @@
<div ui-view>
<section class="content-header">
<h1>Bill QR Code</h1>
<ol class="breadcrumb">
<li>
<i class="fa fa-sitemap"></i> Bill QR Code
</li>
<li class="active">Bill QR Code Management</li>
</ol>
</section>
<div class="content">
<div class="row">
<div class="col-sm-12">
<div class="box-solid">
<div class="box box-warning">
<div class="box-header">
<div class="form-horizontal col-sm-8">
<div class="form-group">
<label class="control-label col-xs-4 col-sm-3">* Order Amount</label>
<div class="col-xs-8 col-sm-6">
<input type="number" step="0.01" name="order_amount" class="form-control"
ng-model="new_bill.order_amount" required
onkeyup="this.value=this.value.replace(/^(\-)*(\d+)\.(\d\d).*$/,'$1$2.$3')">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-4 col-sm-3">* Expire</label>
<div class="col-xs-8 col-sm-6">
<input class="form-control" ng-model="new_bill.cancle_time"
uib-datepicker-popup size="10" is-open="ctrl.dateInput"
ng-click="ctrl.dateInput=true"
datepicker-options="{minDate:today}" name="cancle_time" required>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-4 col-sm-3">Client Order Id</label>
<div class="col-xs-8 col-sm-6">
<input class="form-control" ng-model="new_bill.client_order_id">
<p class="small text-info">Client Order Id 为空时,系统自动生成</p>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-4 col-sm-3">Remark</label>
<div class="col-xs-8 col-sm-6">
<textarea class="form-control" ng-model="new_bill.remark"></textarea>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-4 col-sm-3"></label>
<div class="col-sm-6">
<button class="btn btn-success" ng-click="generateBill()">generate
</button>&nbsp;&nbsp;&nbsp;
<button class="btn btn-primary" ng-click="clearBill()">clear</button>
</div>
</div>
</div>
<div class="col-sm-4" style="text-align: center" ng-if="code_url">
<a class="thumbnail" download ng-href="{{code_url}}" uib-tooltip="Download">
<img ng-src="{{code_url}}">
</a>
<p>
<a ng-href="{{code_url}}" download><i class="fa fa-download"></i> Download Bill QR
Code Image</a>
</p>
</div>
</div>
</div>
<div class="box">
<div class="box-header">
<h3 class="box-title">Bill List &nbsp;&nbsp;&nbsp;&nbsp;
<a role="button" ng-class="{'bg-primary':params.status==null}"
ng-click="params.status=null;loadBills(1)">All</a> |
<a role="button" ng-class="{'bg-primary':params.status==3}"
ng-click="params.status=3;loadBills(1)">Success</a> |
<a role="button" ng-class="{'bg-primary':params.status==2}"
ng-click="params.status=2;loadBills(1)">Disabled</a>
</h3>
</div>
<div class="box-body table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Price</th>
<th>Client Order Id</th>
<th>Create time</th>
<th>Expire time</th>
<th>Remark</th>
<th>Order Status</th>
<th>Bill Status</th>
<th>Payer</th>
<th>Operation</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="bill in bills">
<td ng-bind="bill.order_amount"></td>
<th ng-bind="bill.client_order_id"></th>
<td ng-bind="bill.create_time"></td>
<td ng-bind="bill.cancle_time"></td>
<td ng-bind="bill.remark"></td>
<th ng-bind="bill.order_status|tradeStatus"></th>
<th ng-bind="bill.status|billStatus"></th>
<td ng-bind="bill.nickname"></td>
<td>
<a ng-if="bill.order_id" class="text-primary" role="button" title="Detail"
ng-click="showTradeDetail(bill)">
<i class="fa fa-search"></i>
</a>
<a ng-if="bill.code_url&&bill.status==1" ng-href="{{bill.code_url}}" download><i
class="fa fa-download"
uib-tooltip="Download Bill QR Code Image"></i></a>
<a ng-if="!bill.order_id&&bill.status==1"
class="text-bold text-danger" role="button" ng-click="disableBill(bill)">Disable</a>
</td>
</tr>
</tbody>
</table>
</div>
<div class="box-footer" ng-if="bills.length">
<uib-pagination class="pagination"
total-items="pagination.totalCount"
boundary-links="true"
ng-model="pagination.page"
items-per-page="pagination.limit"
max-size="10"
ng-change="loadBills(1)"
previous-text="&lsaquo;"
next-text="&rsaquo;"
first-text="&laquo;"
last-text="&raquo;"></uib-pagination>
<div class="row">
<div class="col-xs-12">Total Records:{{pagination.totalCount}};Total
Pages:{{pagination.totalPages}}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Loading…
Cancel
Save