|
|
|
@ -2,14 +2,27 @@ package com.ruoyi.newstocksystem.service.impl;
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.Date;
|
|
|
|
import java.util.Date;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
|
|
import java.util.HashSet;
|
|
|
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.newstocksystem.domain.TIndustryBasic;
|
|
|
|
|
|
|
|
import com.ruoyi.newstocksystem.domain.TStockBasic;
|
|
|
|
import com.ruoyi.newstocksystem.domain.TStockDailyTrade;
|
|
|
|
import com.ruoyi.newstocksystem.domain.TStockDailyTrade;
|
|
|
|
|
|
|
|
import com.ruoyi.newstocksystem.mapper.TStockBasicMapper;
|
|
|
|
import com.ruoyi.newstocksystem.mapper.TStockDailyTradeMapper;
|
|
|
|
import com.ruoyi.newstocksystem.mapper.TStockDailyTradeMapper;
|
|
|
|
|
|
|
|
import com.ruoyi.newstocksystem.service.IIndustryIndexService;
|
|
|
|
|
|
|
|
import com.ruoyi.newstocksystem.service.IStockBasicService;
|
|
|
|
import com.ruoyi.newstocksystem.service.IStockDailyTradeService;
|
|
|
|
import com.ruoyi.newstocksystem.service.IStockDailyTradeService;
|
|
|
|
|
|
|
|
import com.ruoyi.newstocksystem.service.ITIndustryBasicService;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 个股每日交易数据Service实现类
|
|
|
|
* 个股每日交易数据Service实现类
|
|
|
|
@ -20,9 +33,20 @@ import com.ruoyi.newstocksystem.service.IStockDailyTradeService;
|
|
|
|
@Service
|
|
|
|
@Service
|
|
|
|
public class StockDailyTradeServiceImpl implements IStockDailyTradeService
|
|
|
|
public class StockDailyTradeServiceImpl implements IStockDailyTradeService
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
private static final Logger logger = LoggerFactory.getLogger(StockDailyTradeServiceImpl.class);
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
@Autowired
|
|
|
|
private TStockDailyTradeMapper stockDailyTradeMapper;
|
|
|
|
private TStockDailyTradeMapper stockDailyTradeMapper;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
|
|
private IStockBasicService stockBasicService;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
|
|
private IIndustryIndexService industryIndexService;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
|
|
private ITIndustryBasicService industryBasicService;
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
|
public TStockDailyTrade selectStockDailyTradeByCodeAndDate(String stockCode, Date tradeDate)
|
|
|
|
public TStockDailyTrade selectStockDailyTradeByCodeAndDate(String stockCode, Date tradeDate)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -86,17 +110,255 @@ public class StockDailyTradeServiceImpl implements IStockDailyTradeService
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
long startTime = System.currentTimeMillis();
|
|
|
|
|
|
|
|
logger.info("开始处理股票基础数据,待处理记录数: {}", stockDailyTradeList.size());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 收集所有需要检查的行业代码
|
|
|
|
|
|
|
|
Set<String> industryCodes = new HashSet<>();
|
|
|
|
|
|
|
|
for (TStockDailyTrade stockDailyTrade : stockDailyTradeList)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (stockDailyTrade.getIndustryIndexCode() != null && !stockDailyTrade.getIndustryIndexCode().trim().isEmpty())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
industryCodes.add(stockDailyTrade.getIndustryIndexCode());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 检查并确保行业基础数据存在
|
|
|
|
|
|
|
|
logger.info("开始检查行业基础数据,需要检查的行业代码数量: {}", industryCodes.size());
|
|
|
|
|
|
|
|
if (!industryCodes.isEmpty())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 检查每个行业代码是否存在,如果不存在则创建
|
|
|
|
|
|
|
|
for (String industryCode : industryCodes)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!industryIndexService.checkIndustryBasicExists(industryCode))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 如果行业基础数据不存在,创建一个基础记录
|
|
|
|
|
|
|
|
String industryName = industryCode; // 使用代码作为默认名称
|
|
|
|
|
|
|
|
if (stockDailyTradeList != null) {
|
|
|
|
|
|
|
|
// 尝试从数据中获取行业名称
|
|
|
|
|
|
|
|
for (TStockDailyTrade trade : stockDailyTradeList) {
|
|
|
|
|
|
|
|
if (trade.getIndustryIndexCode() != null && trade.getIndustryIndexCode().equals(industryCode)) {
|
|
|
|
|
|
|
|
if (trade.getIndustryIndexName() != null) {
|
|
|
|
|
|
|
|
industryName = trade.getIndustryIndexName();
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
industryIndexService.createIndustryBasic(industryCode, industryName);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 收集所有需要检查的股票代码
|
|
|
|
|
|
|
|
Set<String> stockCodes = new HashSet<>();
|
|
|
|
|
|
|
|
for (TStockDailyTrade stockDailyTrade : stockDailyTradeList)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
stockCodes.add(stockDailyTrade.getStockCode());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 一次性读取所有存在的股票基础数据
|
|
|
|
|
|
|
|
logger.info("开始批量读取股票基础数据,股票代码数量: {}", stockCodes.size());
|
|
|
|
|
|
|
|
long loadStart = System.currentTimeMillis();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<TStockBasic> existingBasics = stockBasicService.selectStockBasicList(new TStockBasic());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 将现有基础数据转换为Map以便快速查找
|
|
|
|
|
|
|
|
Map<String, TStockBasic> existingBasicsMap = new HashMap<>();
|
|
|
|
|
|
|
|
for (TStockBasic basic : existingBasics)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (stockCodes.contains(basic.getStockCode()))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
existingBasicsMap.put(basic.getStockCode(), basic);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
long loadEnd = System.currentTimeMillis();
|
|
|
|
|
|
|
|
logger.info("股票基础数据读取完成,耗时: {} ms", (loadEnd - loadStart));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 准备需要插入和更新的数据
|
|
|
|
|
|
|
|
List<TStockBasic> newBasicsToInsert = new ArrayList<>();
|
|
|
|
|
|
|
|
List<TStockBasic> basicsToUpdate = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.info("开始比较和分类股票基础数据");
|
|
|
|
|
|
|
|
long compareStart = System.currentTimeMillis();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (TStockDailyTrade stockDailyTrade : stockDailyTradeList)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
TStockBasic existingBasic = existingBasicsMap.get(stockDailyTrade.getStockCode());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (existingBasic == null)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 如果不存在,创建新的股票基础数据记录
|
|
|
|
|
|
|
|
// 只有当必要字段不为空时才插入
|
|
|
|
|
|
|
|
if (stockDailyTrade.getStockCode() != null && stockDailyTrade.getStockName() != null)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
TStockBasic newBasic = new TStockBasic();
|
|
|
|
|
|
|
|
newBasic.setStockCode(stockDailyTrade.getStockCode());
|
|
|
|
|
|
|
|
newBasic.setStockName(stockDailyTrade.getStockName());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置从TStockDailyTrade传入的其他基础信息
|
|
|
|
|
|
|
|
if (stockDailyTrade.getIndustryIndexCode() != null && !stockDailyTrade.getIndustryIndexCode().trim().isEmpty()) {
|
|
|
|
|
|
|
|
newBasic.setIndustryIndexCode(stockDailyTrade.getIndustryIndexCode());
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
newBasic.setIndustryIndexCode(""); // 设置默认值
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stockDailyTrade.getIndustryIndexName() != null) {
|
|
|
|
|
|
|
|
newBasic.setIndustryIndexName(stockDailyTrade.getIndustryIndexName());
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
newBasic.setIndustryIndexName("");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stockDailyTrade.getListingDate() != null) {
|
|
|
|
|
|
|
|
newBasic.setListingDate(stockDailyTrade.getListingDate());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stockDailyTrade.getListingDays() != null) {
|
|
|
|
|
|
|
|
newBasic.setListingDays(stockDailyTrade.getListingDays());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 确保ST相关字段有默认值
|
|
|
|
|
|
|
|
if (stockDailyTrade.getIsSt() != null) {
|
|
|
|
|
|
|
|
newBasic.setIsSt(stockDailyTrade.getIsSt());
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
newBasic.setIsSt(0); // 默认不是ST股票
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stockDailyTrade.getIsStarSt() != null) {
|
|
|
|
|
|
|
|
newBasic.setIsStarSt(stockDailyTrade.getIsStarSt());
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
newBasic.setIsStarSt(0); // 默认不是*ST股票
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
newBasicsToInsert.add(newBasic);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 如果存在,检查各字段是否发生变化,如有变化则更新
|
|
|
|
|
|
|
|
boolean needUpdate = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 只有当新值不为null时才更新
|
|
|
|
|
|
|
|
if (stockDailyTrade.getStockName() != null &&
|
|
|
|
|
|
|
|
(existingBasic.getStockName() == null || !existingBasic.getStockName().equals(stockDailyTrade.getStockName())))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
existingBasic.setStockName(stockDailyTrade.getStockName());
|
|
|
|
|
|
|
|
needUpdate = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stockDailyTrade.getIndustryIndexCode() != null &&
|
|
|
|
|
|
|
|
(existingBasic.getIndustryIndexCode() == null || !existingBasic.getIndustryIndexCode().equals(stockDailyTrade.getIndustryIndexCode())))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
existingBasic.setIndustryIndexCode(stockDailyTrade.getIndustryIndexCode());
|
|
|
|
|
|
|
|
needUpdate = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stockDailyTrade.getIndustryIndexName() != null &&
|
|
|
|
|
|
|
|
(existingBasic.getIndustryIndexName() == null || !existingBasic.getIndustryIndexName().equals(stockDailyTrade.getIndustryIndexName())))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
existingBasic.setIndustryIndexName(stockDailyTrade.getIndustryIndexName());
|
|
|
|
|
|
|
|
needUpdate = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stockDailyTrade.getListingDate() != null &&
|
|
|
|
|
|
|
|
(existingBasic.getListingDate() == null || !existingBasic.getListingDate().equals(stockDailyTrade.getListingDate())))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
existingBasic.setListingDate(stockDailyTrade.getListingDate());
|
|
|
|
|
|
|
|
needUpdate = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stockDailyTrade.getListingDays() != null &&
|
|
|
|
|
|
|
|
(existingBasic.getListingDays() == null || !existingBasic.getListingDays().equals(stockDailyTrade.getListingDays())))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
existingBasic.setListingDays(stockDailyTrade.getListingDays());
|
|
|
|
|
|
|
|
needUpdate = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stockDailyTrade.getIsSt() != null &&
|
|
|
|
|
|
|
|
(existingBasic.getIsSt() == null || !existingBasic.getIsSt().equals(stockDailyTrade.getIsSt())))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
existingBasic.setIsSt(stockDailyTrade.getIsSt());
|
|
|
|
|
|
|
|
needUpdate = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stockDailyTrade.getIsStarSt() != null &&
|
|
|
|
|
|
|
|
(existingBasic.getIsStarSt() == null || !existingBasic.getIsStarSt().equals(stockDailyTrade.getIsStarSt())))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
existingBasic.setIsStarSt(stockDailyTrade.getIsStarSt());
|
|
|
|
|
|
|
|
needUpdate = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (needUpdate)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 确保ST相关字段始终有值,避免数据库约束错误
|
|
|
|
|
|
|
|
if (existingBasic.getIsSt() == null) {
|
|
|
|
|
|
|
|
existingBasic.setIsSt(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (existingBasic.getIsStarSt() == null) {
|
|
|
|
|
|
|
|
existingBasic.setIsStarSt(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
basicsToUpdate.add(existingBasic);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
long compareEnd = System.currentTimeMillis();
|
|
|
|
|
|
|
|
logger.info("股票基础数据比较完成,耗时: {} ms", (compareEnd - compareStart));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 批量插入新的股票基础数据
|
|
|
|
|
|
|
|
if (!newBasicsToInsert.isEmpty())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
logger.info("准备批量插入 {} 条新的股票基础数据", newBasicsToInsert.size());
|
|
|
|
|
|
|
|
long insertStart = System.currentTimeMillis();
|
|
|
|
|
|
|
|
stockBasicService.importStockBasic(newBasicsToInsert); // 使用importStockBasic方法进行批量插入
|
|
|
|
|
|
|
|
long insertEnd = System.currentTimeMillis();
|
|
|
|
|
|
|
|
logger.info("股票基础数据插入完成,耗时: {} ms", (insertEnd - insertStart));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
logger.info("无需插入新的股票基础数据");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 批量更新股票基础数据
|
|
|
|
|
|
|
|
if (!basicsToUpdate.isEmpty())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
logger.info("准备批量更新 {} 条股票基础数据", basicsToUpdate.size());
|
|
|
|
|
|
|
|
long updateStart = System.currentTimeMillis();
|
|
|
|
|
|
|
|
for (TStockBasic basic : basicsToUpdate)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 确保ST相关字段始终有值,避免数据库约束错误
|
|
|
|
|
|
|
|
if (basic.getIsSt() == null) {
|
|
|
|
|
|
|
|
basic.setIsSt(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (basic.getIsStarSt() == null) {
|
|
|
|
|
|
|
|
basic.setIsStarSt(0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
stockBasicService.updateStockBasic(basic);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
long updateEnd = System.currentTimeMillis();
|
|
|
|
|
|
|
|
logger.info("股票基础数据更新完成,耗时: {} ms", (updateEnd - updateStart));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
logger.info("无需更新股票基础数据");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
long checkTime = System.currentTimeMillis();
|
|
|
|
|
|
|
|
logger.info("股票基础数据处理完成,耗时: {} ms", (checkTime - startTime));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 批量插入每日交易数据
|
|
|
|
int count = 0;
|
|
|
|
int count = 0;
|
|
|
|
int batchSize = 500;
|
|
|
|
int batchSize = 500;
|
|
|
|
|
|
|
|
|
|
|
|
// 分批导入数据
|
|
|
|
logger.info("开始批量插入每日交易数据,总记录数: {}", stockDailyTradeList.size());
|
|
|
|
|
|
|
|
long tradeDataStartTime = System.currentTimeMillis();
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < stockDailyTradeList.size(); i += batchSize)
|
|
|
|
for (int i = 0; i < stockDailyTradeList.size(); i += batchSize)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
int end = Math.min(i + batchSize, stockDailyTradeList.size());
|
|
|
|
int end = Math.min(i + batchSize, stockDailyTradeList.size());
|
|
|
|
List<TStockDailyTrade> batchList = stockDailyTradeList.subList(i, end);
|
|
|
|
List<TStockDailyTrade> batchList = stockDailyTradeList.subList(i, end);
|
|
|
|
count += stockDailyTradeMapper.batchUpsertStockDailyTrade(batchList);
|
|
|
|
int batchCount = stockDailyTradeMapper.batchUpsertStockDailyTrade(batchList);
|
|
|
|
|
|
|
|
count += batchCount;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.info("批次 [{}-{}] 交易数据插入完成,本次插入: {} 条", i, end, batchCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
long tradeDataEndTime = System.currentTimeMillis();
|
|
|
|
|
|
|
|
logger.info("每日交易数据批量插入完成,总插入: {} 条,耗时: {} ms", count, (tradeDataEndTime - tradeDataStartTime));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
long totalTime = System.currentTimeMillis() - startTime;
|
|
|
|
|
|
|
|
logger.info("导入任务完成,总耗时: {} ms", totalTime);
|
|
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -117,4 +379,4 @@ public class StockDailyTradeServiceImpl implements IStockDailyTradeService
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return stockDailyTradeMapper.selectStrongStockList(stockDailyTrade);
|
|
|
|
return stockDailyTradeMapper.selectStrongStockList(stockDailyTrade);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|