From 4d5a5032e19ad8319786f8a2631f086262e5215d Mon Sep 17 00:00:00 2001 From: liushaodong Date: Mon, 4 Nov 2019 11:13:40 +0800 Subject: [PATCH] init --- .gitignore | 30 + .../blockchain-common-base/pom.xml | 21 + .../common/base/constant/BaseConstant.java | 18 + .../common/base/constant/PushConstants.java | 11 + .../common/base/constant/TokenTypeEnums.java | 24 + .../blockchain/common/base/dto/BaseDTO.java | 6 + .../blockchain/common/base/dto/GasDTO.java | 17 + .../blockchain/common/base/dto/MarketDTO.java | 22 + .../blockchain/common/base/dto/PageDTO.java | 54 + .../blockchain/common/base/dto/ResultDTO.java | 49 + .../common/base/dto/SessionUserDTO.java | 15 + .../blockchain/common/base/dto/TokenDTO.java | 18 + .../common/base/dto/WalletChangeDTO.java | 19 + .../common/base/dto/WalletOrderDTO.java | 18 + .../common/base/entity/BaseModel.java | 10 + .../common/base/enums/BaseResultEnums.java | 57 + .../common/base/enums/PushEnums.java | 94 + .../common/base/exception/BaseException.java | 57 + .../common/base/exception/RPCException.java | 17 + .../common/base/util/DateTimeUtils.java | 757 ++ .../base/util/ExceptionPreconditionUtils.java | 79 + .../common/base/util/FileUploadHelper.java | 138 + .../common/base/util/HttpRequestUtil.java | 74 + .../common/base/util/HttpUtilManager.java | 160 + .../common/base/util/JsonUtils.java | 273 + .../blockchain/common/base/util/MD5Utils.java | 38 + .../common/base/util/RSACoderUtils.java | 265 + .../common/base/util/SSOHelper.java | 162 + .../blockchain-common-pom/pom.xml | 230 + .../blockchain-common-tx/pom.xml | 45 + .../src/main/resources/README.md | 8 + blockchain-common/pom.xml | 22 + .../blockchain-server-base/pom.xml | 15 + .../com/blockchain/server/base/BaseConf.java | 8 + .../server/base/annotation/BypassedFeign.java | 13 + .../blockchain/server/base/aop/FeignAop.java | 68 + .../blockchain/server/base/aop/MethodAop.java | 66 + .../server/base/conf/DateConvertConfig.java | 26 + .../server/base/conf/FeginConf.java | 32 + .../base/conf/GlobalExceptionHandle.java | 73 + .../base/conf/InnerInterceptorConf.java | 27 + .../server/base/conf/RedisConf.java | 91 + .../server/base/conf/RestTemplateConf.java | 24 + .../server/base/conf/SpringCloudConf.java | 19 + .../server/base/conf/SwaggerConf.java | 40 + .../server/base/conf/TkMybatisConf.java | 17 + .../server/base/conf/XssFilterConfig.java | 45 + .../base/controller/BaseController.java | 28 + .../server/base/filter/XssFilter.java | 92 + .../filter/XssHttpServletRequestWrapper.java | 40 + .../base/interceptor/InnerInterceptor.java | 34 + .../base/interceptor/LoginInterceptor.java | 20 + .../interceptor/StringToDateConverter.java | 46 + .../server/base/redis/IpInnerCache.java | 28 + .../src/main/resources/README.md | 35 + .../i18n/MessgesBundle_default.properties | 5 + .../blockchain-server-btc/pom.xml | 35 + .../blockchain/server/btc/BtcApplication.java | 49 + .../btc/common/config/InterceptorConf.java | 32 + .../common/constants/BtcAddressConstans.java | 14 + .../constants/BtcApplicationConstans.java | 13 + .../constants/BtcBlockNumberConstans.java | 20 + .../common/constants/BtcConfigConstants.java | 15 + .../btc/common/constants/BtcConstans.java | 12 + .../common/constants/BtcTransferConstans.java | 38 + .../btc/common/constants/UsdtConstans.java | 27 + .../server/btc/common/enums/BtcEnums.java | 73 + .../btc/common/exception/BtcException.java | 40 + .../common/util/BtcAddressSetRedisUtils.java | 53 + .../btc/common/util/BtcBlockRedisUtils.java | 43 + .../server/btc/common/util/BtcRASUtils.java | 60 + .../btc/common/util/CheckEthFeginResult.java | 22 + .../btc/controller/BtcTokenController.java | 35 + .../btc/controller/BtcWalletController.java | 54 + .../BtcWalletTransferController.java | 72 + .../ConfigWalletParamController.java | 29 + .../btc/controller/api/BtcTokenApi.java | 17 + .../btc/controller/api/BtcWalletApi.java | 29 + .../controller/api/BtcWalletTransferApi.java | 33 + .../controller/api/ConfigWalletParamApi.java | 12 + .../server/btc/dto/BtcApplicationDTO.java | 21 + .../server/btc/dto/BtcBlockNumberDTO.java | 23 + .../server/btc/dto/BtcTokenDTO.java | 25 + .../btc/dto/BtcTransferAuditingDTO.java | 25 + .../server/btc/dto/BtcWalletDTO.java | 29 + .../server/btc/dto/BtcWalletKeyDTO.java | 21 + .../server/btc/dto/BtcWalletOutDTO.java | 26 + .../server/btc/dto/BtcWalletTransferDTO.java | 35 + .../server/btc/dto/ConfigWalletParamDTO.java | 24 + .../server/btc/entity/BtcApplication.java | 29 + .../server/btc/entity/BtcBlockNumber.java | 33 + .../server/btc/entity/BtcToken.java | 37 + .../btc/entity/BtcTransferAuditing.java | 37 + .../server/btc/entity/BtcWallet.java | 43 + .../server/btc/entity/BtcWalletKey.java | 29 + .../server/btc/entity/BtcWalletOut.java | 39 + .../server/btc/entity/BtcWalletTransfer.java | 57 + .../server/btc/entity/ConfigWalletParam.java | 34 + .../server/btc/feign/CoinFegin.java | 31 + .../server/btc/feign/EthServerFegin.java | 20 + .../server/btc/feign/UserServerFegin.java | 46 + .../server/btc/inner/BtcWalletInner.java | 29 + .../btc/inner/BtcWalletTransferInner.java | 41 + .../btc/inner/api/BtcWalletInnerApi.java | 12 + .../inner/api/BtcWalletTransferInnerApi.java | 18 + .../btc/mapper/BtcApplicationMapper.java | 26 + .../btc/mapper/BtcBlockNumberMapper.java | 35 + .../server/btc/mapper/BtcTokenMapper.java | 34 + .../btc/mapper/BtcTransferAuditingMapper.java | 14 + .../server/btc/mapper/BtcWalletKeyMapper.java | 14 + .../server/btc/mapper/BtcWalletMapper.java | 70 + .../server/btc/mapper/BtcWalletOutMapper.java | 14 + .../btc/mapper/BtcWalletTransferMapper.java | 37 + .../btc/mapper/ConfigWalletParamMapper.java | 15 + .../server/btc/rpc/BtcOmniRpcClient.java | 44 + .../blockchain/server/btc/rpc/BtcUtils.java | 553 ++ .../blockchain/server/btc/rpc/UsdtUtils.java | 113 + .../RechargeBtcAndUsdtBlockTimer.java | 280 + .../btc/scheduleTask/WallertTimerTask.java | 86 + .../btc/service/BtcApplicationService.java | 23 + .../btc/service/BtcBlockNumberService.java | 40 + .../server/btc/service/BtcTokenService.java | 48 + .../service/BtcTransferAuditingService.java | 16 + .../btc/service/BtcWalletKeyService.java | 7 + .../btc/service/BtcWalletOutService.java | 14 + .../server/btc/service/BtcWalletService.java | 105 + .../btc/service/BtcWalletTransferService.java | 103 + .../btc/service/ConfigWalletParamService.java | 18 + .../impl/BtcApplicationServiceImpl.java | 34 + .../impl/BtcBlockNumberServiceImpl.java | 68 + .../btc/service/impl/BtcTokenServiceImpl.java | 69 + .../impl/BtcTransferAuditingServiceImpl.java | 24 + .../service/impl/BtcWalletKeyServiceImpl.java | 33 + .../service/impl/BtcWalletOutServiceImpl.java | 28 + .../service/impl/BtcWalletServiceImpl.java | 194 + .../impl/BtcWalletTransferServiceImpl.java | 335 + .../impl/ConfigWalletParamServiceImpl.java | 35 + .../src/main/resources/application.yml | 3 + .../src/main/resources/bootstrap.yml | 24 + .../main/resources/logback/logback-dev.xml | 54 + .../main/resources/logback/logback-pro.xml | 54 + .../resources/mapper/BtcApplicationMapper.xml | 17 + .../resources/mapper/BtcBlockNumberMapper.xml | 27 + .../main/resources/mapper/BtcTokenMapper.xml | 26 + .../mapper/BtcTransferAuditingMapper.xml | 15 + .../resources/mapper/BtcWalletKeyMapper.xml | 11 + .../main/resources/mapper/BtcWalletMapper.xml | 63 + .../resources/mapper/BtcWalletOutMapper.xml | 16 + .../mapper/BtcWalletTransferMapper.xml | 40 + .../mapper/ConfigWalletParamMapper.xml | 7 + .../blockchain-server-cct/pom.xml | 41 + .../blockchain/server/cct/CctApplication.java | 12 + .../server/cct/common/enums/CctDataEnums.java | 73 + .../server/cct/common/enums/CctEnums.java | 44 + .../cct/common/exception/CctException.java | 38 + .../common/interceptor/InterceptorConf.java | 58 + .../interceptor/TradingInterceptor.java | 16 + .../common/publish/listener/ListenerBean.java | 58 + .../publish/receiver/MessageReceiver.java | 282 + .../cct/common/redisson/RedissonConfig.java | 41 + .../cct/common/redisson/RedissonTool.java | 48 + .../cct/common/redistool/RedisTool.java | 128 + .../util/CCTExceptionPreconditionUtils.java | 46 + .../cct/common/util/CheckConfigUtil.java | 100 + .../cct/common/websocket/WebSocketConfig.java | 19 + .../server/cct/controller/CoinController.java | 44 + .../cct/controller/ConfigController.java | 31 + .../controller/PublishOrderController.java | 213 + .../controller/TradingDetailController.java | 98 + .../controller/TradingRecordController.java | 37 + .../server/cct/controller/api/CoinApi.java | 17 + .../server/cct/controller/api/ConfigApi.java | 10 + .../cct/controller/api/PublishOrderApi.java | 80 + .../cct/controller/api/TradingDetailApi.java | 45 + .../cct/controller/api/TradingRecordApi.java | 26 + .../server/cct/dto/coin/TradingOnDTO.java | 14 + .../server/cct/dto/config/ConfigDTO.java | 9 + .../cct/dto/matchconfig/MatchConfigDTO.java | 18 + .../server/cct/dto/order/PublishOrderDTO.java | 25 + .../cct/dto/order/PublishOrderParamDTO.java | 29 + .../dto/rpc/currency/CurrencyMarketDTO.java | 32 + .../cct/dto/trading/DetailByOrderIdDTO.java | 23 + .../cct/dto/trading/ListUserDetailDTO.java | 31 + .../cct/dto/trading/ListUserHistoryDTO.java | 29 + .../blockchain/server/cct/entity/Bill.java | 41 + .../blockchain/server/cct/entity/Coin.java | 49 + .../blockchain/server/cct/entity/CoinLog.java | 45 + .../server/cct/entity/Commission.java | 44 + .../blockchain/server/cct/entity/Config.java | 39 + .../server/cct/entity/ConfigLog.java | 47 + .../server/cct/entity/MatchConfig.java | 40 + .../cct/entity/MatchConfigHandleLog.java | 51 + .../server/cct/entity/PublishOrder.java | 56 + .../server/cct/entity/TradingDetail.java | 50 + .../server/cct/entity/TradingRecord.java | 46 + .../cct/entity/rpc/user/UserRelation.java | 50 + .../blockchain/server/cct/feign/BTCFeign.java | 30 + .../server/cct/feign/CurrencyFeign.java | 50 + .../blockchain/server/cct/feign/EOSFeign.java | 30 + .../blockchain/server/cct/feign/ETHFeign.java | 44 + .../server/cct/feign/PushFeign.java | 26 + .../server/cct/feign/UserFeign.java | 35 + .../blockchain/server/cct/inner/CctInner.java | 100 + .../server/cct/mapper/CoinMapper.java | 48 + .../server/cct/mapper/CommissionMapper.java | 52 + .../server/cct/mapper/ConfigMapper.java | 31 + .../mapper/MatchConfigHandleLogMapper.java | 15 + .../server/cct/mapper/MatchConfigMapper.java | 24 + .../server/cct/mapper/PublishOrderMapper.java | 127 + .../cct/mapper/TradingDetailMapper.java | 41 + .../cct/mapper/TradingRecordMapper.java | 33 + .../server/cct/redis/PublishOrderCache.java | 144 + .../server/cct/service/CoinService.java | 39 + .../server/cct/service/CommissionService.java | 19 + .../server/cct/service/ConfigService.java | 42 + .../cct/service/MatchConfigService.java | 17 + .../server/cct/service/MatchService.java | 38 + .../cct/service/PublishOrderService.java | 157 + .../cct/service/TradingDetailService.java | 37 + .../cct/service/TradingRecordService.java | 31 + .../server/cct/service/WalletService.java | 37 + .../cct/service/impl/CoinServiceImpl.java | 63 + .../service/impl/CommissionServiceImpl.java | 209 + .../cct/service/impl/ConfigServiceImpl.java | 79 + .../service/impl/MatchConfigServiceImpl.java | 62 + .../cct/service/impl/MatchServiceImpl.java | 698 ++ .../service/impl/PublishOrderServiceImpl.java | 709 ++ .../impl/TradingDetailServiceImpl.java | 35 + .../impl/TradingRecordServiceImpl.java | 33 + .../cct/service/impl/WalletServiceImpl.java | 101 + .../src/main/resources/application.yml | 3 + .../src/main/resources/bootstrap.yml | 22 + .../main/resources/logback/logback-dev.xml | 54 + .../main/resources/logback/logback-pro.xml | 54 + .../src/main/resources/mapper/CoinMapper.xml | 53 + .../resources/mapper/CommissionMapper.xml | 49 + .../main/resources/mapper/ConfigMapper.xml | 34 + .../mapper/MatchConfigHandleLogMapper.xml | 22 + .../resources/mapper/MatchConfigMapper.xml | 30 + .../resources/mapper/PublishOrderMapper.xml | 170 + .../resources/mapper/TradingDetailMapper.xml | 112 + .../resources/mapper/TradingRecordMapper.xml | 40 + .../blockchain-server-currency/pom.xml | 39 + .../server/currency/CurrencyApplication.java | 19 + .../Scheduling/CurrencyMarketScheduling.java | 92 + .../Scheduling/HuobiMarketScheduling.java | 89 + .../currency/common/conf/InterceptorConf.java | 13 + .../common/constant/BaseCoinEnums.java | 26 + .../common/constant/BaseCurrencyEnums.java | 25 + .../common/constant/CommonConstants.java | 26 + .../common/constant/DatatablesEnums.java | 30 + .../common/constant/MarketKEnums.java | 32 + .../common/constant/MarketKTypeEnums.java | 37 + .../currency/common/constant/RatesEnums.java | 26 + .../common/constant/TradingTypeEnums.java | 24 + .../enums/CurrencyMarketResultEnums.java | 34 + .../exception/CurrencyMarketExeption.java | 39 + .../controller/CurrencyController.java | 55 + .../controller/CurrencyMarketController.java | 124 + .../currency/controller/api/CurrencyApi.java | 27 + .../controller/api/CurrencyMarketApi.java | 71 + .../server/currency/dto/CurrencyDTO.java | 28 + .../currency/dto/CurrencyMarketDTO.java | 33 + .../dto/CurrencyMarketHistoryDTO.java | 21 + .../currency/dto/CurrencyMarketKDTO.java | 26 + .../server/currency/dto/CurrencyPairDTO.java | 15 + .../inner/CurrencyMarketInnerController.java | 39 + .../inner/api/CurrencyMarketInnerApi.java | 17 + .../currency/mapper/CurrencyMapper.java | 23 + .../currency/mapper/CurrencyMarketMapper.java | 25 + .../currency/mapper/CurrencyPairMapper.java | 20 + .../server/currency/model/Currency.java | 52 + .../server/currency/model/CurrencyMarket.java | 30 + .../server/currency/model/CurrencyPair.java | 30 + .../server/currency/redis/HistoryCache.java | 38 + .../server/currency/redis/MarketCache.java | 53 + .../server/currency/redis/MarketKCache.java | 72 + .../currency/redis/MarketLegalCache.java | 36 + .../server/currency/redis/RedissonConfig.java | 41 + .../server/currency/redis/RedissonTool.java | 48 + .../service/CurrencyMarketService.java | 142 + .../currency/service/CurrencyPairService.java | 35 + .../currency/service/CurrencyService.java | 17 + .../impl/CurrencyMarketServiceImpl.java | 627 ++ .../service/impl/CurrencyPairServiceImpl.java | 59 + .../service/impl/CurrencyServiceImpl.java | 32 + .../currency/websocket/WebSocketConfig.java | 39 + .../websocket/WebSocketSendMsgUtils.java | 37 + .../src/main/resources/application.yml | 4 + .../src/main/resources/bootstrap.yml | 22 + .../main/resources/logback/logback-dev.xml | 54 + .../main/resources/logback/logback-pro.xml | 54 + .../main/resources/mapper/CurrencyMapper.xml | 47 + .../resources/mapper/CurrencyMarketMapper.xml | 62 + .../resources/mapper/CurrencyPairMapper.xml | 28 + .../blockchain-server-databot/pom.xml | 57 + .../server/databot/BotApplication.java | 12 + .../common/constant/CommonConstant.java | 12 + .../databot/common/enums/DataBotEnums.java | 34 + .../common/exception/DataBotExeption.java | 39 + .../common/redisson/RedissonConfig.java | 41 + .../databot/common/redisson/RedissonTool.java | 48 + .../databot/dto/rpc/CurrencyMarketDTO.java | 32 + .../databot/dto/rpc/PublishOrderDTO.java | 25 + .../server/databot/entity/CurrencyConfig.java | 62 + .../server/databot/entity/MatchConfig.java | 50 + .../server/databot/feign/CctFeign.java | 85 + .../server/databot/feign/CurrencyFeign.java | 42 + .../databot/mapper/CurrencyConfigMapper.java | 32 + .../databot/mapper/MatchConfigMapper.java | 28 + .../databot/redis/CurrencyConfigCache.java | 143 + .../databot/redis/MatchConfigCache.java | 88 + .../databot/redis/PublishOrderCache.java | 170 + .../databot/schedule/KMarketScheduling.java | 605 ++ .../databot/schedule/MatchScheduling.java | 137 + .../service/CurrencyConfigService.java | 29 + .../databot/service/MatchConfigService.java | 14 + .../server/databot/service/MatchService.java | 14 + .../impl/CurrencyConfigServiceImpl.java | 73 + .../service/impl/MatchConfigServiceImpl.java | 57 + .../service/impl/MatchServiceImpl.java | 87 + .../src/main/resources/application.yml | 3 + .../src/main/resources/bootstrap.yml | 22 + .../main/resources/logback/logback-dev.xml | 54 + .../main/resources/logback/logback-pro.xml | 54 + .../resources/mapper/CurrencyConfigMapper.xml | 43 + .../resources/mapper/MatchConfigMapper.xml | 33 + .../blockchain-server-eos/pom.xml | 57 + .../blockchain/server/eos/EosApplication.java | 21 + .../eos/common/config/InterceptorConf.java | 32 + .../eos/common/constant/EosConstant.java | 90 + .../eos/common/enums/EosWalletEnums.java | 68 + .../common/exception/EosWalletException.java | 39 + .../server/eos/common/util/EosUtil.java | 178 + .../server/eos/common/util/RedisUtil.java | 88 + .../ConfigWalletParamController.java | 36 + .../eos/controller/EosTokenController.java | 36 + .../eos/controller/EosWalletController.java | 77 + .../eos/controller/EosWalletInController.java | 42 + .../EosWalletTransferController.java | 68 + .../controller/api/ConfigWalletParamApi.java | 17 + .../eos/controller/api/EosTokenApi.java | 17 + .../eos/controller/api/EosWalletApi.java | 44 + .../eos/controller/api/EosWalletInApi.java | 17 + .../controller/api/EosWalletTransferApi.java | 28 + .../server/eos/dto/BlockNumberDTO.java | 28 + .../server/eos/dto/BlockchainNumDTO.java | 23 + .../blockchain/server/eos/dto/TokenDTO.java | 26 + .../blockchain/server/eos/dto/WalletDTO.java | 29 + .../server/eos/dto/WalletInDTO.java | 25 + .../server/eos/dto/WalletTransferDTO.java | 37 + .../server/eos/entity/Application.java | 27 + .../server/eos/entity/BlockNumber.java | 34 + .../server/eos/entity/ConfigWalletParam.java | 35 + .../blockchain/server/eos/entity/Token.java | 39 + .../server/eos/entity/TransferAuditing.java | 39 + .../blockchain/server/eos/entity/Wallet.java | 44 + .../server/eos/entity/WalletIn.java | 36 + .../server/eos/entity/WalletOut.java | 40 + .../server/eos/entity/WalletTransfer.java | 61 + .../com/blockchain/server/eos/eos4j/Ecc.java | 154 + .../blockchain/server/eos/eos4j/EosTest.java | 417 + .../eos/eos4j/GetTransactionParamentDto.java | 30 + .../server/eos/eos4j/JsonUtils.java | 273 + .../server/eos/eos4j/OfflineSign.java | 142 + .../server/eos/eos4j/OfflineTest.java | 82 + .../com/blockchain/server/eos/eos4j/Rpc.java | 513 ++ .../com/blockchain/server/eos/eos4j/Test.java | 95 + .../eos/eos4j/api/exception/ApiError.java | 39 + .../eos/eos4j/api/exception/ApiException.java | 40 + .../server/eos/eos4j/api/exception/Error.java | 54 + .../eos/eos4j/api/exception/ErrorDetails.java | 57 + .../eos/eos4j/api/service/RpcService.java | 42 + .../server/eos/eos4j/api/utils/Generator.java | 48 + .../server/eos/eos4j/api/vo/BaseVo.java | 5 + .../server/eos/eos4j/api/vo/Block.java | 145 + .../server/eos/eos4j/api/vo/ChainInfo.java | 170 + .../server/eos/eos4j/api/vo/SignParam.java | 72 + .../server/eos/eos4j/api/vo/TableRows.java | 30 + .../server/eos/eos4j/api/vo/TableRowsReq.java | 58 + .../eos/eos4j/api/vo/account/Account.java | 158 + .../eos/eos4j/api/vo/account/CpuLimit.java | 55 + .../server/eos/eos4j/api/vo/account/Key.java | 44 + .../eos/eos4j/api/vo/account/NetLimit.java | 55 + .../eos/eos4j/api/vo/account/Permission.java | 59 + .../eos4j/api/vo/account/RequiredAuth.java | 54 + .../eos/eos4j/api/vo/transaction/Act.java | 67 + .../api/vo/transaction/ActionTraces.java | 31 + .../eos/eos4j/api/vo/transaction/Data.java | 67 + .../eos4j/api/vo/transaction/Processed.java | 118 + .../eos/eos4j/api/vo/transaction/Receipt.java | 55 + .../eos4j/api/vo/transaction/Transaction.java | 47 + .../eos/eos4j/api/vo/transaction/push/Tx.java | 106 + .../api/vo/transaction/push/TxAction.java | 68 + .../api/vo/transaction/push/TxActionAuth.java | 42 + .../vo/transaction/push/TxExtenstions.java | 15 + .../api/vo/transaction/push/TxRequest.java | 53 + .../eos4j/api/vo/transaction/push/TxSign.java | 41 + .../server/eos/eos4j/ecc/Curve.java | 105 + .../server/eos/eos4j/ecc/EccTool.java | 189 + .../server/eos/eos4j/ecc/Ecdsa.java | 270 + .../server/eos/eos4j/ecc/FieldElement.java | 166 + .../server/eos/eos4j/ecc/Point.java | 214 + .../server/eos/eos4j/ecc/Secp256k.java | 53 + .../server/eos/eos4j/ese/Action.java | 27 + .../server/eos/eos4j/ese/DataParam.java | 74 + .../server/eos/eos4j/ese/DataType.java | 27 + .../blockchain/server/eos/eos4j/ese/Ese.java | 156 + .../server/eos/eos4j/utils/Base58.java | 163 + .../server/eos/eos4j/utils/ByteBuffer.java | 28 + .../server/eos/eos4j/utils/ByteUtils.java | 399 + .../server/eos/eos4j/utils/EException.java | 38 + .../server/eos/eos4j/utils/GeneralDigest.java | 109 + .../server/eos/eos4j/utils/Hex.java | 84 + .../server/eos/eos4j/utils/ObjectUtils.java | 157 + .../server/eos/eos4j/utils/Ripemd160.java | 533 ++ .../server/eos/eos4j/utils/Sha.java | 86 + .../server/eos/feign/UserServerFegin.java | 46 + .../server/eos/feign/WalletTransferFegin.java | 24 + .../eos/inner/EosWalletInnerController.java | 52 + .../server/eos/inner/api/EosWalletApi.java | 30 + .../server/eos/mapper/ApplicationMapper.java | 22 + .../server/eos/mapper/BlockNumberMapper.java | 66 + .../eos/mapper/ConfigWalletParamMapper.java | 15 + .../server/eos/mapper/EosWalletInMapper.java | 25 + .../server/eos/mapper/TokenMapper.java | 31 + .../server/eos/mapper/WalletInMapper.java | 41 + .../server/eos/mapper/WalletMapper.java | 83 + .../eos/mapper/WalletTransferMapper.java | 35 + .../TransferDisposeTimerTask.java | 54 + .../eos/scheduleTask/WallertTimerTask.java | 64 + .../eos/service/ConfigWalletParamService.java | 26 + .../eos/service/EosApplicationService.java | 26 + .../eos/service/EosBlockNumberService.java | 59 + .../server/eos/service/EosTokenService.java | 42 + .../eos/service/EosWalletInService.java | 35 + .../server/eos/service/EosWalletService.java | 153 + .../eos/service/EosWalletTransferService.java | 80 + .../eos/service/EosWalletUtilService.java | 48 + .../impl/ConfigWalletParamServiceImpl.java | 52 + .../impl/EosApplicationServiceImpl.java | 42 + .../impl/EosBlockNumberServiceImpl.java | 201 + .../eos/service/impl/EosTokenServiceImpl.java | 64 + .../service/impl/EosWalletInServiceImpl.java | 89 + .../service/impl/EosWalletServiceImpl.java | 433 + .../impl/EosWalletTransferServiceImpl.java | 153 + .../impl/EosWalletUtilServiceImpl.java | 109 + .../src/main/resources/application.yml | 3 + .../src/main/resources/bootstrap.yml | 22 + .../i18n/MessgesBundle_zh_CN.properties | 5 + .../main/resources/logback/logback-dev.xml | 54 + .../main/resources/logback/logback-pro.xml | 54 + .../resources/mapper/ApplicationMapper.xml | 20 + .../resources/mapper/BlockNumberMapper.xml | 55 + .../mapper/ConfigWalletParamMapper.xml | 19 + .../src/main/resources/mapper/TokenMapper.xml | 25 + .../main/resources/mapper/WalletInMapper.xml | 39 + .../main/resources/mapper/WalletMapper.xml | 81 + .../resources/mapper/WalletTransferMapper.xml | 42 + .../blockchain-server-eth/pom.xml | 63 + .../blockchain/server/eth/EthApplication.java | 20 + .../server/eth/common/config/EthConfig.java | 31 + .../eth/common/config/InterceptorConf.java | 32 + .../common/constants/EthConfigConstants.java | 30 + .../common/constants/EthWalletConstants.java | 49 + .../eth/common/enums/EthWalletEnums.java | 97 + .../common/exception/EthWalletException.java | 39 + .../eth/common/util/BaseHelperUtil.java | 22 + .../server/eth/common/util/DataCheckUtil.java | 84 + .../eth/common/util/RedisBlockUtil.java | 133 + .../eth/common/util/RedisPrivateUtil.java | 60 + .../eth/common/util/RedisWalletAddrUtil.java | 91 + .../ConfigWalletParamController.java | 29 + .../eth/controller/EthTokenController.java | 38 + .../eth/controller/EthWalletController.java | 150 + .../EthWalletTransferController.java | 61 + .../controller/api/ConfigWalletParamApi.java | 12 + .../eth/controller/api/EthTokenApi.java | 16 + .../eth/controller/api/EthWalletApi.java | 71 + .../controller/api/EthWalletTransferApi.java | 17 + .../blockchain/server/eth/dto/EthGasDTO.java | 26 + .../server/eth/dto/EthWalletDTO.java | 30 + .../server/eth/dto/EthWalletTransferDTO.java | 38 + .../server/eth/dto/Web3jTransferDTO.java | 19 + .../server/eth/dto/Web3jWalletDTO.java | 10 + .../server/eth/entity/ConfigWalletParam.java | 34 + .../server/eth/entity/EthApplication.java | 26 + .../server/eth/entity/EthBlockNumber.java | 33 + .../server/eth/entity/EthGasWallet.java | 32 + .../server/eth/entity/EthToken.java | 43 + .../eth/entity/EthTransferAuditing.java | 34 + .../server/eth/entity/EthWallet.java | 45 + .../server/eth/entity/EthWalletKey.java | 36 + .../server/eth/entity/EthWalletOut.java | 36 + .../server/eth/entity/EthWalletTransfer.java | 57 + .../server/eth/feign/UserFeign.java | 42 + .../server/eth/feign/api/UserInnerApi.java | 12 + .../server/eth/inner/EthWalletInner.java | 63 + .../eth/inner/EthWalletTransferInner.java | 51 + .../server/eth/inner/api/EthWalletApi.java | 39 + .../eth/inner/api/EthWalletTransferApi.java | 17 + .../eth/mapper/ConfigWalletParamMapper.java | 16 + .../eth/mapper/EthApplicationMapper.java | 14 + .../eth/mapper/EthBlockNumberMapper.java | 30 + .../server/eth/mapper/EthGasWalletMapper.java | 18 + .../server/eth/mapper/EthTokenMapper.java | 22 + .../eth/mapper/EthTransferAuditingMapper.java | 14 + .../server/eth/mapper/EthWalletKeyMapper.java | 26 + .../server/eth/mapper/EthWalletMapper.java | 117 + .../server/eth/mapper/EthWalletOutMapper.java | 14 + .../eth/mapper/EthWalletTransferMapper.java | 29 + .../eth/scheduleTask/EthTxInTimerTask.java | 141 + .../eth/scheduleTask/EthWallertTimerTask.java | 116 + .../service/IConfigWalletParamService.java | 21 + .../eth/service/IEthApplicationService.java | 30 + .../eth/service/IEthBlockNumberService.java | 80 + .../eth/service/IEthGasWalletService.java | 23 + .../server/eth/service/IEthTokenService.java | 52 + .../eth/service/IEthWalletKeyService.java | 46 + .../server/eth/service/IEthWalletService.java | 208 + .../service/IEthWalletTransferService.java | 126 + .../impl/ConfigWalletParamServiceImpl.java | 39 + .../impl/EthApplicationServiceImpl.java | 47 + .../impl/EthBlockNumberServiceImpl.java | 205 + .../service/impl/EthGasWalletServiceImpl.java | 43 + .../eth/service/impl/EthTokenServiceImpl.java | 81 + .../service/impl/EthWalletKeyServiceImpl.java | 164 + .../service/impl/EthWalletServiceImpl.java | 559 ++ .../impl/EthWalletTransferServiceImpl.java | 164 + .../server/eth/web3j/IBaseWeb3j.java | 72 + .../server/eth/web3j/IWalletWeb3j.java | 111 + .../server/eth/web3j/impl/BaseWeb3jImpl.java | 170 + .../eth/web3j/impl/WalletWeb3jImpl.java | 258 + .../src/main/resources/application.yml | 3 + .../src/main/resources/bootstrap.yml | 22 + .../main/resources/logback/logback-dev.xml | 54 + .../main/resources/logback/logback-pro.xml | 54 + .../mapper/ConfigWalletParamMapper.xml | 7 + .../resources/mapper/EthApplicationMapper.xml | 11 + .../resources/mapper/EthBlockNumberMapper.xml | 27 + .../resources/mapper/EthGasWalletMapper.xml | 6 + .../main/resources/mapper/EthTokenMapper.xml | 22 + .../mapper/EthTransferAuditingMapper.xml | 15 + .../resources/mapper/EthWalletKeyMapper.xml | 28 + .../main/resources/mapper/EthWalletMapper.xml | 125 + .../resources/mapper/EthWalletOutMapper.xml | 16 + .../mapper/EthWalletTransferMapper.xml | 32 + .../blockchain-server-imJg/pom.xml | 33 + .../server/imjg/ImJgApplication.java | 15 + .../imjg/common/enums/MessageLogEnums.java | 26 + .../imjg/common/enums/NodeCueEnums.java | 26 + .../server/imjg/common/util/HttpUtils.java | 206 + .../imjg/common/util/JiGuangIMUtils.java | 18 + .../imjg/controller/ImjgController.java | 135 + .../imjg/controller/ImjgUserController.java | 16 + .../server/imjg/controller/api/ImjgApi.java | 80 + .../blockchain/server/imjg/dto/JgError.java | 14 + .../blockchain/server/imjg/dto/JgInitDTO.java | 25 + .../server/imjg/dto/JgMessageBodyDTO.java | 64 + .../server/imjg/dto/JgMessageDTO.java | 68 + .../server/imjg/dto/JgMessageLogDTO.java | 19 + .../server/imjg/dto/JgResourceDTO.java | 16 + .../server/imjg/dto/JgResponse.java | 10 + .../blockchain/server/imjg/dto/JgUserDTO.java | 37 + .../server/imjg/dto/JgUserInfoDTO.java | 33 + .../server/imjg/dto/JgUserStatDTO.java | 14 + .../server/imjg/dto/MessageDTO.java | 62 + .../server/imjg/dto/MessageLogDTO.java | 69 + .../blockchain/server/imjg/dto/UserDTO.java | 47 + .../server/imjg/entity/ImjgMessage.java | 54 + .../server/imjg/entity/ImjgMessageLog.java | 54 + .../server/imjg/entity/ImjgUser.java | 35 + .../server/imjg/feign/UserFeign.java | 20 + .../imjg/inner/ImjgInnerController.java | 192 + .../server/imjg/inner/api/ImjgInnerApi.java | 78 + .../imjg/mapper/ImjgMessageLogMapper.java | 18 + .../server/imjg/mapper/ImjgMessageMapper.java | 11 + .../server/imjg/mapper/ImjgUserMapper.java | 13 + .../imjg/service/ImjgMessageLogService.java | 54 + .../server/imjg/service/ImjgUserService.java | 30 + .../impl/ImjgMessageLogServiceImpl.java | 157 + .../service/impl/ImjgUserServiceImpl.java | 33 + .../src/main/resources/application.yml | 3 + .../src/main/resources/bootstrap.yml | 22 + .../main/resources/logback/logback-dev.xml | 54 + .../main/resources/logback/logback-pro.xml | 54 + .../resources/mapper/ImjgMessageLogMapper.xml | 57 + .../resources/mapper/ImjgMessageMapper.xml | 58 + .../main/resources/mapper/ImjgUserMapper.xml | 60 + .../blockchain-server-otc/pom.xml | 35 + .../blockchain/server/otc/OtcApplication.java | 12 + .../otc/common/constant/AdConstants.java | 16 + .../otc/common/constant/AppealConstants.java | 31 + .../otc/common/constant/BillConstants.java | 19 + .../otc/common/constant/CommonConstans.java | 24 + .../otc/common/constant/ConfigConstants.java | 20 + .../common/constant/MarketApplyConstants.java | 20 + .../common/constant/MarketUserConstants.java | 14 + .../otc/common/constant/OrderConstants.java | 17 + .../common/constant/UserHandleConstants.java | 25 + .../otc/common/constant/UserPayConstants.java | 20 + .../server/otc/common/enums/JgMsgEnums.java | 48 + .../server/otc/common/enums/OtcEnums.java | 84 + .../otc/common/exception/OtcException.java | 35 + .../common/interceptor/InterceptorConf.java | 61 + .../interceptor/TradingInterceptor.java | 16 + .../common/listener/RedisListenerBean.java | 55 + .../otc/common/listener/RedisReceiver.java | 31 + .../otc/common/util/CheckDecimalUtil.java | 27 + .../server/otc/common/util/ImUtil.java | 55 + .../server/otc/controller/AdController.java | 222 + .../otc/controller/AppealController.java | 88 + .../server/otc/controller/CoinController.java | 42 + .../otc/controller/ConfigController.java | 48 + .../otc/controller/MarketApplyController.java | 55 + .../otc/controller/MarketUserController.java | 35 + .../otc/controller/OrderController.java | 135 + .../otc/controller/UserPayInfoController.java | 181 + .../server/otc/controller/api/AdApi.java | 81 + .../server/otc/controller/api/AppealApi.java | 25 + .../server/otc/controller/api/CoinApi.java | 15 + .../server/otc/controller/api/ConfigApi.java | 20 + .../otc/controller/api/MarketApplyApi.java | 15 + .../otc/controller/api/MarketUserApi.java | 10 + .../server/otc/controller/api/OrderApi.java | 81 + .../server/otc/controller/api/UserPayApi.java | 84 + .../server/otc/dto/ad/ListAdDTO.java | 31 + .../server/otc/dto/ad/ListUserAdDTO.java | 27 + .../server/otc/dto/ad/PublishAdParamDTO.java | 20 + .../otc/dto/appeal/AppealHandleLogDTO.java | 11 + .../server/otc/dto/coin/CoinDTO.java | 11 + .../otc/dto/coin/CoinServiceChargeDTO.java | 13 + .../server/otc/dto/config/ConfigDTO.java | 11 + .../server/otc/dto/jgim/ImjgMessage.java | 54 + .../server/otc/dto/jgim/JgMassageParam.java | 77 + .../server/otc/dto/jgim/JgMessageBodyDTO.java | 63 + .../server/otc/dto/order/OrderDTO.java | 34 + .../server/otc/dto/user/UserBaseDTO.java | 24 + .../com/blockchain/server/otc/entity/Ad.java | 60 + .../blockchain/server/otc/entity/Appeal.java | 35 + .../server/otc/entity/AppealDetail.java | 37 + .../server/otc/entity/AppealHandleLog.java | 39 + .../server/otc/entity/AppealImg.java | 33 + .../blockchain/server/otc/entity/Bill.java | 42 + .../blockchain/server/otc/entity/Coin.java | 48 + .../blockchain/server/otc/entity/Config.java | 37 + .../server/otc/entity/DealStats.java | 39 + .../server/otc/entity/MarketApply.java | 42 + .../otc/entity/MarketApplyHandleLog.java | 38 + .../server/otc/entity/MarketFreeze.java | 37 + .../server/otc/entity/MarketUser.java | 34 + .../otc/entity/MarketUserHandleLog.java | 38 + .../blockchain/server/otc/entity/Order.java | 62 + .../server/otc/entity/UserHandleLog.java | 37 + .../server/otc/entity/UserPayInfo.java | 45 + .../blockchain/server/otc/feign/BTCFeign.java | 30 + .../blockchain/server/otc/feign/EOSFeign.java | 30 + .../blockchain/server/otc/feign/ETHFeign.java | 44 + .../server/otc/feign/IMJGFeign.java | 34 + .../server/otc/feign/PushFeign.java | 25 + .../server/otc/feign/UserFeign.java | 49 + .../server/otc/mapper/AdMapper.java | 69 + .../server/otc/mapper/AppealDetailMapper.java | 14 + .../otc/mapper/AppealHandleLogMapper.java | 24 + .../server/otc/mapper/AppealImgMapper.java | 15 + .../server/otc/mapper/AppealMapper.java | 24 + .../server/otc/mapper/BillMapper.java | 15 + .../server/otc/mapper/CoinMapper.java | 41 + .../server/otc/mapper/ConfigMapper.java | 23 + .../server/otc/mapper/DealStatsMapper.java | 45 + .../mapper/MarketApplyHandleLogMapper.java | 14 + .../server/otc/mapper/MarketApplyMapper.java | 26 + .../server/otc/mapper/MarketFreezeMapper.java | 23 + .../otc/mapper/MarketUserHandleLogMapper.java | 14 + .../server/otc/mapper/MarketUserMapper.java | 23 + .../server/otc/mapper/OrderMapper.java | 91 + .../otc/mapper/UserHandleLogMapper.java | 15 + .../server/otc/mapper/UserPayInfoMapper.java | 33 + .../server/otc/redis/OrderCache.java | 50 + .../schedule/AutoCancelOrderScheduling.java | 79 + .../server/otc/service/AdService.java | 88 + .../otc/service/AppealDetailService.java | 14 + .../otc/service/AppealHandleLogService.java | 22 + .../server/otc/service/AppealImgService.java | 12 + .../server/otc/service/AppealService.java | 22 + .../server/otc/service/BillService.java | 18 + .../server/otc/service/CoinService.java | 30 + .../server/otc/service/ConfigService.java | 61 + .../server/otc/service/DealStatsService.java | 47 + .../otc/service/MarketApplyService.java | 22 + .../otc/service/MarketFreezeService.java | 13 + .../server/otc/service/MarketUserService.java | 33 + .../server/otc/service/OrderService.java | 138 + .../otc/service/UserHandleLogService.java | 13 + .../otc/service/UserPayInfoService.java | 73 + .../server/otc/service/WalletService.java | 37 + .../otc/service/impl/AdServiceImpl.java | 561 ++ .../service/impl/AppealDetailServiceImpl.java | 33 + .../impl/AppealHandleLogServiceImpl.java | 38 + .../service/impl/AppealImgServiceImpl.java | 29 + .../otc/service/impl/AppealServiceImpl.java | 234 + .../otc/service/impl/BillServiceImpl.java | 34 + .../otc/service/impl/CoinServiceImpl.java | 35 + .../otc/service/impl/ConfigServiceImpl.java | 115 + .../service/impl/DealStatsServiceImpl.java | 67 + .../service/impl/MarketApplyServiceImpl.java | 169 + .../service/impl/MarketFreezeServiceImpl.java | 19 + .../service/impl/MarketUserServiceImpl.java | 47 + .../otc/service/impl/OrderServiceImpl.java | 1125 +++ .../impl/UserHandleLogServiceImpl.java | 32 + .../service/impl/UserPayInfoServiceImpl.java | 182 + .../otc/service/impl/WalletServiceImpl.java | 125 + .../src/main/resources/application.yml | 3 + .../src/main/resources/bootstrap.yml | 39 + .../main/resources/logback/logback-dev.xml | 54 + .../main/resources/logback/logback-pro.xml | 54 + .../src/main/resources/mapper/AdMapper.xml | 132 + .../resources/mapper/AppealDetailMapper.xml | 15 + .../mapper/AppealHandleLogMapper.xml | 31 + .../main/resources/mapper/AppealImgMapper.xml | 13 + .../main/resources/mapper/AppealMapper.xml | 24 + .../src/main/resources/mapper/BillMapper.xml | 17 + .../src/main/resources/mapper/CoinMapper.xml | 59 + .../main/resources/mapper/ConfigMapper.xml | 25 + .../main/resources/mapper/DealStatsMapper.xml | 51 + .../mapper/MarketApplyHandleLogMapper.xml | 16 + .../resources/mapper/MarketApplyMapper.xml | 29 + .../resources/mapper/MarketFreezeMapper.xml | 25 + .../mapper/MarketUserHandleLogMapper.xml | 16 + .../resources/mapper/MarketUserMapper.xml | 24 + .../src/main/resources/mapper/OrderMapper.xml | 113 + .../resources/mapper/UserHandleLogMapper.xml | 15 + .../resources/mapper/UserPayInfoMapper.xml | 35 + .../blockchain-server-sysconf/pom.xml | 29 + .../server/sysconf/SysConfigApplication.java | 19 + .../common/config/InterceptorConf.java | 14 + .../common/constants/AgreementConstant.java | 10 + .../common/constants/ContactUsConstant.java | 9 + .../common/constants/ImageConstant.java | 14 + .../common/constants/NewsConstant.java | 12 + .../common/constants/NoticeConstant.java | 10 + .../common/constants/UserConstant.java | 23 + .../common/constants/VersionConstant.java | 14 + .../sysconf/common/enums/SysConfigEnums.java | 27 + .../common/exception/SysConfigException.java | 33 + .../sysconf/common/util/CheckUtils.java | 164 + .../sysconf/controller/AboutUsController.java | 39 + .../controller/AdSliderController.java | 32 + .../controller/AgreementController.java | 45 + .../controller/ContactUsController.java | 39 + .../controller/HelpCenterController.java | 59 + .../sysconf/controller/ImageController.java | 50 + .../sysconf/controller/NewsController.java | 40 + .../sysconf/controller/NoticeController.java | 48 + .../controller/ProjectCenterController.java | 112 + .../sysconf/controller/VersionController.java | 53 + .../controller/WgtVersionController.java | 33 + .../sysconf/controller/api/AboutUsApi.java | 11 + .../sysconf/controller/api/AdSliderApi.java | 10 + .../sysconf/controller/api/AgreementApi.java | 13 + .../sysconf/controller/api/ContactUsApi.java | 11 + .../sysconf/controller/api/HelpCenterApi.java | 21 + .../sysconf/controller/api/NewsApi.java | 20 + .../controller/api/ProjectCenterApi.java | 48 + .../controller/api/SysconfImageApi.java | 20 + .../controller/api/SysconfNoticeApi.java | 18 + .../sysconf/controller/api/VersionApi.java | 15 + .../sysconf/controller/api/WgtVersionApi.java | 12 + .../sysconf/dto/AboutUsQueryConditionDTO.java | 20 + .../server/sysconf/dto/AdSliderDto.java | 36 + .../dto/ContactUsQueryConditionDTO.java | 19 + .../server/sysconf/dto/HelpCenterDTO.java | 24 + .../server/sysconf/dto/NewsDTO.java | 19 + .../sysconf/dto/NewsQueryConditionDTO.java | 18 + .../server/sysconf/dto/ProjectCenterDto.java | 16 + .../server/sysconf/entity/AboutUs.java | 35 + .../server/sysconf/entity/AdSlider.java | 33 + .../server/sysconf/entity/Agreement.java | 33 + .../server/sysconf/entity/ContactUs.java | 40 + .../server/sysconf/entity/HelpCenter.java | 45 + .../server/sysconf/entity/News.java | 37 + .../sysconf/entity/ProjectCenterClassify.java | 42 + .../sysconf/entity/ProjectCenterInfo.java | 79 + .../sysconf/entity/ProjectCenterReport.java | 46 + .../sysconf/entity/ProjectCenterStar.java | 37 + .../server/sysconf/entity/SmsCount.java | 26 + .../server/sysconf/entity/SystemImage.java | 37 + .../server/sysconf/entity/SystemNotice.java | 39 + .../server/sysconf/entity/Version.java | 56 + .../server/sysconf/entity/WgtVersion.java | 47 + .../server/sysconf/mapper/AboutUsMapper.java | 17 + .../server/sysconf/mapper/AdSliderMapper.java | 26 + .../sysconf/mapper/AgreementMapper.java | 19 + .../sysconf/mapper/ContactUsMapper.java | 16 + .../sysconf/mapper/HelpCenterMapper.java | 24 + .../server/sysconf/mapper/NewsMapper.java | 24 + .../sysconf/mapper/ProjectCenterMapper.java | 20 + .../mapper/ProjectCenterStarMapper.java | 11 + .../sysconf/mapper/SystemImageMapper.java | 18 + .../sysconf/mapper/SystemNoticeMapper.java | 21 + .../server/sysconf/mapper/VersionMapper.java | 18 + .../sysconf/mapper/WgtVersionMapper.java | 17 + .../sysconf/service/AboutUsService.java | 23 + .../sysconf/service/AdSliderService.java | 16 + .../sysconf/service/AgreementService.java | 18 + .../sysconf/service/ContactUsService.java | 19 + .../sysconf/service/HelpCenterService.java | 21 + .../server/sysconf/service/NewsService.java | 19 + .../sysconf/service/ProjectCenterService.java | 25 + .../service/ProjectCenterStarService.java | 25 + .../sysconf/service/SystemImageService.java | 13 + .../sysconf/service/SystemNoticeService.java | 14 + .../sysconf/service/VersionService.java | 36 + .../sysconf/service/WgtVersionService.java | 20 + .../service/impl/AboutUsServiceImpl.java | 33 + .../service/impl/AdSliderServiceImpl.java | 22 + .../service/impl/AgreementServiceImpl.java | 28 + .../service/impl/ContactUsServiceImpl.java | 29 + .../service/impl/HelpCenterServiceImpl.java | 37 + .../sysconf/service/impl/NewsServiceImpl.java | 41 + .../impl/ProjectCenterServiceImpl.java | 53 + .../impl/ProjectCenterStarServiceImpl.java | 45 + .../service/impl/SystemImageServiceImpl.java | 26 + .../service/impl/SystemNoticeServiceImpl.java | 28 + .../service/impl/VersionServiceImpl.java | 48 + .../service/impl/WgtVersionServiceImpl.java | 28 + .../src/main/resources/application.yml | 3 + .../src/main/resources/bootstrap.yml | 22 + .../i18n/MessgesBundle_zh_CN.properties | 5 + .../main/resources/logback/logback-dev.xml | 54 + .../main/resources/logback/logback-pro.xml | 54 + .../main/resources/mapper/AboutUsMapper.xml | 26 + .../main/resources/mapper/AdSliderMapper.xml | 20 + .../main/resources/mapper/ContactUsMapper.xml | 34 + .../resources/mapper/HelpCenterMapper.xml | 44 + .../src/main/resources/mapper/NewsMapper.xml | 84 + .../resources/mapper/ProjectCenterMapper.xml | 49 + .../mapper/ProjectCenterStarMapper.xml | 15 + .../resources/mapper/SystemImageMapper.xml | 17 + .../resources/mapper/SystemNoticeMapper.xml | 17 + .../resources/mapper/UserAgreementMapper.xml | 22 + .../main/resources/mapper/VersionMapper.xml | 39 + .../resources/mapper/WgtVersionMapper.xml | 28 + .../blockchain-server-user/pom.xml | 68 + .../server/user/UserApplication.java | 20 + .../user/common/config/InterceptorConf.java | 33 + .../other/InternationalConstant.java | 35 + .../common/constants/other/RedisConstant.java | 43 + .../constants/other/StringFormatConstant.java | 37 + .../other/UserFileUploadConstant.java | 10 + .../sql/AuthenticationApplyConstant.java | 14 + .../common/constants/sql/ConfigConstant.java | 12 + .../common/constants/sql/GlobalConstant.java | 13 + .../constants/sql/SmsCountConstant.java | 15 + .../sql/UserAuthenticationConstant.java | 21 + .../constants/sql/UserInfoConstant.java | 16 + .../constants/sql/UserListConstant.java | 21 + .../constants/sql/UserLoginLogConstant.java | 10 + .../constants/sql/UserMainConstant.java | 10 + .../common/constants/sql/UserOptConstant.java | 14 + .../user/common/enums/SmsCountEnum.java | 58 + .../server/user/common/enums/UserEnums.java | 93 + .../user/common/exceprion/UserException.java | 52 + .../server/user/common/utils/CheckUtils.java | 148 + .../server/user/common/utils/CommonUtils.java | 47 + .../common/utils/EMailTransmitHelper.java | 137 + .../user/common/utils/EmailCodeUtils.java | 71 + .../user/common/utils/FileUploadHelper.java | 174 + .../utils/GoogleAuthenticatorUtils.java | 149 + .../user/common/utils/SendSmsgCode.java | 126 + .../user/common/utils/SmsCodeUtils.java | 77 + .../user/common/utils/push/PushUtils.java | 83 + .../user/common/utils/push/TemplateUtils.java | 108 + .../user/controller/LoginController.java | 314 + .../user/controller/PushUserController.java | 38 + .../user/controller/UserController.java | 430 + .../controller/UserLoginLogController.java | 56 + .../server/user/controller/api/LoginApi.java | 102 + .../user/controller/api/PushUserApi.java | 10 + .../server/user/controller/api/UserApi.java | 144 + .../controller/api/UserAuthenticationApi.java | 72 + .../user/controller/api/UserLoginLogApi.java | 21 + .../server/user/dto/PushInfoDTO.java | 31 + .../server/user/dto/UserBaseDTO.java | 24 + .../server/user/dto/UserLoginLogDto.java | 19 + .../user/entity/AuthenticationApply.java | 81 + .../user/entity/AuthenticationApplyLog.java | 39 + .../blockchain/server/user/entity/Config.java | 32 + .../user/entity/HighAuthenticationApply.java | 59 + .../server/user/entity/PushUser.java | 37 + .../server/user/entity/SmsCount.java | 24 + .../user/entity/UserAuthentication.java | 80 + .../server/user/entity/UserInfo.java | 89 + .../server/user/entity/UserList.java | 47 + .../server/user/entity/UserLogin.java | 47 + .../server/user/entity/UserLoginLog.java | 46 + .../server/user/entity/UserMain.java | 58 + .../server/user/entity/UserOptLog.java | 53 + .../server/user/entity/UserRelation.java | 52 + .../server/user/feign/BtcFeign.java | 16 + .../server/user/feign/EosFeign.java | 16 + .../server/user/feign/EthFeign.java | 16 + .../server/user/inner/PushInner.java | 31 + .../server/user/inner/UserInner.java | 203 + .../server/user/inner/api/PushInnerApi.java | 12 + .../server/user/inner/api/UserInnerApi.java | 79 + .../mapper/AuthenticationApplyLogMapper.java | 17 + .../mapper/AuthenticationApplyMapper.java | 22 + .../server/user/mapper/ConfigMapper.java | 13 + .../mapper/HighAuthenticationApplyMapper.java | 22 + .../server/user/mapper/PushUserMapper.java | 43 + .../server/user/mapper/SmsCountMapper.java | 34 + .../user/mapper/UserAuthenticationMapper.java | 14 + .../server/user/mapper/UserInfoMapper.java | 19 + .../server/user/mapper/UserListMapper.java | 14 + .../user/mapper/UserLoginLogMapper.java | 23 + .../server/user/mapper/UserLoginMapper.java | 16 + .../server/user/mapper/UserMainMapper.java | 19 + .../server/user/mapper/UserOptLogMapper.java | 14 + .../user/mapper/UserRelationMapper.java | 14 + .../AuthenticationApplyLogService.java | 12 + .../service/AuthenticationApplyService.java | 47 + .../server/user/service/ConfigService.java | 11 + .../server/user/service/PushService.java | 16 + .../server/user/service/PushUserService.java | 55 + .../server/user/service/SmsCountService.java | 14 + .../server/user/service/UserInfoService.java | 64 + .../server/user/service/UserListService.java | 11 + .../user/service/UserLoginLogService.java | 20 + .../server/user/service/UserLoginService.java | 67 + .../server/user/service/UserMainService.java | 76 + .../user/service/UserOptLogService.java | 19 + .../user/service/UserRelationService.java | 27 + .../server/user/service/UserService.java | 22 + .../AuthenticationApplyLogServiceImpl.java | 26 + .../impl/AuthenticationApplyServiceImpl.java | 146 + .../user/service/impl/ConfigServiceImpl.java | 33 + .../user/service/impl/PushServiceImpl.java | 38 + .../service/impl/PushUserServiceImpl.java | 112 + .../service/impl/SmsCountServiceImpl.java | 78 + .../service/impl/UserInfoServiceImpl.java | 163 + .../service/impl/UserListServiceImpl.java | 28 + .../impl/UserLoginLoginServiceImpl.java | 51 + .../service/impl/UserLoginServiceImpl.java | 163 + .../service/impl/UserMainServiceImpl.java | 204 + .../service/impl/UserOptLogServiceImpl.java | 40 + .../service/impl/UserRelationServiceImpl.java | 78 + .../user/service/impl/UserServiceImpl.java | 85 + .../src/main/resources/application.yml | 4 + .../src/main/resources/bootstrap.yml | 33 + .../main/resources/logback/logback-dev.xml | 54 + .../main/resources/logback/logback-pro.xml | 54 + .../mapper/AuthenticationApplyLogMapper.xml | 19 + .../mapper/AuthenticationApplyMapper.xml | 29 + .../main/resources/mapper/ConfigMapper.xml | 18 + .../mapper/HighAuthenticationApplyMapper.xml | 24 + .../main/resources/mapper/PushUserMapper.xml | 43 + .../main/resources/mapper/SmsCountMapper.xml | 28 + .../mapper/UserAuthenticationMapper.xml | 20 + .../main/resources/mapper/UserInfoMapper.xml | 26 + .../main/resources/mapper/UserListMapper.xml | 14 + .../main/resources/mapper/UserLoginMapper.xml | 20 + .../resources/mapper/UserLoinLogMapper.xml | 38 + .../main/resources/mapper/UserMainMapper.xml | 28 + .../resources/mapper/UserOptLogMapper.xml | 15 + .../resources/mapper/UserRelationMapper.xml | 15 + .../src/test/java/MD5Test.java | 11 + blockchain-server/pom.xml | 43 + pom.xml | 119 + spring-cloud/pom.xml | 22 + spring-cloud/spring-cloud-config/pom.xml | 37 + .../cloud/config/ConfigApplication.java | 19 + .../src/main/resources/application.yml | 20 + .../resources/properties/authconfig-dev.yml | 3 + .../main/resources/properties/btcconf-dev.yml | 15 + .../main/resources/properties/dbconf-dev.yml | 41 + .../resources/properties/emailconf-dev.yml | 13 + .../main/resources/properties/eosconf-dev.yml | 8 + .../main/resources/properties/ethconf-dev.yml | 16 + .../resources/properties/fileconf-dev.yml | 2 + .../resources/properties/imjgconf-dev.yml | 10 + .../main/resources/properties/ipconf-dev.yml | 2 + .../resources/properties/marketconf-dev.yml | 9 + .../resources/properties/pushconf-dev.yml | 6 + .../resources/properties/redisconf-dev.yml | 19 + .../main/resources/properties/smsconf-dev.yml | 20 + .../properties/springcloudconf-dev.yml | 26 + .../resources/properties/springconf-dev.yml | 13 + .../main/resources/properties/txconf-dev.yml | 4 + .../main/resources/properties/xssconf-dev.yml | 7 + spring-cloud/spring-cloud-eureka/pom.xml | 29 + .../cloud/eureka/EurekaApplication.java | 17 + .../src/main/resources/application.yml | 15 + .../spring-cloud-hystrix-dashboard/pom.xml | 33 + .../dashboard/DashboardApplication.java | 17 + .../src/main/resources/application.yml | 5 + tx-lcn/LICENSE | 201 + tx-lcn/README.md | 166 + tx-lcn/pom.xml | 29 + tx-lcn/transaction-springcloud/README.md | 16 + tx-lcn/transaction-springcloud/pom.xml | 55 + .../loadbalancer/LcnLoadBalancerRule.java | 94 + .../LcnNoOpLoadBalancerProxy.java | 34 + .../loadbalancer/LcnRibbonConfiguration.java | 37 + .../LcnZoneAwareLoadBalancerProxy.java | 42 + .../tx/RequestInterceptorConfiguration.java | 18 + .../tx/TransactionConfiguration.java | 20 + .../TransactionRestTemplateInterceptor.java | 30 + .../TransactionHttpRequestInterceptor.java | 34 + .../interceptor/TransactionAspect.java | 51 + .../interceptor/TxManagerInterceptor.java | 34 + .../springcloud/listener/ServerListener.java | 48 + .../service/impl/ModelNameServiceImpl.java | 78 + .../main/resources/META-INF/spring.factories | 1 + .../src/main/resources/banner.txt | 13 + tx-lcn/tx-client/README.md | 3 + tx-lcn/tx-client/pom.xml | 126 + .../main/java/com/codingapi/tx/Constants.java | 26 + .../tx/annotation/ITxTransaction.java | 7 + .../tx/annotation/TxTransaction.java | 48 + .../tx/annotation/TxTransactionMode.java | 22 + .../tx/aop/bean/TxCompensateLocal.java | 50 + .../tx/aop/bean/TxTransactionInfo.java | 61 + .../tx/aop/bean/TxTransactionLocal.java | 180 + .../tx/aop/service/AspectBeforeService.java | 11 + .../tx/aop/service/TransactionServer.java | 17 + .../TransactionServerFactoryService.java | 11 + .../service/impl/AspectBeforeServiceImpl.java | 60 + .../TransactionServerFactoryServiceImpl.java | 95 + .../impl/TxDefaultTransactionServerImpl.java | 20 + .../TxRunningNoTransactionServerImpl.java | 52 + .../impl/TxRunningTransactionServerImpl.java | 120 + .../impl/TxStartTransactionServerImpl.java | 154 + .../tx/compensate/model/CompensateInfo.java | 147 + .../codingapi/tx/compensate/package-info.java | 6 + .../compensate/service/CompensateService.java | 17 + .../service/impl/CompensateServiceImpl.java | 49 + .../com/codingapi/tx/config/ConfigReader.java | 32 + .../control/LCNTransactionAspectSupport.java | 53 + .../tx/control/service/IActionService.java | 12 + .../service/TransactionControlService.java | 13 + .../service/impl/ActionCServiceImpl.java | 64 + .../service/impl/ActionTServiceImpl.java | 104 + .../impl/TransactionControlServiceImpl.java | 52 + .../tx/datasource/AbstractResourceProxy.java | 191 + .../codingapi/tx/datasource/ICallClose.java | 10 + .../tx/datasource/ILCNConnection.java | 15 + .../codingapi/tx/datasource/ILCNResource.java | 29 + .../tx/datasource/ILCNTransactionControl.java | 34 + .../datasource/aspect/DataSourceAspect.java | 42 + .../codingapi/tx/datasource/package-info.java | 6 + .../datasource/service/DataSourceService.java | 15 + .../service/impl/DataSourceServiceImpl.java | 45 + .../tx/framework/task/TaskGroup.java | 86 + .../tx/framework/task/TaskGroupManager.java | 74 + .../tx/framework/task/TaskState.java | 31 + .../codingapi/tx/framework/task/TxTask.java | 109 + .../tx/framework/thread/HookRunnable.java | 49 + .../framework/thread/NamedThreadFactory.java | 55 + .../tx/framework/utils/MethodUtils.java | 27 + .../tx/framework/utils/SerializerUtils.java | 38 + .../tx/framework/utils/SocketManager.java | 152 + .../tx/framework/utils/SocketUtils.java | 37 + .../utils/serializer/ISerializer.java | 32 + .../serializer/ProtostuffSerializer.java | 74 + .../utils/serializer/SchemaCache.java | 60 + .../tx/listener/service/InitService.java | 9 + .../tx/listener/service/ModelNameService.java | 13 + .../service/impl/InitServiceImpl.java | 27 + .../java/com/codingapi/tx/model/Request.java | 62 + .../tx/model/TransactionInvocation.java | 83 + .../java/com/codingapi/tx/model/TxGroup.java | 97 + .../java/com/codingapi/tx/model/TxServer.java | 80 + .../tx/netty/handler/TransactionHandler.java | 122 + .../tx/netty/service/MQTxManagerService.java | 80 + .../tx/netty/service/NettyControlService.java | 15 + .../netty/service/NettyDistributeService.java | 8 + .../tx/netty/service/NettyService.java | 14 + .../service/TxManagerHttpRequestHelper.java | 30 + .../service/impl/MQTxManagerServiceImpl.java | 144 + .../service/impl/NettyControlServiceImpl.java | 123 + .../impl/NettyDistributeServiceImpl.java | 65 + .../netty/service/impl/NettyServiceImpl.java | 146 + .../tx/netty/utils/IpAddressUtils.java | 20 + tx-lcn/tx-manager/README.md | 14 + tx-lcn/tx-manager/pom.xml | 77 + tx-lcn/tx-manager/src/main/build/package.xml | 37 + .../main/java/com/codingapi/tm/Constants.java | 15 + .../java/com/codingapi/tm/CorsConfig.java | 40 + .../java/com/codingapi/tm/RestConfig.java | 24 + .../com/codingapi/tm/ServletInitializer.java | 18 + .../codingapi/tm/TxManagerApplication.java | 18 + .../tm/api/controller/AdminController.java | 78 + .../api/controller/TxManagerController.java | 56 + .../tm/api/service/ApiAdminService.java | 32 + .../tm/api/service/ApiModelService.java | 15 + .../tm/api/service/ApiTxManagerService.java | 61 + .../api/service/impl/ApiAdminServiceImpl.java | 72 + .../api/service/impl/ApiModelServiceImpl.java | 23 + .../service/impl/ApiTxManagerServiceImpl.java | 68 + .../tm/compensate/dao/CompensateDao.java | 29 + .../dao/impl/CompensateDaoImpl.java | 110 + .../model/TransactionCompensateMsg.java | 138 + .../tm/compensate/model/TxModel.java | 87 + .../compensate/service/CompensateService.java | 35 + .../service/impl/CompensateServiceImpl.java | 336 + .../com/codingapi/tm/config/ConfigReader.java | 106 + .../tm/framework/utils/SocketManager.java | 110 + .../tm/framework/utils/SocketUtils.java | 36 + .../tm/listener/ApplicationStartListener.java | 37 + .../codingapi/tm/listener/ServerListener.java | 37 + .../tm/listener/service/InitService.java | 12 + .../service/impl/InitServiceImpl.java | 37 + .../tm/manager/ModelInfoManager.java | 73 + .../manager/service/LoadBalanceService.java | 18 + .../tm/manager/service/MicroService.java | 16 + .../service/TxManagerSenderService.java | 15 + .../tm/manager/service/TxManagerService.java | 79 + .../service/impl/LoadBalanceServiceImpl.java | 54 + .../service/impl/MicroServiceImpl.java | 130 + .../impl/TxManagerSenderServiceImpl.java | 323 + .../service/impl/TxManagerServiceImpl.java | 213 + .../com/codingapi/tm/model/ChannelSender.java | 72 + .../codingapi/tm/model/LoadBalanceInfo.java | 39 + .../com/codingapi/tm/model/ModelInfo.java | 48 + .../com/codingapi/tm/model/ModelName.java | 28 + .../java/com/codingapi/tm/model/TxServer.java | 69 + .../java/com/codingapi/tm/model/TxState.java | 170 + .../tm/netty/handler/TxCoreServerHandler.java | 123 + .../com/codingapi/tm/netty/model/TxGroup.java | 183 + .../com/codingapi/tm/netty/model/TxInfo.java | 174 + .../tm/netty/service/IActionService.java | 13 + .../tm/netty/service/NettyServerService.java | 13 + .../tm/netty/service/NettyService.java | 11 + .../service/impl/ActionATGServiceImpl.java | 39 + .../service/impl/ActionCGServiceImpl.java | 34 + .../service/impl/ActionCKGServiceImpl.java | 30 + .../service/impl/ActionCServiceImpl.java | 14 + .../service/impl/ActionCTGServiceImpl.java | 27 + .../service/impl/ActionGLBServiceImpl.java | 37 + .../service/impl/ActionHServiceImpl.java | 25 + .../service/impl/ActionPLBServiceImpl.java | 38 + .../service/impl/ActionRGServiceImpl.java | 28 + .../service/impl/ActionTServiceImpl.java | 14 + .../service/impl/ActionUMIServiceImpl.java | 39 + .../service/impl/BaseSignalTaskService.java | 28 + .../service/impl/NettyServerServiceImpl.java | 103 + .../netty/service/impl/NettyServiceImpl.java | 22 + .../tm/redis/JedisClusterConfig.java | 89 + .../com/codingapi/tm/redis/RedisConfig.java | 44 + .../codingapi/tm/redis/RedisProperties.java | 35 + .../tm/redis/service/RedisServerService.java | 34 + .../service/impl/RedisServerServiceImpl.java | 115 + .../src/main/resources/application.properties | 95 + .../tx-manager/src/main/resources/banner.txt | 13 + .../src/main/resources/static/index.html | 138 + .../src/main/resources/static/log.html | 192 + .../src/main/resources/static/model.html | 65 + .../static/bootstrap/css/bootstrap-theme.css | 650 ++ .../bootstrap/css/bootstrap-theme.css.map | 20 + .../bootstrap/css/bootstrap-theme.min.css | 6 + .../bootstrap/css/bootstrap-theme.min.css.map | 17 + .../static/static/bootstrap/css/bootstrap.css | 8198 +++++++++++++++++ .../static/bootstrap/css/bootstrap.css.map | 142 + .../static/bootstrap/css/bootstrap.min.css | 6 + .../bootstrap/css/bootstrap.min.css.map | 143 + .../fonts/glyphicons-halflings-regular.eot | Bin 0 -> 20127 bytes .../fonts/glyphicons-halflings-regular.svg | 543 ++ .../fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 45404 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 0 -> 23424 bytes .../fonts/glyphicons-halflings-regular.woff2 | Bin 0 -> 18028 bytes .../static/static/bootstrap/js/bootstrap.js | 2395 +++++ .../static/bootstrap/js/bootstrap.min.js | 7 + .../static/static/bootstrap/js/npm.js | 13 + .../resources/static/static/common/common.js | 62 + .../resources/static/static/common/http.js | 82 + .../resources/static/static/common/jbase64.js | 221 + .../resources/static/static/images/logo.png | Bin 0 -> 2491 bytes .../src/main/resources/static/static/index.js | 29 + .../static/static/jquery/jquery-2.2.3.min.js | 4 + .../static/jquery/jquery.blockUI-css.js | 19 + .../static/jquery/jquery.blockUI.min.js | 6 + .../static/static/jquery/jquery.form.js | 1294 +++ .../static/static/jquery/jquery.min.js | 5 + .../src/main/resources/static/static/log.js | 292 + .../src/main/resources/static/static/model.js | 25 + tx-lcn/tx-plugins-db/README.md | 5 + tx-lcn/tx-plugins-db/pom.xml | 44 + .../relational/AbstractTransactionThread.java | 57 + .../datasource/relational/LCNConnection.java | 11 + .../relational/LCNDBConnection.java | 468 + .../relational/LCNStartConnection.java | 460 + .../relational/LCNTransactionDataSource.java | 90 + .../relational/txc/AbstractTxcConnection.java | 415 + .../datasource/relational/txc/ColumnInfo.java | 79 + .../relational/txc/ITxcStatement.java | 21 + .../datasource/relational/txc/IndexInfo.java | 10 + .../relational/txc/TableMetaInfo.java | 123 + .../relational/txc/TableMetaUtils.java | 144 + .../relational/txc/TxcDBConnection.java | 91 + .../relational/txc/TxcPreparedStatement.java | 427 + .../txc/TxcRuntimeContextService.java | 14 + .../relational/txc/TxcSqlExecutor.java | 34 + .../relational/txc/TxcStatement.java | 340 + .../relational/txc/parser/AbstractParser.java | 187 + .../relational/txc/parser/CommitInfo.java | 106 + .../relational/txc/parser/DeleteParser.java | 71 + .../relational/txc/parser/ExecutePaser.java | 126 + .../relational/txc/parser/InsertParser.java | 94 + .../txc/parser/ResultConvertUtils.java | 150 + .../relational/txc/parser/SQLType.java | 12 + .../relational/txc/parser/SqlUtils.java | 18 + .../relational/txc/parser/TxcField.java | 50 + .../relational/txc/parser/TxcLine.java | 46 + .../txc/parser/TxcRuntimeContext.java | 73 + .../relational/txc/parser/TxcTable.java | 74 + .../relational/txc/parser/UpdateParser.java | 126 + .../txc/rollback/AbstractRollback.java | 43 + .../txc/rollback/DeleteRollback.java | 91 + .../relational/txc/rollback/DiffUtils.java | 108 + .../txc/rollback/InsertRollback.java | 111 + .../txc/rollback/TxcRollbackDataSource.java | 34 + .../txc/rollback/TxcRollbackService.java | 18 + .../txc/rollback/TxcRollbackServiceImpl.java | 56 + .../txc/rollback/UpdateRollback.java | 111 + 1225 files changed, 84516 insertions(+) create mode 100644 .gitignore create mode 100644 blockchain-common/blockchain-common-base/pom.xml create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/constant/BaseConstant.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/constant/PushConstants.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/constant/TokenTypeEnums.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/BaseDTO.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/GasDTO.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/MarketDTO.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/PageDTO.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/ResultDTO.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/SessionUserDTO.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/TokenDTO.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/WalletChangeDTO.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/WalletOrderDTO.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/entity/BaseModel.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/enums/BaseResultEnums.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/enums/PushEnums.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/exception/BaseException.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/exception/RPCException.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/DateTimeUtils.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/ExceptionPreconditionUtils.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/FileUploadHelper.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/HttpRequestUtil.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/HttpUtilManager.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/JsonUtils.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/MD5Utils.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/RSACoderUtils.java create mode 100644 blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/SSOHelper.java create mode 100644 blockchain-common/blockchain-common-pom/pom.xml create mode 100644 blockchain-common/blockchain-common-tx/pom.xml create mode 100644 blockchain-common/blockchain-common-tx/src/main/resources/README.md create mode 100644 blockchain-common/pom.xml create mode 100644 blockchain-server/blockchain-server-base/pom.xml create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/BaseConf.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/annotation/BypassedFeign.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/aop/FeignAop.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/aop/MethodAop.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/DateConvertConfig.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/FeginConf.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/GlobalExceptionHandle.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/InnerInterceptorConf.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/RedisConf.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/RestTemplateConf.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/SpringCloudConf.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/SwaggerConf.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/TkMybatisConf.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/XssFilterConfig.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/controller/BaseController.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/filter/XssFilter.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/filter/XssHttpServletRequestWrapper.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/interceptor/InnerInterceptor.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/interceptor/LoginInterceptor.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/interceptor/StringToDateConverter.java create mode 100644 blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/redis/IpInnerCache.java create mode 100644 blockchain-server/blockchain-server-base/src/main/resources/README.md create mode 100644 blockchain-server/blockchain-server-base/src/main/resources/i18n/MessgesBundle_default.properties create mode 100644 blockchain-server/blockchain-server-btc/pom.xml create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/BtcApplication.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/config/InterceptorConf.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcAddressConstans.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcApplicationConstans.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcBlockNumberConstans.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcConfigConstants.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcConstans.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcTransferConstans.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/UsdtConstans.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/enums/BtcEnums.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/exception/BtcException.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/BtcAddressSetRedisUtils.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/BtcBlockRedisUtils.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/BtcRASUtils.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/CheckEthFeginResult.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/BtcTokenController.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/BtcWalletController.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/BtcWalletTransferController.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/ConfigWalletParamController.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/BtcTokenApi.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/BtcWalletApi.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/BtcWalletTransferApi.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/ConfigWalletParamApi.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcApplicationDTO.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcBlockNumberDTO.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcTokenDTO.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcTransferAuditingDTO.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletDTO.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletKeyDTO.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletOutDTO.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletTransferDTO.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/ConfigWalletParamDTO.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcApplication.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcBlockNumber.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcToken.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcTransferAuditing.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWallet.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWalletKey.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWalletOut.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWalletTransfer.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/ConfigWalletParam.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/feign/CoinFegin.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/feign/EthServerFegin.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/feign/UserServerFegin.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/BtcWalletInner.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/BtcWalletTransferInner.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/api/BtcWalletInnerApi.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/api/BtcWalletTransferInnerApi.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcApplicationMapper.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcBlockNumberMapper.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcTokenMapper.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcTransferAuditingMapper.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletKeyMapper.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletMapper.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletOutMapper.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletTransferMapper.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/ConfigWalletParamMapper.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/rpc/BtcOmniRpcClient.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/rpc/BtcUtils.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/rpc/UsdtUtils.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/scheduleTask/RechargeBtcAndUsdtBlockTimer.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/scheduleTask/WallertTimerTask.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcApplicationService.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcBlockNumberService.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcTokenService.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcTransferAuditingService.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletKeyService.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletOutService.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletService.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletTransferService.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/ConfigWalletParamService.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcApplicationServiceImpl.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcBlockNumberServiceImpl.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcTokenServiceImpl.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcTransferAuditingServiceImpl.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletKeyServiceImpl.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletOutServiceImpl.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletServiceImpl.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletTransferServiceImpl.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/ConfigWalletParamServiceImpl.java create mode 100644 blockchain-server/blockchain-server-btc/src/main/resources/application.yml create mode 100644 blockchain-server/blockchain-server-btc/src/main/resources/bootstrap.yml create mode 100644 blockchain-server/blockchain-server-btc/src/main/resources/logback/logback-dev.xml create mode 100644 blockchain-server/blockchain-server-btc/src/main/resources/logback/logback-pro.xml create mode 100644 blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcApplicationMapper.xml create mode 100644 blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcBlockNumberMapper.xml create mode 100644 blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcTokenMapper.xml create mode 100644 blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcTransferAuditingMapper.xml create mode 100644 blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletKeyMapper.xml create mode 100644 blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletMapper.xml create mode 100644 blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletOutMapper.xml create mode 100644 blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletTransferMapper.xml create mode 100644 blockchain-server/blockchain-server-btc/src/main/resources/mapper/ConfigWalletParamMapper.xml create mode 100644 blockchain-server/blockchain-server-cct/pom.xml create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/CctApplication.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/enums/CctDataEnums.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/enums/CctEnums.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/exception/CctException.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/interceptor/InterceptorConf.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/interceptor/TradingInterceptor.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/publish/listener/ListenerBean.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/publish/receiver/MessageReceiver.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/redisson/RedissonConfig.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/redisson/RedissonTool.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/redistool/RedisTool.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/util/CCTExceptionPreconditionUtils.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/util/CheckConfigUtil.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/websocket/WebSocketConfig.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/CoinController.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/ConfigController.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/PublishOrderController.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/TradingDetailController.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/TradingRecordController.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/CoinApi.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/ConfigApi.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/PublishOrderApi.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/TradingDetailApi.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/TradingRecordApi.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/coin/TradingOnDTO.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/config/ConfigDTO.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/matchconfig/MatchConfigDTO.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/order/PublishOrderDTO.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/order/PublishOrderParamDTO.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/rpc/currency/CurrencyMarketDTO.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/trading/DetailByOrderIdDTO.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/trading/ListUserDetailDTO.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/trading/ListUserHistoryDTO.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Bill.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Coin.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/CoinLog.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Commission.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Config.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/ConfigLog.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/MatchConfig.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/MatchConfigHandleLog.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/PublishOrder.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/TradingDetail.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/TradingRecord.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/rpc/user/UserRelation.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/BTCFeign.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/CurrencyFeign.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/EOSFeign.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/ETHFeign.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/PushFeign.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/UserFeign.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/inner/CctInner.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/CoinMapper.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/CommissionMapper.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/ConfigMapper.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/MatchConfigHandleLogMapper.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/MatchConfigMapper.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/PublishOrderMapper.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/TradingDetailMapper.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/TradingRecordMapper.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/redis/PublishOrderCache.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/CoinService.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/CommissionService.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/ConfigService.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/MatchConfigService.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/MatchService.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/PublishOrderService.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/TradingDetailService.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/TradingRecordService.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/WalletService.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/CoinServiceImpl.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/CommissionServiceImpl.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/ConfigServiceImpl.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/MatchConfigServiceImpl.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/MatchServiceImpl.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/PublishOrderServiceImpl.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/TradingDetailServiceImpl.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/TradingRecordServiceImpl.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/WalletServiceImpl.java create mode 100644 blockchain-server/blockchain-server-cct/src/main/resources/application.yml create mode 100644 blockchain-server/blockchain-server-cct/src/main/resources/bootstrap.yml create mode 100644 blockchain-server/blockchain-server-cct/src/main/resources/logback/logback-dev.xml create mode 100644 blockchain-server/blockchain-server-cct/src/main/resources/logback/logback-pro.xml create mode 100644 blockchain-server/blockchain-server-cct/src/main/resources/mapper/CoinMapper.xml create mode 100644 blockchain-server/blockchain-server-cct/src/main/resources/mapper/CommissionMapper.xml create mode 100644 blockchain-server/blockchain-server-cct/src/main/resources/mapper/ConfigMapper.xml create mode 100644 blockchain-server/blockchain-server-cct/src/main/resources/mapper/MatchConfigHandleLogMapper.xml create mode 100644 blockchain-server/blockchain-server-cct/src/main/resources/mapper/MatchConfigMapper.xml create mode 100644 blockchain-server/blockchain-server-cct/src/main/resources/mapper/PublishOrderMapper.xml create mode 100644 blockchain-server/blockchain-server-cct/src/main/resources/mapper/TradingDetailMapper.xml create mode 100644 blockchain-server/blockchain-server-cct/src/main/resources/mapper/TradingRecordMapper.xml create mode 100644 blockchain-server/blockchain-server-currency/pom.xml create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/CurrencyApplication.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/Scheduling/CurrencyMarketScheduling.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/Scheduling/HuobiMarketScheduling.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/conf/InterceptorConf.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/BaseCoinEnums.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/BaseCurrencyEnums.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/CommonConstants.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/DatatablesEnums.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/MarketKEnums.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/MarketKTypeEnums.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/RatesEnums.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/TradingTypeEnums.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/enums/CurrencyMarketResultEnums.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/exception/CurrencyMarketExeption.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/CurrencyController.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/CurrencyMarketController.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/api/CurrencyApi.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/api/CurrencyMarketApi.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyDTO.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyMarketDTO.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyMarketHistoryDTO.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyMarketKDTO.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyPairDTO.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/inner/CurrencyMarketInnerController.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/inner/api/CurrencyMarketInnerApi.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/mapper/CurrencyMapper.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/mapper/CurrencyMarketMapper.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/mapper/CurrencyPairMapper.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/model/Currency.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/model/CurrencyMarket.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/model/CurrencyPair.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/HistoryCache.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/MarketCache.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/MarketKCache.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/MarketLegalCache.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/RedissonConfig.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/RedissonTool.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/CurrencyMarketService.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/CurrencyPairService.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/CurrencyService.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/impl/CurrencyMarketServiceImpl.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/impl/CurrencyPairServiceImpl.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/impl/CurrencyServiceImpl.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/websocket/WebSocketConfig.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/websocket/WebSocketSendMsgUtils.java create mode 100644 blockchain-server/blockchain-server-currency/src/main/resources/application.yml create mode 100644 blockchain-server/blockchain-server-currency/src/main/resources/bootstrap.yml create mode 100644 blockchain-server/blockchain-server-currency/src/main/resources/logback/logback-dev.xml create mode 100644 blockchain-server/blockchain-server-currency/src/main/resources/logback/logback-pro.xml create mode 100644 blockchain-server/blockchain-server-currency/src/main/resources/mapper/CurrencyMapper.xml create mode 100644 blockchain-server/blockchain-server-currency/src/main/resources/mapper/CurrencyMarketMapper.xml create mode 100644 blockchain-server/blockchain-server-currency/src/main/resources/mapper/CurrencyPairMapper.xml create mode 100644 blockchain-server/blockchain-server-databot/pom.xml create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/BotApplication.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/constant/CommonConstant.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/enums/DataBotEnums.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/exception/DataBotExeption.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/redisson/RedissonConfig.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/redisson/RedissonTool.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/dto/rpc/CurrencyMarketDTO.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/dto/rpc/PublishOrderDTO.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/entity/CurrencyConfig.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/entity/MatchConfig.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/feign/CctFeign.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/feign/CurrencyFeign.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/mapper/CurrencyConfigMapper.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/mapper/MatchConfigMapper.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/redis/CurrencyConfigCache.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/redis/MatchConfigCache.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/redis/PublishOrderCache.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/schedule/KMarketScheduling.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/schedule/MatchScheduling.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/CurrencyConfigService.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/MatchConfigService.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/MatchService.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/impl/CurrencyConfigServiceImpl.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/impl/MatchConfigServiceImpl.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/impl/MatchServiceImpl.java create mode 100644 blockchain-server/blockchain-server-databot/src/main/resources/application.yml create mode 100644 blockchain-server/blockchain-server-databot/src/main/resources/bootstrap.yml create mode 100644 blockchain-server/blockchain-server-databot/src/main/resources/logback/logback-dev.xml create mode 100644 blockchain-server/blockchain-server-databot/src/main/resources/logback/logback-pro.xml create mode 100644 blockchain-server/blockchain-server-databot/src/main/resources/mapper/CurrencyConfigMapper.xml create mode 100644 blockchain-server/blockchain-server-databot/src/main/resources/mapper/MatchConfigMapper.xml create mode 100644 blockchain-server/blockchain-server-eos/pom.xml create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/EosApplication.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/config/InterceptorConf.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/constant/EosConstant.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/enums/EosWalletEnums.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/exception/EosWalletException.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/util/EosUtil.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/util/RedisUtil.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/ConfigWalletParamController.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosTokenController.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosWalletController.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosWalletInController.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosWalletTransferController.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/ConfigWalletParamApi.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosTokenApi.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosWalletApi.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosWalletInApi.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosWalletTransferApi.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/BlockNumberDTO.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/BlockchainNumDTO.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/TokenDTO.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/WalletDTO.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/WalletInDTO.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/WalletTransferDTO.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/Application.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/BlockNumber.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/ConfigWalletParam.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/Token.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/TransferAuditing.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/Wallet.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/WalletIn.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/WalletOut.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/WalletTransfer.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/Ecc.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/EosTest.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/GetTransactionParamentDto.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/JsonUtils.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/OfflineSign.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/OfflineTest.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/Rpc.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/Test.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/ApiError.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/ApiException.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/Error.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/ErrorDetails.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/service/RpcService.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/utils/Generator.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/BaseVo.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/Block.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/ChainInfo.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/SignParam.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/TableRows.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/TableRowsReq.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/Account.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/CpuLimit.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/Key.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/NetLimit.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/Permission.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/RequiredAuth.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Act.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/ActionTraces.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Data.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Processed.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Receipt.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Transaction.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/Tx.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxAction.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxActionAuth.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxExtenstions.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxRequest.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxSign.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Curve.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/EccTool.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Ecdsa.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/FieldElement.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Point.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Secp256k.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/Action.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/DataParam.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/DataType.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/Ese.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Base58.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/ByteBuffer.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/ByteUtils.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/EException.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/GeneralDigest.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Hex.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/ObjectUtils.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Ripemd160.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Sha.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/feign/UserServerFegin.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/feign/WalletTransferFegin.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/inner/EosWalletInnerController.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/inner/api/EosWalletApi.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/ApplicationMapper.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/BlockNumberMapper.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/ConfigWalletParamMapper.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/EosWalletInMapper.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/TokenMapper.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/WalletInMapper.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/WalletMapper.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/WalletTransferMapper.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/scheduleTask/TransferDisposeTimerTask.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/scheduleTask/WallertTimerTask.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/ConfigWalletParamService.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosApplicationService.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosBlockNumberService.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosTokenService.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletInService.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletService.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletTransferService.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletUtilService.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/ConfigWalletParamServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosApplicationServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosBlockNumberServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosTokenServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletInServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletTransferServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletUtilServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eos/src/main/resources/application.yml create mode 100644 blockchain-server/blockchain-server-eos/src/main/resources/bootstrap.yml create mode 100644 blockchain-server/blockchain-server-eos/src/main/resources/i18n/MessgesBundle_zh_CN.properties create mode 100644 blockchain-server/blockchain-server-eos/src/main/resources/logback/logback-dev.xml create mode 100644 blockchain-server/blockchain-server-eos/src/main/resources/logback/logback-pro.xml create mode 100644 blockchain-server/blockchain-server-eos/src/main/resources/mapper/ApplicationMapper.xml create mode 100644 blockchain-server/blockchain-server-eos/src/main/resources/mapper/BlockNumberMapper.xml create mode 100644 blockchain-server/blockchain-server-eos/src/main/resources/mapper/ConfigWalletParamMapper.xml create mode 100644 blockchain-server/blockchain-server-eos/src/main/resources/mapper/TokenMapper.xml create mode 100644 blockchain-server/blockchain-server-eos/src/main/resources/mapper/WalletInMapper.xml create mode 100644 blockchain-server/blockchain-server-eos/src/main/resources/mapper/WalletMapper.xml create mode 100644 blockchain-server/blockchain-server-eos/src/main/resources/mapper/WalletTransferMapper.xml create mode 100644 blockchain-server/blockchain-server-eth/pom.xml create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/EthApplication.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/config/EthConfig.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/config/InterceptorConf.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/constants/EthConfigConstants.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/constants/EthWalletConstants.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/enums/EthWalletEnums.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/exception/EthWalletException.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/BaseHelperUtil.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/DataCheckUtil.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/RedisBlockUtil.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/RedisPrivateUtil.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/RedisWalletAddrUtil.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/ConfigWalletParamController.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/EthTokenController.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/EthWalletController.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/EthWalletTransferController.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/ConfigWalletParamApi.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/EthTokenApi.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/EthWalletApi.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/EthWalletTransferApi.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/EthGasDTO.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/EthWalletDTO.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/EthWalletTransferDTO.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/Web3jTransferDTO.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/Web3jWalletDTO.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/ConfigWalletParam.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthApplication.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthBlockNumber.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthGasWallet.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthToken.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthTransferAuditing.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWallet.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWalletKey.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWalletOut.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWalletTransfer.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/feign/UserFeign.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/feign/api/UserInnerApi.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/EthWalletInner.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/EthWalletTransferInner.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/api/EthWalletApi.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/api/EthWalletTransferApi.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/ConfigWalletParamMapper.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthApplicationMapper.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthBlockNumberMapper.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthGasWalletMapper.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthTokenMapper.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthTransferAuditingMapper.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletKeyMapper.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletMapper.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletOutMapper.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletTransferMapper.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/scheduleTask/EthTxInTimerTask.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/scheduleTask/EthWallertTimerTask.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IConfigWalletParamService.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthApplicationService.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthBlockNumberService.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthGasWalletService.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthTokenService.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthWalletKeyService.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthWalletService.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthWalletTransferService.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/ConfigWalletParamServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthApplicationServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthBlockNumberServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthGasWalletServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthTokenServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthWalletKeyServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthWalletServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthWalletTransferServiceImpl.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/IBaseWeb3j.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/IWalletWeb3j.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/impl/BaseWeb3jImpl.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/impl/WalletWeb3jImpl.java create mode 100644 blockchain-server/blockchain-server-eth/src/main/resources/application.yml create mode 100644 blockchain-server/blockchain-server-eth/src/main/resources/bootstrap.yml create mode 100644 blockchain-server/blockchain-server-eth/src/main/resources/logback/logback-dev.xml create mode 100644 blockchain-server/blockchain-server-eth/src/main/resources/logback/logback-pro.xml create mode 100644 blockchain-server/blockchain-server-eth/src/main/resources/mapper/ConfigWalletParamMapper.xml create mode 100644 blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthApplicationMapper.xml create mode 100644 blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthBlockNumberMapper.xml create mode 100644 blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthGasWalletMapper.xml create mode 100644 blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthTokenMapper.xml create mode 100644 blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthTransferAuditingMapper.xml create mode 100644 blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletKeyMapper.xml create mode 100644 blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletMapper.xml create mode 100644 blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletOutMapper.xml create mode 100644 blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletTransferMapper.xml create mode 100644 blockchain-server/blockchain-server-imJg/pom.xml create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/ImJgApplication.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/enums/MessageLogEnums.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/enums/NodeCueEnums.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/util/HttpUtils.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/util/JiGuangIMUtils.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/controller/ImjgController.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/controller/ImjgUserController.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/controller/api/ImjgApi.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgError.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgInitDTO.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgMessageBodyDTO.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgMessageDTO.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgMessageLogDTO.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgResourceDTO.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgResponse.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgUserDTO.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgUserInfoDTO.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgUserStatDTO.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/MessageDTO.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/MessageLogDTO.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/UserDTO.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/entity/ImjgMessage.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/entity/ImjgMessageLog.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/entity/ImjgUser.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/feign/UserFeign.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/inner/ImjgInnerController.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/inner/api/ImjgInnerApi.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/mapper/ImjgMessageLogMapper.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/mapper/ImjgMessageMapper.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/mapper/ImjgUserMapper.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/ImjgMessageLogService.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/ImjgUserService.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/impl/ImjgMessageLogServiceImpl.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/impl/ImjgUserServiceImpl.java create mode 100644 blockchain-server/blockchain-server-imJg/src/main/resources/application.yml create mode 100644 blockchain-server/blockchain-server-imJg/src/main/resources/bootstrap.yml create mode 100644 blockchain-server/blockchain-server-imJg/src/main/resources/logback/logback-dev.xml create mode 100644 blockchain-server/blockchain-server-imJg/src/main/resources/logback/logback-pro.xml create mode 100644 blockchain-server/blockchain-server-imJg/src/main/resources/mapper/ImjgMessageLogMapper.xml create mode 100644 blockchain-server/blockchain-server-imJg/src/main/resources/mapper/ImjgMessageMapper.xml create mode 100644 blockchain-server/blockchain-server-imJg/src/main/resources/mapper/ImjgUserMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/pom.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/OtcApplication.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/AdConstants.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/AppealConstants.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/BillConstants.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/CommonConstans.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/ConfigConstants.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/MarketApplyConstants.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/MarketUserConstants.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/OrderConstants.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/UserHandleConstants.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/UserPayConstants.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/enums/JgMsgEnums.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/enums/OtcEnums.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/exception/OtcException.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/interceptor/InterceptorConf.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/interceptor/TradingInterceptor.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/listener/RedisListenerBean.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/listener/RedisReceiver.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/util/CheckDecimalUtil.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/util/ImUtil.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/AdController.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/AppealController.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/CoinController.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/ConfigController.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/MarketApplyController.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/MarketUserController.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/OrderController.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/UserPayInfoController.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/AdApi.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/AppealApi.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/CoinApi.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/ConfigApi.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/MarketApplyApi.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/MarketUserApi.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/OrderApi.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/UserPayApi.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/ad/ListAdDTO.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/ad/ListUserAdDTO.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/ad/PublishAdParamDTO.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/appeal/AppealHandleLogDTO.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/coin/CoinDTO.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/coin/CoinServiceChargeDTO.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/config/ConfigDTO.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/jgim/ImjgMessage.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/jgim/JgMassageParam.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/jgim/JgMessageBodyDTO.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/order/OrderDTO.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/user/UserBaseDTO.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Ad.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Appeal.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/AppealDetail.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/AppealHandleLog.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/AppealImg.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Bill.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Coin.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Config.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/DealStats.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketApply.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketApplyHandleLog.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketFreeze.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketUser.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketUserHandleLog.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Order.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/UserHandleLog.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/UserPayInfo.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/BTCFeign.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/EOSFeign.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/ETHFeign.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/IMJGFeign.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/PushFeign.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/UserFeign.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AdMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealDetailMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealHandleLogMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealImgMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/BillMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/CoinMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/ConfigMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/DealStatsMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketApplyHandleLogMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketApplyMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketFreezeMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketUserHandleLogMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketUserMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/OrderMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/UserHandleLogMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/UserPayInfoMapper.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/redis/OrderCache.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/schedule/AutoCancelOrderScheduling.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AdService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealDetailService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealHandleLogService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealImgService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/BillService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/CoinService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/ConfigService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/DealStatsService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/MarketApplyService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/MarketFreezeService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/MarketUserService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/OrderService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/UserHandleLogService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/UserPayInfoService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/WalletService.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AdServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealDetailServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealHandleLogServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealImgServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/BillServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/CoinServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/ConfigServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/DealStatsServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/MarketApplyServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/MarketFreezeServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/MarketUserServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/OrderServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/UserHandleLogServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/UserPayInfoServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/WalletServiceImpl.java create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/application.yml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/bootstrap.yml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/logback/logback-dev.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/logback/logback-pro.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/AdMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealDetailMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealHandleLogMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealImgMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/BillMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/CoinMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/ConfigMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/DealStatsMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketApplyHandleLogMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketApplyMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketFreezeMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketUserHandleLogMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketUserMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/OrderMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/UserHandleLogMapper.xml create mode 100644 blockchain-server/blockchain-server-otc/src/main/resources/mapper/UserPayInfoMapper.xml create mode 100644 blockchain-server/blockchain-server-sysconf/pom.xml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/SysConfigApplication.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/config/InterceptorConf.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/AgreementConstant.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/ContactUsConstant.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/ImageConstant.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/NewsConstant.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/NoticeConstant.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/UserConstant.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/VersionConstant.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/enums/SysConfigEnums.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/exception/SysConfigException.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/util/CheckUtils.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/AboutUsController.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/AdSliderController.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/AgreementController.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/ContactUsController.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/HelpCenterController.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/ImageController.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/NewsController.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/NoticeController.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/ProjectCenterController.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/VersionController.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/WgtVersionController.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/AboutUsApi.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/AdSliderApi.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/AgreementApi.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/ContactUsApi.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/HelpCenterApi.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/NewsApi.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/ProjectCenterApi.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/SysconfImageApi.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/SysconfNoticeApi.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/VersionApi.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/WgtVersionApi.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/AboutUsQueryConditionDTO.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/AdSliderDto.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/ContactUsQueryConditionDTO.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/HelpCenterDTO.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/NewsDTO.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/NewsQueryConditionDTO.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/ProjectCenterDto.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/AboutUs.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/AdSlider.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/Agreement.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ContactUs.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/HelpCenter.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/News.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterClassify.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterInfo.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterReport.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterStar.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/SmsCount.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/SystemImage.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/SystemNotice.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/Version.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/WgtVersion.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/AboutUsMapper.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/AdSliderMapper.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/AgreementMapper.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/ContactUsMapper.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/HelpCenterMapper.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/NewsMapper.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/ProjectCenterMapper.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/ProjectCenterStarMapper.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/SystemImageMapper.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/SystemNoticeMapper.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/VersionMapper.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/WgtVersionMapper.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/AboutUsService.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/AdSliderService.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/AgreementService.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/ContactUsService.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/HelpCenterService.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/NewsService.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/ProjectCenterService.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/ProjectCenterStarService.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/SystemImageService.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/SystemNoticeService.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/VersionService.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/WgtVersionService.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/AboutUsServiceImpl.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/AdSliderServiceImpl.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/AgreementServiceImpl.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/ContactUsServiceImpl.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/HelpCenterServiceImpl.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/NewsServiceImpl.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/ProjectCenterServiceImpl.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/ProjectCenterStarServiceImpl.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/SystemImageServiceImpl.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/SystemNoticeServiceImpl.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/VersionServiceImpl.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/WgtVersionServiceImpl.java create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/application.yml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/bootstrap.yml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/i18n/MessgesBundle_zh_CN.properties create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/logback/logback-dev.xml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/logback/logback-pro.xml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/AboutUsMapper.xml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/AdSliderMapper.xml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/ContactUsMapper.xml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/HelpCenterMapper.xml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/NewsMapper.xml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/ProjectCenterMapper.xml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/ProjectCenterStarMapper.xml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/SystemImageMapper.xml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/SystemNoticeMapper.xml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/UserAgreementMapper.xml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/VersionMapper.xml create mode 100644 blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/WgtVersionMapper.xml create mode 100644 blockchain-server/blockchain-server-user/pom.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/UserApplication.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/config/InterceptorConf.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/InternationalConstant.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/RedisConstant.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/StringFormatConstant.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/UserFileUploadConstant.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/AuthenticationApplyConstant.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/ConfigConstant.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/GlobalConstant.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/SmsCountConstant.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserAuthenticationConstant.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserInfoConstant.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserListConstant.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserLoginLogConstant.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserMainConstant.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserOptConstant.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/enums/SmsCountEnum.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/enums/UserEnums.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/exceprion/UserException.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/CheckUtils.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/CommonUtils.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/EMailTransmitHelper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/EmailCodeUtils.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/FileUploadHelper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/GoogleAuthenticatorUtils.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/SendSmsgCode.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/SmsCodeUtils.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/push/PushUtils.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/push/TemplateUtils.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/LoginController.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/PushUserController.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/UserController.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/UserLoginLogController.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/LoginApi.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/PushUserApi.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/UserApi.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/UserAuthenticationApi.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/UserLoginLogApi.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/dto/PushInfoDTO.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/dto/UserBaseDTO.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/dto/UserLoginLogDto.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/AuthenticationApply.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/AuthenticationApplyLog.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/Config.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/HighAuthenticationApply.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/PushUser.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/SmsCount.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserAuthentication.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserInfo.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserList.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserLogin.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserLoginLog.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserMain.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserOptLog.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserRelation.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/feign/BtcFeign.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/feign/EosFeign.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/feign/EthFeign.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/PushInner.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/UserInner.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/api/PushInnerApi.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/api/UserInnerApi.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/AuthenticationApplyLogMapper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/AuthenticationApplyMapper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/ConfigMapper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/HighAuthenticationApplyMapper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/PushUserMapper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/SmsCountMapper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserAuthenticationMapper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserInfoMapper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserListMapper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserLoginLogMapper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserLoginMapper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserMainMapper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserOptLogMapper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserRelationMapper.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/AuthenticationApplyLogService.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/AuthenticationApplyService.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/ConfigService.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/PushService.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/PushUserService.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/SmsCountService.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserInfoService.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserListService.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserLoginLogService.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserLoginService.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserMainService.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserOptLogService.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserRelationService.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserService.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/AuthenticationApplyLogServiceImpl.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/AuthenticationApplyServiceImpl.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/ConfigServiceImpl.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/PushServiceImpl.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/PushUserServiceImpl.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/SmsCountServiceImpl.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserInfoServiceImpl.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserListServiceImpl.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserLoginLoginServiceImpl.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserLoginServiceImpl.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserMainServiceImpl.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserOptLogServiceImpl.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserRelationServiceImpl.java create mode 100644 blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserServiceImpl.java create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/application.yml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/bootstrap.yml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/logback/logback-dev.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/logback/logback-pro.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/mapper/AuthenticationApplyLogMapper.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/mapper/AuthenticationApplyMapper.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/mapper/ConfigMapper.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/mapper/HighAuthenticationApplyMapper.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/mapper/PushUserMapper.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/mapper/SmsCountMapper.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/mapper/UserAuthenticationMapper.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/mapper/UserInfoMapper.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/mapper/UserListMapper.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/mapper/UserLoginMapper.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/mapper/UserLoinLogMapper.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/mapper/UserMainMapper.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/mapper/UserOptLogMapper.xml create mode 100644 blockchain-server/blockchain-server-user/src/main/resources/mapper/UserRelationMapper.xml create mode 100644 blockchain-server/blockchain-server-user/src/test/java/MD5Test.java create mode 100644 blockchain-server/pom.xml create mode 100644 pom.xml create mode 100644 spring-cloud/pom.xml create mode 100644 spring-cloud/spring-cloud-config/pom.xml create mode 100644 spring-cloud/spring-cloud-config/src/main/java/com/blockchain/spring/cloud/config/ConfigApplication.java create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/application.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/authconfig-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/btcconf-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/dbconf-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/emailconf-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/eosconf-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/ethconf-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/fileconf-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/imjgconf-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/ipconf-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/marketconf-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/pushconf-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/redisconf-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/smsconf-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/springcloudconf-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/springconf-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/txconf-dev.yml create mode 100644 spring-cloud/spring-cloud-config/src/main/resources/properties/xssconf-dev.yml create mode 100644 spring-cloud/spring-cloud-eureka/pom.xml create mode 100644 spring-cloud/spring-cloud-eureka/src/main/java/com/blockchain/spring/cloud/eureka/EurekaApplication.java create mode 100644 spring-cloud/spring-cloud-eureka/src/main/resources/application.yml create mode 100644 spring-cloud/spring-cloud-hystrix-dashboard/pom.xml create mode 100644 spring-cloud/spring-cloud-hystrix-dashboard/src/main/java/com/blockchain/springcloud/dashboard/DashboardApplication.java create mode 100644 spring-cloud/spring-cloud-hystrix-dashboard/src/main/resources/application.yml create mode 100644 tx-lcn/LICENSE create mode 100644 tx-lcn/README.md create mode 100644 tx-lcn/pom.xml create mode 100644 tx-lcn/transaction-springcloud/README.md create mode 100644 tx-lcn/transaction-springcloud/pom.xml create mode 100644 tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnLoadBalancerRule.java create mode 100644 tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnNoOpLoadBalancerProxy.java create mode 100644 tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnRibbonConfiguration.java create mode 100644 tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnZoneAwareLoadBalancerProxy.java create mode 100644 tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/RequestInterceptorConfiguration.java create mode 100644 tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/TransactionConfiguration.java create mode 100644 tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/feign/TransactionRestTemplateInterceptor.java create mode 100644 tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/http/TransactionHttpRequestInterceptor.java create mode 100644 tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TransactionAspect.java create mode 100644 tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TxManagerInterceptor.java create mode 100644 tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/listener/ServerListener.java create mode 100644 tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/service/impl/ModelNameServiceImpl.java create mode 100644 tx-lcn/transaction-springcloud/src/main/resources/META-INF/spring.factories create mode 100644 tx-lcn/transaction-springcloud/src/main/resources/banner.txt create mode 100644 tx-lcn/tx-client/README.md create mode 100644 tx-lcn/tx-client/pom.xml create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/Constants.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/annotation/ITxTransaction.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/annotation/TxTransaction.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/annotation/TxTransactionMode.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxCompensateLocal.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionInfo.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionLocal.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/AspectBeforeService.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/TransactionServer.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/TransactionServerFactoryService.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/AspectBeforeServiceImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TransactionServerFactoryServiceImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxDefaultTransactionServerImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningNoTransactionServerImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningTransactionServerImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxStartTransactionServerImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/model/CompensateInfo.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/package-info.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/service/CompensateService.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/service/impl/CompensateServiceImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/config/ConfigReader.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/LCNTransactionAspectSupport.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/IActionService.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/TransactionControlService.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/impl/ActionCServiceImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/impl/ActionTServiceImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/impl/TransactionControlServiceImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/AbstractResourceProxy.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ICallClose.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ILCNConnection.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ILCNResource.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ILCNTransactionControl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/aspect/DataSourceAspect.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/package-info.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/service/DataSourceService.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/service/impl/DataSourceServiceImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TaskGroup.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TaskGroupManager.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TaskState.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TxTask.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/thread/HookRunnable.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/thread/NamedThreadFactory.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/MethodUtils.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/SerializerUtils.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/SocketManager.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/SocketUtils.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/serializer/ISerializer.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/serializer/ProtostuffSerializer.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/serializer/SchemaCache.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/listener/service/InitService.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/listener/service/ModelNameService.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/listener/service/impl/InitServiceImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/Request.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/TransactionInvocation.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/TxGroup.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/TxServer.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/handler/TransactionHandler.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/MQTxManagerService.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/NettyControlService.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/NettyDistributeService.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/NettyService.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/TxManagerHttpRequestHelper.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/MQTxManagerServiceImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyControlServiceImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyDistributeServiceImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyServiceImpl.java create mode 100644 tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/utils/IpAddressUtils.java create mode 100644 tx-lcn/tx-manager/README.md create mode 100644 tx-lcn/tx-manager/pom.xml create mode 100644 tx-lcn/tx-manager/src/main/build/package.xml create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/Constants.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/CorsConfig.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/RestConfig.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/ServletInitializer.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/TxManagerApplication.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/controller/AdminController.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/controller/TxManagerController.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/ApiAdminService.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/ApiModelService.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/ApiTxManagerService.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/impl/ApiAdminServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/impl/ApiModelServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/impl/ApiTxManagerServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/dao/CompensateDao.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/dao/impl/CompensateDaoImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/model/TransactionCompensateMsg.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/model/TxModel.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/service/CompensateService.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/service/impl/CompensateServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/config/ConfigReader.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/framework/utils/SocketManager.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/framework/utils/SocketUtils.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/ApplicationStartListener.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/ServerListener.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/service/InitService.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/service/impl/InitServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/ModelInfoManager.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/LoadBalanceService.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/MicroService.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/TxManagerSenderService.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/TxManagerService.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/LoadBalanceServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/MicroServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerSenderServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/ChannelSender.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/LoadBalanceInfo.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/ModelInfo.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/ModelName.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/TxServer.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/TxState.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/handler/TxCoreServerHandler.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/model/TxGroup.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/model/TxInfo.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/IActionService.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/NettyServerService.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/NettyService.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionATGServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCGServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCKGServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCTGServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionGLBServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionHServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionPLBServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionRGServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionTServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionUMIServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/BaseSignalTaskService.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/NettyServerServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/NettyServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/JedisClusterConfig.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/RedisConfig.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/RedisProperties.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/service/RedisServerService.java create mode 100644 tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/service/impl/RedisServerServiceImpl.java create mode 100644 tx-lcn/tx-manager/src/main/resources/application.properties create mode 100644 tx-lcn/tx-manager/src/main/resources/banner.txt create mode 100644 tx-lcn/tx-manager/src/main/resources/static/index.html create mode 100644 tx-lcn/tx-manager/src/main/resources/static/log.html create mode 100644 tx-lcn/tx-manager/src/main/resources/static/model.html create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.css create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.css.map create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.min.css create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.min.css.map create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap.css create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap.css.map create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap.min.css create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap.min.css.map create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/fonts/glyphicons-halflings-regular.eot create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/fonts/glyphicons-halflings-regular.svg create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/fonts/glyphicons-halflings-regular.ttf create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/fonts/glyphicons-halflings-regular.woff create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/js/bootstrap.js create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/js/bootstrap.min.js create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/js/npm.js create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/common/common.js create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/common/http.js create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/common/jbase64.js create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/images/logo.png create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/index.js create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/jquery/jquery-2.2.3.min.js create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/jquery/jquery.blockUI-css.js create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/jquery/jquery.blockUI.min.js create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/jquery/jquery.form.js create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/jquery/jquery.min.js create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/log.js create mode 100644 tx-lcn/tx-manager/src/main/resources/static/static/model.js create mode 100644 tx-lcn/tx-plugins-db/README.md create mode 100644 tx-lcn/tx-plugins-db/pom.xml create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/AbstractTransactionThread.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNConnection.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNDBConnection.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNStartConnection.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/LCNTransactionDataSource.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/AbstractTxcConnection.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/ColumnInfo.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/ITxcStatement.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/IndexInfo.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TableMetaInfo.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TableMetaUtils.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcDBConnection.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcPreparedStatement.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcRuntimeContextService.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcSqlExecutor.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/TxcStatement.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/AbstractParser.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/CommitInfo.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/DeleteParser.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/ExecutePaser.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/InsertParser.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/ResultConvertUtils.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/SQLType.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/SqlUtils.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcField.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcLine.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcRuntimeContext.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/TxcTable.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/parser/UpdateParser.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/AbstractRollback.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/DeleteRollback.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/DiffUtils.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/InsertRollback.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/TxcRollbackDataSource.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/TxcRollbackService.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/TxcRollbackServiceImpl.java create mode 100644 tx-lcn/tx-plugins-db/src/main/java/com/codingapi/tx/datasource/relational/txc/rollback/UpdateRollback.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b66d4d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Created by .ignore support plugin (hsz.mobi) +### Java template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# idea +*.iml +target/ +.idea/ diff --git a/blockchain-common/blockchain-common-base/pom.xml b/blockchain-common/blockchain-common-base/pom.xml new file mode 100644 index 0000000..655cdcd --- /dev/null +++ b/blockchain-common/blockchain-common-base/pom.xml @@ -0,0 +1,21 @@ + + + + blockchain-common + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + blockchain-common-base + + + + com.blockchain + blockchain-common-pom + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/constant/BaseConstant.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/constant/BaseConstant.java new file mode 100644 index 0000000..313e2f6 --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/constant/BaseConstant.java @@ -0,0 +1,18 @@ +package com.blockchain.common.base.constant; + +public class BaseConstant { + public static final String REDIS_TOKEN_KEY = "user:token:";//redis 存储token的key值,用于获取用户信息 + public static final String REDIS_TOKEN_PC_KEY = "user:token:pc";//redis 存储token的key值,用于获取用户信息 + public static final String PARAMS_LOCALE = "locale";//中英文参数 + public static final String SSO_TOKEN_HEADER = "X-Requested-Token";//sso单点登录的header + + public static final String PAGE_DEFAULT_SIZE = "10";//分页默认查询条数 + public static final String PAGE_DEFAULT_INDEX = "1";//分页默认查询起始页 + + public static final String USER_LOCALE_ZH_CN = "zh_CN";//中文 + public static final String USER_LOCALE_EN_US = "en_US";//英文 + public static final String USER_LOCALE_ZH_HK = "zh_HK";//繁体中文 + public static final String USER_LOCALE_DEFAULT = USER_LOCALE_ZH_CN;//默认国际化标识为繁体中文 + + public static final int REQUEST_SUCCESS = 200;// 成功标识 +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/constant/PushConstants.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/constant/PushConstants.java new file mode 100644 index 0000000..a44910f --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/constant/PushConstants.java @@ -0,0 +1,11 @@ +package com.blockchain.common.base.constant; + +/**** + * 推送通知的透传参数Key常量 + */ +public class PushConstants { + + public static final String ORDER_ID = "orderId"; //币币交易和法币交易的订单Id常量 + public static final String AD_ID = "adId"; //法币交易的广告Id常量 + public static final String PUSH_TYPE = "pushType"; //推送触发类型 +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/constant/TokenTypeEnums.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/constant/TokenTypeEnums.java new file mode 100644 index 0000000..a76646c --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/constant/TokenTypeEnums.java @@ -0,0 +1,24 @@ +package com.blockchain.common.base.constant; + +/** + * \*

Desciption:汇率枚举

+ * \* CreateTime: 2019/3/21 20:00 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +public enum TokenTypeEnums { + APP("APP"), + PC("PC") + ; + + private String value; + + TokenTypeEnums(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/BaseDTO.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/BaseDTO.java new file mode 100644 index 0000000..ceec36c --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/BaseDTO.java @@ -0,0 +1,6 @@ +package com.blockchain.common.base.dto; + +import java.io.Serializable; + +public class BaseDTO implements Serializable { +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/GasDTO.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/GasDTO.java new file mode 100644 index 0000000..84ad4db --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/GasDTO.java @@ -0,0 +1,17 @@ +package com.blockchain.common.base.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * GasDTO 手续费公用类 + */ +@Data +public class GasDTO extends BaseDTO { + private BigDecimal gasPrice; // 手续费 + private String gasTokenType; // 币种识别标识 + private String gasTokenSymbol; // 币种名称 + private String gasTokenName; // 币种名称 + private BigDecimal minWdAmount; // 最小提现余额 +} \ No newline at end of file diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/MarketDTO.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/MarketDTO.java new file mode 100644 index 0000000..289f06f --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/MarketDTO.java @@ -0,0 +1,22 @@ +package com.blockchain.common.base.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/*** + * 交易盘口列表DTO + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MarketDTO { + private BigDecimal unitPrice; //单价 + private BigDecimal totalNum; //总数量 + private BigDecimal totalLastNum; //总剩余数量 + private String unitName; //二级货币 + private String coinName; //基本货币 + private String tradingType; //交易方向 +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/PageDTO.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/PageDTO.java new file mode 100644 index 0000000..405b41d --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/PageDTO.java @@ -0,0 +1,54 @@ +package com.blockchain.common.base.dto; + +import lombok.Data; + +import java.util.List; + +@Data +public class PageDTO extends BaseDTO { + /* 总条目数 */ + private Long total; + /* 当前页码 */ + private Integer pageNum; + /* 每页记录数 */ + private Integer pageSize; + /* 第一页页码 */ + private Integer firstPage; + /* 上一页页码 */ + private Integer prePage; + /* 下一页页码 */ + private Integer nextPage; + /* 最后一页页码,最大页数 */ + private Integer lastPage; + /* 结果集 */ + private List rows; + + /** + * 构造函数 + * + * @param total : 总数 + * @param pageNum : 当前页数 + * @param pageSize : 页数大小 + * @param rows : 集合 + */ + public PageDTO(Long total, Integer pageNum, Integer pageSize, List rows) { + this.total = total; + this.pageSize = pageSize; + this.rows = rows; + if (total == null || total == 0) { + this.total = 0l; + this.pageNum = 1; + this.firstPage = 1; + this.prePage = 1; + this.nextPage = 1; + this.lastPage = 1; + } else { + this.pageNum = pageNum; + this.firstPage = 1; + long pageTotal = total / pageSize; + this.lastPage = (int) ((total % pageSize == 0) ? pageTotal : pageTotal + 1); + this.prePage = this.pageNum - 1; + this.nextPage = this.pageNum == lastPage ? 0 : this.pageNum + 1; + } + } +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/ResultDTO.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/ResultDTO.java new file mode 100644 index 0000000..53018b1 --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/ResultDTO.java @@ -0,0 +1,49 @@ +package com.blockchain.common.base.dto; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.enums.BaseResultEnums; +import com.blockchain.common.base.util.HttpRequestUtil; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +/** + * 统一数据返回格式 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ResultDTO extends BaseDTO { + private int code;//返回码 + private String msg;//返回码描述 + private T data;//返回数据 + + public static ResultDTO requstSuccess() { + return requstSuccess(null); + } + + public static ResultDTO requstSuccess(Object data) { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + String userLocale = BaseConstant.USER_LOCALE_DEFAULT; + if (request != null) { + userLocale = HttpRequestUtil.getUserLocale(request); + } + String msg; + switch (userLocale) { + case BaseConstant.USER_LOCALE_EN_US: + msg = BaseResultEnums.SUCCESS.getEnMsg(); + break; + case BaseConstant.USER_LOCALE_ZH_CN: + msg = BaseResultEnums.SUCCESS.getCnmsg(); + break; + default: + msg = BaseResultEnums.SUCCESS.getHkmsg(); + break; + } + return new ResultDTO(BaseResultEnums.SUCCESS.getCode(), msg, data); + } +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/SessionUserDTO.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/SessionUserDTO.java new file mode 100644 index 0000000..6ec6391 --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/SessionUserDTO.java @@ -0,0 +1,15 @@ +package com.blockchain.common.base.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 放到session里的用户 + */ +@Data +public class SessionUserDTO extends BaseDTO{ + private String id;//用户id + private String tel;//手机号 + private Long timestamp;//时间戳 +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/TokenDTO.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/TokenDTO.java new file mode 100644 index 0000000..2bbfacb --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/TokenDTO.java @@ -0,0 +1,18 @@ +package com.blockchain.common.base.dto; + + +import lombok.Data; + +/** + * \*

Desciption:

+ * \* CreateTime: 2018/12/1 14:06 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +@Data +public class TokenDTO { + String tel; + Long timestamp; + String tokenType;//APP、PC +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/WalletChangeDTO.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/WalletChangeDTO.java new file mode 100644 index 0000000..2aa9458 --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/WalletChangeDTO.java @@ -0,0 +1,19 @@ +package com.blockchain.common.base.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 钱包修改余额接口DTO + */ +@Data +public class WalletChangeDTO { + String userId; + String recordId; // 记录标识 + String tokenName; // 币种名称 + BigDecimal freeBalance; // 可用余额 + BigDecimal freezeBalance; // 冻结余额 + BigDecimal gasBalance; // 手续费 + String walletType; // 钱包标识 +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/WalletOrderDTO.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/WalletOrderDTO.java new file mode 100644 index 0000000..620277a --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/dto/WalletOrderDTO.java @@ -0,0 +1,18 @@ +package com.blockchain.common.base.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 钱包修改余额接口DTO + */ +@Data +public class WalletOrderDTO { + String userId; + String recordId; // 记录标识(可以为空) + String tokenName; // 币种名称 + BigDecimal freeBalance; // 可用余额 + BigDecimal freezeBalance; // 冻结余额 + String walletType; // 钱包标识 +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/entity/BaseModel.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/entity/BaseModel.java new file mode 100644 index 0000000..10901fa --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/entity/BaseModel.java @@ -0,0 +1,10 @@ +package com.blockchain.common.base.entity; + +import java.io.Serializable; + +/** + * @author huangxl + * Created on 2018/11/25 + */ +public class BaseModel implements Serializable { +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/enums/BaseResultEnums.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/enums/BaseResultEnums.java new file mode 100644 index 0000000..53761c2 --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/enums/BaseResultEnums.java @@ -0,0 +1,57 @@ +package com.blockchain.common.base.enums; + +public enum BaseResultEnums { + SUCCESS(200, "请求成功", "Request success", "請求成功"), + NO_LOGIN(201, "未登录", "No login", "未登錄"), + LOGIN_REPLACED(202, "您的账号在别处登录!", "Your account is logged in elsewhere!", "您的帳號在別處登錄"), + RSA_ERROR(250, "加密错误", "RSACoder error", "加密錯誤"), + BUSY(300, "服务器繁忙", "Server busy", "伺服器繁忙"), + DEFAULT(500, "系统错误", "System error", "系統錯誤"), + PASSWORD_ERROR(300, "密码错误", "Password error", "密碼錯誤"), + PARAMS_ERROR(403, "参数异常", "Params error", "參數異常"), + NOT_FOUND(404, "NOT FOUND", "Params error", "參數異常"); + + private int code; + private String hkmsg; + private String enMsg; + private String cnmsg; + + BaseResultEnums(int code, String cnmsg, String enMsg, String hkmsg) { + this.code = code; + this.cnmsg = cnmsg; + this.enMsg = enMsg; + this.hkmsg = hkmsg; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getHkmsg() { + return hkmsg; + } + + public void setHkmsg(String hkmsg) { + this.hkmsg = hkmsg; + } + + public String getEnMsg() { + return enMsg; + } + + public void setEnMsg(String enMsg) { + this.enMsg = enMsg; + } + + public String getCnmsg() { + return cnmsg; + } + + public void setCnmsg(String cnmsg) { + this.cnmsg = cnmsg; + } +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/enums/PushEnums.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/enums/PushEnums.java new file mode 100644 index 0000000..10dee50 --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/enums/PushEnums.java @@ -0,0 +1,94 @@ +package com.blockchain.common.base.enums; + +import lombok.Data; + +import java.util.HashMap; +import java.util.Map; + +public enum PushEnums { + OTC_ORDER_DEAL_BUY("OTC_ORDER_DEAL_BUY", "法币交易", "已有用户下单向您购买代币,请尽快处理!", "Fiat", "There are already users under one-way you buy token, please deal with it as soon as possible!"), + OTC_ORDER_DEAL_SELL("OTC_ORDER_DEAL_SELL", "法币交易", "已有用户下单向您出售代币,请尽快处理!", "Fiat", "Existing users under one-way you sell tokens, please deal with as soon as possible!"), + OTC_ORDER_PAY("OTC_ORDER_PAY", "法币交易", "买家已确认付款请尽快处理!", "Fiat", "The buyer has confirmed the payment, please deal with it as soon as possible!"), + OTC_ORDER_RECEIPT("OTC_ORDER_RECEIPT", "法币交易", "卖家已确认收款,交易完成!", "Fiat", "The seller has confirmed the payment, the transaction is completed!"), + OTC_ORDER_APPEAL("OTC_ORDER_APPEAL", "法币交易", "订单已进入申诉流程,请尽快处理!", "Fiat", "The order has entered the complaint process, please deal with it as soon as possible!"), + OTC_ORDER_APPEAL_FINISH("OTC_ORDER_APPEAL_FINISH", "法币交易", "订单申诉已结束,请前往查看>>", "Fiat", "The appeal of order is over, please go to see >>"), + OTC_ORDER_CANCEL("OTC_ORDER_CANCEL", "法币交易", "订单已取消,买家已取消订单!", "Fiat", "Order cancelled, buyer cancelled order!"), + OTC_ORDER_AUTO_CANCEL("OTC_ORDER_AUTO_CANCEL", "法币交易", "买家付款超时,订单已自动取消!", "Fiat", "Buyer payment timeout, the order has been automatically cancelled!"), + OTC_AD_ADMIND_CANCEL("OTC_AD_ADMIND_CANCEL", "法币交易", "后台管理员已经您的广告取消,请前往查看>>", "Fiat", "Background administrator has cancelled your advertisement, please go to see >>"), + OTC_MARKET_AGREE("OTC_MARKET_AGREE", "焰火网", "您提交的市商申请已通过!", "FLAME", "Your marketing application has been approved!"), + OTC_MARKET_REJECT("OTC_MARKET_REJECT", "焰火网", "您提交的市商申请已被驳回!", "FLAME", "Your marketing application has been rejected!"), + OTC_MARKET_CANCEL_AGREE("OTC_MARKET_CANCEL_AGREE", "焰火网", "您提交的取消市商申请已通过!", "FLAME", "Your application for cancellation has been approved!"), + OTC_MARKET_CANCEL_REJECT("OTC_MARKET_CANCEL_REJECT", "焰火网", "您提交的取消市商申请已驳回!", "FLAME", "Your application for cancellation has been rejected!"), + + CCT_HISTORY_MARK("CCT_HISTORY_MARK", "币币交易", "您的订单已成交,请前往查看>>", "Exchange", "Your order has been completed, please go to >>"), + CCT_HISTORY_AUTO_CANCEL("CCT_HISTORY_AUTO_CANCEL", "币币交易", "找不到匹配的撮合订单,订单已自动撤销", "Exchange", "Matching matching order cannot be found, the order has been automatically cancelled"), + CCT_HISTORY_ADMIN_CANCEL("CCT_HISTORY_ADMIN_CANCEL", "币币交易", "后台管理员已将您的订单取消,请前往查看>>", "Exchange", "The background administrator has cancelled your order. Please check >>"), + ; + + private String pushType; + private String cnTitle; + private String cnContent; + private String enTitle; + private String enContent; + + private static Map enumsMap = new HashMap<>(); + + static { + PushEnums[] pushEnums = PushEnums.values(); + for (PushEnums pushEnum : pushEnums) { + enumsMap.put(pushEnum.getPushType(), pushEnum); + } + } + + public static PushEnums getEnumByPushType(String pushType) { + return enumsMap.get(pushType); + } + + PushEnums(String pushType, String cnTitle, String cnContent, String enTitle, String enContent) { + this.pushType = pushType; + this.cnTitle = cnTitle; + this.cnContent = cnContent; + this.enTitle = enTitle; + this.enContent = enContent; + } + + public String getPushType() { + return pushType; + } + + public void setPushType(String pushType) { + this.pushType = pushType; + } + + public String getCnTitle() { + return cnTitle; + } + + public void setCnTitle(String cnTitle) { + this.cnTitle = cnTitle; + } + + public String getCnContent() { + return cnContent; + } + + public void setCnContent(String cnContent) { + this.cnContent = cnContent; + } + + public String getEnTitle() { + return enTitle; + } + + public void setEnTitle(String enTitle) { + this.enTitle = enTitle; + } + + public String getEnContent() { + return enContent; + } + + public void setEnContent(String enContent) { + this.enContent = enContent; + } +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/exception/BaseException.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/exception/BaseException.java new file mode 100644 index 0000000..50ccc7b --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/exception/BaseException.java @@ -0,0 +1,57 @@ +package com.blockchain.common.base.exception; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.enums.BaseResultEnums; +import com.blockchain.common.base.util.HttpRequestUtil; +import lombok.Data; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +/** + * 自定义基础异常,用于向上跑出可预知异常 + * + * @author huangxl + * @create 2018-11-12 11:09 + */ +@Data +public class BaseException extends RuntimeException { + protected int code; + protected String msg; + + public BaseException() { + this(BaseResultEnums.DEFAULT); + } + + public BaseException(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public BaseException(BaseResultEnums rs) { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + //可能是定时器调用,避免获取request空指针 + if (servletRequestAttributes == null) { + this.code = rs.getCode(); + this.msg = rs.getCnmsg(); + } else { + HttpServletRequest request = servletRequestAttributes.getRequest(); + String userLocale = HttpRequestUtil.getUserLocale(request); + String msg = ""; + switch (userLocale) { + case BaseConstant.USER_LOCALE_EN_US: + msg = rs.getEnMsg(); + break; + case BaseConstant.USER_LOCALE_ZH_HK: + msg = rs.getHkmsg(); + break; + default: + msg = rs.getCnmsg(); + break; + } + this.code = rs.getCode(); + this.msg = msg; + } + } +} \ No newline at end of file diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/exception/RPCException.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/exception/RPCException.java new file mode 100644 index 0000000..c526dcc --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/exception/RPCException.java @@ -0,0 +1,17 @@ +package com.blockchain.common.base.exception; + +import com.blockchain.common.base.dto.ResultDTO; + +/** + * @author huangxl + * @create 2018-11-21 21:19 + */ +public class RPCException extends BaseException { + public RPCException(int code, String msg) { + super(code, msg); + } + public RPCException(ResultDTO rs) { + this.code = rs.getCode(); + this.msg = rs.getMsg(); + } +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/DateTimeUtils.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/DateTimeUtils.java new file mode 100644 index 0000000..4336739 --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/DateTimeUtils.java @@ -0,0 +1,757 @@ +package com.blockchain.common.base.util; + +import org.apache.commons.lang3.ArrayUtils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.MathContext; +import java.math.RoundingMode; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +public class DateTimeUtils { + /** + * 计算精度 + */ + private static final MathContext mc = new MathContext(64, RoundingMode.HALF_UP); + + /* 毫秒级别常量 START */ + /** + * 一秒包含多少毫秒 + */ + public static final Long MILLISECOND_OF_SECOND = 1000L; + + /** + * 一分钟包含多少毫秒 + */ + public static final Long MILLISECOND_OF_MINUTE = 60 * MILLISECOND_OF_SECOND; + + /** + * 一小时包含多少毫秒 + */ + public static final Long MILLISECOND_OF_HOUR = 60 * MILLISECOND_OF_MINUTE; + + /** + * 一日包含多少毫秒 + */ + public static final Long MILLISECOND_OF_DAY = 24 * MILLISECOND_OF_HOUR; + + /** + * 一星期包含多少毫秒 + */ + public static final Long MILLISECOND_OF_WEEK = 7 * MILLISECOND_OF_DAY; + + /* 毫秒级别常量 END */ + /* 支持的常用格式 START */ + /** + * 日期时间毫秒格式 + */ + public static final String DATE_TIME_MILLISECOND_FORMAT = "yyyy-MM-dd HH:mm:ss SSS"; + + /** + * 日期时间格式 + */ + public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; + + /** + * 日期小时格式 + */ + public static final String DATE_HOUR_FORMAT = "yyyy-MM-dd HH"; + + /** + * 日期小时分钟格式 + */ + public static final String DATE_HOUR_MINUTE_FORMAT = "yyyy-MM-dd HH:mm"; + + /** + * 日期格式 + */ + public static final String YEAR_MONTH_DATE_FORMAT = "yyyy-MM-dd"; + + /** + * 年份月份格式 + */ + public static final String YEAR_MONTH_FORMAT = "yyyy-MM"; + + /** + * 月日时 + */ + public static final String MONTH_DAY_HOUR_FORMAT = "MM-dd HH"; + + + /** + * 月日 + */ + public static final String MONTH_DAY_FORMAT = "MM-dd"; + + /** + * 时间格式 + */ + public static final String TIME_FORMAT = "HH:mm:ss"; + + /** + * 小时分钟格式 + */ + public static final String HOUR_MINUTE_FORMAT = "HH:mm"; + + /** + * 4位长度年格式 + */ + public static final String YEAR_4_FORMAT = "yyyy"; + + /** + * 2位长度年格式 + */ + public static final String YEAR_2_FORMAT = "yy"; + + /** + * 月格式 + */ + public static final String MONTH_FORMAT = "MM"; + + /** + * 日格式 + */ + public static final String DAY_FORMAT = "dd"; + + /** + * 24小时格式 + */ + public static final String HOUR_24_FORMAT = "HH"; + + /** + * 12小时格式 + */ + public static final String HOUR_12_FORMAT = "hh"; + + /** + * 分钟格式 + */ + public static final String MINUTE_FORMAT = "mm"; + + /** + * 秒格式 + */ + public static final String SECOND_FORMAT = "ss"; + + + /* 支持的常用格式 END */ + + /** + * 时间域枚举类 + */ + public enum DateTimeField { + /** + * 年 + */ + YEAR(1, Calendar.YEAR, "年"), + /** + * 月 + */ + MONTH(2, Calendar.MONTH, "月"), + /** + * 周 + */ + WEEK(4, Calendar.DAY_OF_WEEK, "周"), + /** + * 日 + */ + DAY(8, Calendar.DATE, "日"), + /** + * 时 + */ + HOUR(16, Calendar.HOUR_OF_DAY, "时"), + /** + * 分 + */ + MINUTE(32, Calendar.MINUTE, "分"), + /** + * 秒 + */ + SECOND(64, Calendar.SECOND, "秒"); + + private Integer key; + + private Integer calendarFieldVal; + + private String value; + + private DateTimeField() { + } + + private DateTimeField(Integer key, Integer calendarFieldVal, String value) { + this.key = key; + this.calendarFieldVal = calendarFieldVal; + this.value = value; + } + + public Integer getKey() { + return key; + } + + public void setKey(Integer key) { + this.key = key; + } + + public Integer getCalendarFieldVal() { + return calendarFieldVal; + } + + public void setCalendarFieldVal(Integer calendarFieldVal) { + this.calendarFieldVal = calendarFieldVal; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return this.key + "-" + calendarFieldVal + "-" + this.value; + } + } + + /** + * 不支持时间域类型操作异常 + */ + public static class NoSupportDateTimeFieldException extends RuntimeException { + + private static final long serialVersionUID = 1189120547395966542L; + + public NoSupportDateTimeFieldException() { + super(); + } + + public NoSupportDateTimeFieldException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public NoSupportDateTimeFieldException(String message, Throwable cause) { + super(message, cause); + } + + public NoSupportDateTimeFieldException(String message) { + super(message); + } + + public NoSupportDateTimeFieldException(Throwable cause) { + super(cause); + } + } + + /** + * 将毫秒格式化成指定格式的字符串 + * + * @param date 长整型的毫秒 + * @param formatStr 格式字符串 + * @return 返回已经转换成指定格式的字符串 + */ + public static String format(Long date, String formatStr) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(date); + return format(calendar.getTime(), formatStr); + } + + /** + * 将Calendar时间格式化成默认格式的字符串 + * + * @param date Calendar时间 + * @return 返回已经转换成指定格式的字符串 + */ + public static String format(Calendar date) { + return format(date.getTime()); + } + + /** + * 将Date时间格式化成默认格式的字符串 + * + * @param date Date时间 + * @return 返回已经转换成指定格式的字符串 + */ + public static String format(Date date) { + SimpleDateFormat sdf = new SimpleDateFormat(DATE_TIME_FORMAT); + return sdf.format(date); + } + + /** + * 将Calendar时间格式化成指定格式的字符串 + * + * @param date Calendar时间 + * @param formatStr 格式字符串 + * @return 返回已经转换成指定格式的字符串 + */ + public static String format(Calendar date, String formatStr) { + return format(date.getTime(), formatStr); + } + + /** + * 将Date时间格式化成指定格式的字符串 + * + * @param date Date时间 + * @param formatStr 格式字符串 + * @return 返回已经转换成指定格式的字符串 + */ + public static String format(Date date, String formatStr) { + SimpleDateFormat sdf = new SimpleDateFormat(formatStr); + return sdf.format(date); + } + + /** + * 将指定的日期字符串按照默认格式转化成Date对象 + * + * @param dateStr 日期字符串 + * @return 转化后的Date对象 + */ + public static Date parse(String dateStr) { + SimpleDateFormat sdf = new SimpleDateFormat(DATE_TIME_FORMAT); + try { + return sdf.parse(dateStr); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 将指定的日期字符串按照默认格式转化成Date对象 + * + * @param dateStr 日期字符串 + * @param formatStr 格式字符串 + * @return 转化后的Date对象 + */ + public static Date parse(String dateStr, String formatStr) { + SimpleDateFormat sdf = new SimpleDateFormat(formatStr); + try { + return sdf.parse(dateStr); + } catch (ParseException e) { + e.printStackTrace(); + return null; + } + } + + /** + * 将指定的日期字符串按照多个格式转化成Date对象 + * + * @param dateStr 日期字符串 + * @param formats 格式字符串,自动匹配适合的格式 + * @return 转化后的Date对象 + */ + public static Date parse(String dateStr, String[] formats) { + if (ArrayUtils.isNotEmpty(formats)) { + for (int i = 0, len = formats.length; i < len; i++) { + String format = formats[i]; + Date parseDate = parse(dateStr, format); + if (null != parseDate) { + return parseDate; + } + } + } + return null; + } + + /** + * 将指定的Date对象按照默认格式重新转化成新的Date对象,这个目的是为了排除其他时间域的干扰,比如:排除毫秒、秒等干扰,特殊情况下还可以排除日、月等干扰 + * + * @param date 指定的Date对象 + * @return 新的Date对象 + */ + public static Date parse(Date date) { + String dateFormatStr = format(date); + return parse(dateFormatStr); + } + + /** + * 将指定的Date对象按照指定的格式重新转化成新的Date对象,这个目的是为了排除其他时间域的干扰,比如:排除毫秒、秒等干扰,特殊情况下还可以排除日、月等干扰 + * + * @param date 指定的Date对象 + * @param formatStr 格式字符串 + * @return 新的Date对象 + */ + public static Date parse(Date date, String formatStr) { + String dateFormatStr = format(date, formatStr); + return parse(dateFormatStr, formatStr); + } + + /** + * 将指定的Calendar对象按照指定的格式重新转化成新的Date对象,这个目的是为了排除其他时间域的干扰,比如:排除毫秒、秒等干扰,特殊情况下还可以排除日、月等干扰 + * + * @param date 指定的Calendar对象 + * @param formatStr 格式字符串 + * @return 新的Date对象 + */ + public static Date parse(Calendar date, String formatStr) { + return parse(date.getTime(), formatStr); + } + + /** + * 将指定的毫秒按照指定的格式重新转化成新的Date对象,这个目的是为了排除其他时间域的干扰,比如:排除毫秒、秒等干扰,特殊情况下还可以排除日、月等干扰 + * + * @param date 指定的毫秒 + * @param formatStr 格式字符串 + * @return 新的Date对象 + */ + public static Date parse(Long date, String formatStr) { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(date); + return parse(calendar, formatStr); + } + + /** + * 获取开始时间 + * + * @param field 指定的时间域 + * @param date 指定的日期 + * @return 获取指定字段开始的时间(精确级别到毫秒) + */ + public static Date getStart(DateTimeField field, Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + switch (field) { + case YEAR: + calendar.set(Calendar.MONTH, calendar.getActualMinimum(Calendar.MONTH)); + calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMinimum(Calendar.DAY_OF_MONTH)); + calendar.set(Calendar.HOUR_OF_DAY, calendar.getActualMinimum(Calendar.HOUR_OF_DAY)); + calendar.set(Calendar.MINUTE, calendar.getActualMinimum(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, calendar.getActualMinimum(Calendar.SECOND)); + calendar.set(Calendar.MILLISECOND, calendar.getActualMinimum(Calendar.MILLISECOND)); + break; + case MONTH: + calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMinimum(Calendar.DAY_OF_MONTH)); + calendar.set(Calendar.HOUR_OF_DAY, calendar.getActualMinimum(Calendar.HOUR_OF_DAY)); + calendar.set(Calendar.MINUTE, calendar.getActualMinimum(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, calendar.getActualMinimum(Calendar.SECOND)); + calendar.set(Calendar.MILLISECOND, calendar.getActualMinimum(Calendar.MILLISECOND)); + break; + case WEEK: + calendar.set(Calendar.DAY_OF_WEEK, calendar.getActualMinimum(Calendar.DAY_OF_WEEK)); + calendar.set(Calendar.HOUR_OF_DAY, calendar.getActualMinimum(Calendar.HOUR_OF_DAY)); + calendar.set(Calendar.MINUTE, calendar.getActualMinimum(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, calendar.getActualMinimum(Calendar.SECOND)); + calendar.set(Calendar.MILLISECOND, calendar.getActualMinimum(Calendar.MILLISECOND)); + break; + case DAY: + calendar.set(Calendar.HOUR_OF_DAY, calendar.getActualMinimum(Calendar.HOUR_OF_DAY)); + calendar.set(Calendar.MINUTE, calendar.getActualMinimum(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, calendar.getActualMinimum(Calendar.SECOND)); + calendar.set(Calendar.MILLISECOND, calendar.getActualMinimum(Calendar.MILLISECOND)); + break; + case HOUR: + calendar.set(Calendar.MINUTE, calendar.getActualMinimum(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, calendar.getActualMinimum(Calendar.SECOND)); + calendar.set(Calendar.MILLISECOND, calendar.getActualMinimum(Calendar.MILLISECOND)); + break; + case MINUTE: + calendar.set(Calendar.SECOND, calendar.getActualMinimum(Calendar.SECOND)); + calendar.set(Calendar.MILLISECOND, calendar.getActualMinimum(Calendar.MILLISECOND)); + break; + case SECOND: + calendar.set(Calendar.MILLISECOND, calendar.getActualMinimum(Calendar.MILLISECOND)); + break; + default: + break; + } + return calendar.getTime(); + } + + /** + * 获取结束时间 + * + * @param field 指定的域 + * @param date 指定的日期 + * @return 获取指定字段结束的时间(精确级别到秒) + */ + public static Date getEnd(DateTimeField field, Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + switch (field) { + case YEAR: + calendar.set(Calendar.MONTH, calendar.getActualMaximum(Calendar.MONTH)); + calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH)); + calendar.set(Calendar.HOUR_OF_DAY, calendar.getActualMaximum(Calendar.HOUR_OF_DAY)); + calendar.set(Calendar.MINUTE, calendar.getActualMaximum(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, calendar.getActualMaximum(Calendar.SECOND)); + calendar.set(Calendar.MILLISECOND, calendar.getActualMaximum(Calendar.MILLISECOND)); + break; + case MONTH: + calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH)); + calendar.set(Calendar.HOUR_OF_DAY, calendar.getActualMaximum(Calendar.HOUR_OF_DAY)); + calendar.set(Calendar.MINUTE, calendar.getActualMaximum(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, calendar.getActualMaximum(Calendar.SECOND)); + calendar.set(Calendar.MILLISECOND, calendar.getActualMaximum(Calendar.MILLISECOND)); + break; + case WEEK: + calendar.set(Calendar.DAY_OF_WEEK, calendar.getActualMaximum(Calendar.DAY_OF_WEEK)); + calendar.set(Calendar.HOUR_OF_DAY, calendar.getActualMaximum(Calendar.HOUR_OF_DAY)); + calendar.set(Calendar.MINUTE, calendar.getActualMaximum(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, calendar.getActualMaximum(Calendar.SECOND)); + calendar.set(Calendar.MILLISECOND, calendar.getActualMaximum(Calendar.MILLISECOND)); + break; + case DAY: + calendar.set(Calendar.HOUR_OF_DAY, calendar.getActualMaximum(Calendar.HOUR_OF_DAY)); + calendar.set(Calendar.MINUTE, calendar.getActualMaximum(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, calendar.getActualMaximum(Calendar.SECOND)); + calendar.set(Calendar.MILLISECOND, calendar.getActualMaximum(Calendar.MILLISECOND)); + break; + case HOUR: + calendar.set(Calendar.MINUTE, calendar.getActualMaximum(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, calendar.getActualMaximum(Calendar.SECOND)); + calendar.set(Calendar.MILLISECOND, calendar.getActualMaximum(Calendar.MILLISECOND)); + break; + case MINUTE: + calendar.set(Calendar.SECOND, calendar.getActualMaximum(Calendar.SECOND)); + calendar.set(Calendar.MILLISECOND, calendar.getActualMaximum(Calendar.MILLISECOND)); + break; + case SECOND: + calendar.set(Calendar.MILLISECOND, calendar.getActualMaximum(Calendar.MILLISECOND)); + break; + default: + break; + } + return calendar.getTime(); + } + + /** + * 判断指定的多个Calendar对象是否在同一个时间域内,比如:日、月、年、时、分等等 + * + * @param field 指定的时间域,日、月、年、时、分等等 + * @param dates 指定的多个Calendar对象 + * @return 是否在同一个时间与内, true/false + */ + public static Boolean same(DateTimeField field, Calendar dates[]) { + if (ArrayUtils.isNotEmpty(dates) && 1 < dates.length) { + for (int i = 1, len = dates.length; i < len; i++) { + Calendar std = dates[0];// 第一个作为标准比较 + Calendar target = dates[i]; + Boolean sameFlag = same(field, std, target); + if (sameFlag) { + return false; + } + } + return true; + } else { + return true; + } + } + + /** + * 判断指定的多个Date对象是否在同一个时间域内,比如:日、月、年、时、分等等 + * + * @param field 指定的时间域,日、月、年、时、分等等 + * @param dates 指定的多个Date对象 + * @return 是否在同一个时间与内, true/false + */ + public static Boolean same(DateTimeField field, Date dates[]) { + if (ArrayUtils.isNotEmpty(dates) && 1 < dates.length) { + Calendar[] parseCalendars = new Calendar[dates.length]; + for (int i = 0, len = dates.length; i < len; i++) { + Date date = dates[i]; + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + parseCalendars[i] = calendar; + } + return same(field, parseCalendars); + } else { + return true; + } + } + + /** + * 判断指定的两个Date对象是否在同一个时间域内,比如:日、月、年、时、分等等 + * + * @param field 指定的时间域,日、月、年、时、分等等 + * @param first 第一个Date对象 + * @param second 第二个Date对象 + * @return 是否在同一个时间与内, true/false + */ + public static Boolean same(DateTimeField field, Date first, Date second) { + Calendar startCalendar = Calendar.getInstance(); + startCalendar.setTime(first); + Calendar endCalendar = Calendar.getInstance(); + endCalendar.setTime(second); + return same(field, startCalendar, endCalendar); + } + + /** + * 判断指定的两个Calendar对象是否在同一个时间域内,比如:日、月、年、时、分等等 + * + * @param field 指定的时间域,日、月、年、时、分等等 + * @param first 第一个Calendar对象 + * @param second 第二个Calendar对象 + * @return 是否在同一个时间与内, true/false + */ + public static Boolean same(DateTimeField field, Calendar first, Calendar second) { + Date parseFirstDate = null; + Date parseSecondDate = null; + switch (field) { + case YEAR: + parseFirstDate = parse(first, YEAR_4_FORMAT); + parseSecondDate = parse(second, YEAR_4_FORMAT); + break; + case MONTH: + parseFirstDate = parse(first, YEAR_MONTH_FORMAT); + parseSecondDate = parse(second, YEAR_MONTH_FORMAT); + break; + case WEEK: + Calendar firstCalendar = Calendar.getInstance(); + Calendar secondCalendar = Calendar.getInstance(); + Date parseFirstCalendarDate = parse(first, YEAR_MONTH_DATE_FORMAT); + Date parseSecondCalendarDate = parse(second, YEAR_MONTH_DATE_FORMAT); + firstCalendar.setTime(parseFirstCalendarDate); + secondCalendar.setTime(parseSecondCalendarDate); + firstCalendar.set(Calendar.DAY_OF_WEEK, firstCalendar.getActualMinimum(Calendar.DAY_OF_WEEK)); + secondCalendar.set(Calendar.DAY_OF_WEEK, firstCalendar.getActualMinimum(Calendar.DAY_OF_WEEK)); + parseFirstDate = firstCalendar.getTime(); + parseSecondDate = secondCalendar.getTime(); + break; + case DAY: + parseFirstDate = parse(first, YEAR_MONTH_DATE_FORMAT); + parseSecondDate = parse(second, YEAR_MONTH_DATE_FORMAT); + break; + case HOUR: + parseFirstDate = parse(first, DATE_HOUR_FORMAT); + parseSecondDate = parse(second, DATE_HOUR_FORMAT); + break; + case MINUTE: + parseFirstDate = parse(first, DATE_HOUR_MINUTE_FORMAT); + parseSecondDate = parse(second, DATE_HOUR_MINUTE_FORMAT); + break; + case SECOND: + parseFirstDate = parse(first, DATE_TIME_FORMAT); + parseSecondDate = parse(second, DATE_TIME_FORMAT); + break; + default: + parseFirstDate = parse(first, DATE_TIME_MILLISECOND_FORMAT); + parseSecondDate = parse(second, DATE_TIME_MILLISECOND_FORMAT); + break; + } + return (parseFirstDate.getTime() == parseSecondDate.getTime()); + } + + /** + * 获取两个Data对象的时间差,并且按照指定的时间域为标准来计算 + * + * @param field 指定的时间域,日、月、年、时、分等等 + * @param start 第一个Date对象 + * @param end 第二个Date对象 + * @param ignoreMilliSecond 是否忽略毫秒的干扰 + * @return 指定的时间差, 根据field的域来返回指定时间差 + */ + public static BigDecimal difference(DateTimeField field, Date start, Date end, boolean ignoreMilliSecond) { + Calendar startCalendar = Calendar.getInstance(); + startCalendar.setTime(start); + Calendar endCalendar = Calendar.getInstance(); + endCalendar.setTime(end); + return difference(field, startCalendar, endCalendar, ignoreMilliSecond); + } + + /** + * 获取两个Calendar对象的时间差,并且按照指定的时间域为标准来计算 + * + * @param field 指定的时间域,日、月、年、时、分等等 + * @param start 第一个Calendar对象 + * @param end 第二个Calendar对象 + * @param ignoreMilliSecond 是否忽略毫秒的干扰 + * @return 指定的时间差, 根据field的域来返回指定时间差 + */ + public static BigDecimal difference(DateTimeField field, Calendar start, Calendar end, boolean ignoreMilliSecond) { + BigDecimal returnVal = null; + switch (field) { + case YEAR: + case MONTH: + throw new NoSupportDateTimeFieldException("不支持YEAR/MONTH级别的时间差运算,因为标注参考时间不确定!"); + case WEEK: + returnVal = (new BigDecimal(difference(start, end, ignoreMilliSecond)).divide(BigDecimal.valueOf(MILLISECOND_OF_WEEK), mc).setScale(4, RoundingMode.HALF_UP)); + break; + case DAY: + returnVal = (new BigDecimal(difference(start, end, ignoreMilliSecond)).divide(BigDecimal.valueOf(MILLISECOND_OF_DAY), mc).setScale(4, RoundingMode.HALF_UP)); + break; + case HOUR: + returnVal = (new BigDecimal(difference(start, end, ignoreMilliSecond)).divide(BigDecimal.valueOf(MILLISECOND_OF_HOUR), mc).setScale(4, RoundingMode.HALF_UP)); + break; + case MINUTE: + returnVal = (new BigDecimal(difference(start, end, ignoreMilliSecond)).divide(BigDecimal.valueOf(MILLISECOND_OF_MINUTE), mc).setScale(4, RoundingMode.HALF_UP)); + break; + case SECOND: + returnVal = (new BigDecimal(difference(start, end, ignoreMilliSecond)).divide(BigDecimal.valueOf(MILLISECOND_OF_SECOND), mc).setScale(4, RoundingMode.HALF_UP)); + break; + default: + returnVal = new BigDecimal(difference(start, end, ignoreMilliSecond)); + break; + } + return returnVal; + } + + /** + * 获取两个Calendar对象的时间差,并且按照毫秒为标准来计算 + * + * @param start 第一个Calendar对象 + * @param end 第二个Calendar对象 + * @param ignoreMilliSecond 是否忽略毫秒的干扰 + * @return 指定的时间差, 根据field的域来返回指定时间差 + */ + public static BigInteger difference(Calendar start, Calendar end, boolean ignoreMilliSecond) { + if (ignoreMilliSecond) { + start.clear(Calendar.MILLISECOND); + end.clear(Calendar.MILLISECOND); + } + BigInteger startMilliSecond = BigInteger.valueOf(start.getTimeInMillis()); + BigInteger endMilliSecond = BigInteger.valueOf(end.getTimeInMillis()); + return (endMilliSecond.subtract(startMilliSecond)); + } + + /** + * 获取两个Date对象的时间差,并且按照毫秒为标准来计算 + * + * @param start 第一个Date对象 + * @param end 第二个Date对象 + * @param ignoreMilliSecond 是否忽略毫秒的干扰 + * @return 指定的时间差, 根据field的域来返回指定时间差 + */ + public static BigInteger difference(Date start, Date end, boolean ignoreMilliSecond) { + Calendar startCalendar = Calendar.getInstance(); + startCalendar.setTime(start); + Calendar endCalendar = Calendar.getInstance(); + endCalendar.setTime(end); + return difference(startCalendar, endCalendar, ignoreMilliSecond); + } + + /** + * 将时间毫秒数转化为时间间隔 + * + * @param timeMillis 间隔时间 + */ + public static String getIntervalTimeInString(long timeMillis) { + long time = timeMillis; + long ts = 1000L;//1秒 + long tm = 1000L * 60;//一分钟 + long th = 1000L * 60 * 60;//一小时 + long td = 1000L * 60 * 60 * 24;//一天 + long tM = 1000L * 60 * 60 * 24 * 30;//一个月 + long ty = 1000L * 60 * 60 * 24 * 365;//一年 + if (time > ty) { + return time / ty + "年前"; + } + if (time > tM) { + return time / tM + "个月前"; + } + if (time > td) { + return time / td + "天前"; + } + if (time > th) { + return time / th + "小时前"; + } + if (time > tm) { + return time / tm + "分钟前"; + } + if (time > ts) { + return time / ts + "秒前"; + } + return "刚刚"; + } +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/ExceptionPreconditionUtils.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/ExceptionPreconditionUtils.java new file mode 100644 index 0000000..d118765 --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/ExceptionPreconditionUtils.java @@ -0,0 +1,79 @@ +package com.blockchain.common.base.util; + +import com.blockchain.common.base.enums.BaseResultEnums; +import com.blockchain.common.base.exception.BaseException; +import org.apache.commons.lang3.StringUtils; + +import java.util.Collection; + +public class ExceptionPreconditionUtils { + /** + * 用于判断参数是否符合条件 + * + * @param flag 判断的条件 + * @param e 如果对象不符合条件,平台需要返回的业务错误 + */ + public static void checkBool(boolean flag, BaseException e) { + if (!flag) { + throw e; + } + } + + /** + * 判断字符串不为空或空串 + * + * @param checkString 需要判断的字符串 + * @param e 返回的业务错误 + */ + public static void checkStringNotBlank(String checkString, BaseException e) { + if (StringUtils.isBlank(checkString)) { + throw e; + } + } + + /** + * 判断一个集合是否为空(包含null和size为0的情况) + * + * @param collection + * @param e + */ + public static void checkCollectionNotEmpty(Collection collection, BaseException e) { + if (collection == null || collection.size() == 0) { + throw e; + } + } + + /** + * 判断一个对象不可以为空 + * + * @param obj 需要判断的对象 + * @param e 如果对象为空,平台需要返回的业务错误代码 + */ + public static void checkNotNull(Object obj, BaseException e) { + if (obj == null) { + throw e; + } + } + + /** + * 检验参数不能为空 + * + * @param args 多个参数 + */ + public static void notEmpty(Object... args) { + if (args == null) { + throw new BaseException(BaseResultEnums.PARAMS_ERROR); + } + for (Object obj : args) { + if (obj instanceof String) { + if (StringUtils.isEmpty((String) obj)) { + throw new BaseException(BaseResultEnums.PARAMS_ERROR); + } + } + if (obj == null) { + throw new BaseException(BaseResultEnums.PARAMS_ERROR); + } + } + } + +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/FileUploadHelper.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/FileUploadHelper.java new file mode 100644 index 0000000..6b7e89b --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/FileUploadHelper.java @@ -0,0 +1,138 @@ +package com.blockchain.common.base.util; + +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.multipart.MultipartFile; +import sun.misc.BASE64Decoder; + +import javax.servlet.http.HttpServletRequest; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class FileUploadHelper { + /** + * 保存文件 + * + * @param file 文件 + * @param relativeDir 文件类型 + * @throws IOException 存储异常 + */ + protected static String saveFile(MultipartFile file, String rootDir, String relativeDir) throws IOException { + //源文件名 + String name = file.getOriginalFilename(); + //文件后缀名 + String suffix = name.substring(name.lastIndexOf(".")); + //格式化文件路径 + rootDir = formatSeparator(rootDir); + relativeDir = formatSeparator(relativeDir); + //申诉文件保存到本地/服务器 + String fileRelativePath = generateRelativePath(rootDir, relativeDir, suffix); + file.transferTo(new File(rootDir + fileRelativePath)); + return fileRelativePath; + } + + /** + * 将base64字符串存储为jpg文件 + * + * @param imgBase64 base64字符串 + * @param relativeDir 文件相对路径 + * @return 文件名 + * @throws Exception 存储异常 + */ + protected static String generateImage(String imgBase64, String rootDir, String relativeDir) throws IOException { //对字节数组字符串进行Base64解码并生成图片 + if (StringUtils.isEmpty(imgBase64)) { + throw new RuntimeException("file base64 string is empty"); + } + BASE64Decoder decoder = new BASE64Decoder(); + //Base64解码 + byte[] b = decoder.decodeBuffer(imgBase64); + for (int i = 0; i < b.length; ++i) { + if (b[i] < 0) {//调整异常数据 + b[i] += 256; + } + } + //格式化文件路径 + rootDir = formatSeparator(rootDir); + relativeDir = formatSeparator(relativeDir); + //文件存放路径 + String relativePath = generateRelativePath(rootDir, relativeDir, ".jpg"); + OutputStream out = null; + try { + out = new FileOutputStream(rootDir + relativePath); + out.write(b); + out.flush(); + return relativePath; + } catch (IOException e) { + throw e; + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + /** + * 文件名命名规则 + * + * @return 文件名 + */ + private static String createFileName() { + String timeFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + + RandomStringUtils.random(6, true, true); + return timeFormat; + } + + /** + * 生成文件所在目录,并返回文件的相对地址 + * + * @param rootPath 文件根目录 + * @param relativeDir 文件相对目录 + * @param suffix 文件后缀名 + * @return 文件相对地址 + */ + private static String generateRelativePath(String rootPath, String relativeDir, String suffix) { + String separator = File.separator; + if (relativeDir.startsWith(separator)) { + relativeDir = relativeDir.substring(1); + } + //格式化后缀 + if (!suffix.startsWith(".")) { + suffix = "." + suffix; + } + String fileDir = rootPath + relativeDir; + File file = new File(fileDir); + if (!file.exists()) { + file.mkdirs(); + } + return relativeDir + createFileName() + suffix; + } + + /** + * 格式化文件分隔符 + * + * @param dir 目录 + * @return 格式化后的目录名 + */ + private static String formatSeparator(String dir) { + String separator = File.separator; + if (StringUtils.isNotEmpty(dir)) { + String replace = separator.equals("\\") ? "\\\\" : "/"; + String format = dir.replaceAll("[\\\\/]+", replace); + if (!format.endsWith(separator)) { + format += separator; + } + return format; + } + return ""; + } +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/HttpRequestUtil.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/HttpRequestUtil.java new file mode 100644 index 0000000..1943323 --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/HttpRequestUtil.java @@ -0,0 +1,74 @@ +package com.blockchain.common.base.util; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.dto.SessionUserDTO; +import com.blockchain.common.base.enums.BaseResultEnums; +import com.blockchain.common.base.exception.BaseException; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +/** + * 获取session用户信息的工具类 + */ +public class HttpRequestUtil { + /** + * 获取用户国际化标识 + */ + public static String getUserLocale(HttpServletRequest request) { + String userLocale = request.getParameter(BaseConstant.PARAMS_LOCALE); + if (StringUtils.isNotEmpty(userLocale)) { + return userLocale; + } + userLocale = request.getHeader(BaseConstant.PARAMS_LOCALE); + if(StringUtils.isNotEmpty(userLocale)){ + return userLocale; + } + return BaseConstant.USER_LOCALE_DEFAULT; + } + + /** + * 获取用户真实IP地址,不使用request.getRemoteAddr()的原因是有可能用户使用了代理软件方式避免真实IP地址, + * 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值 + * + * @return ip + */ + public static String getIpAddr() { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + String ip = request.getHeader("x-forwarded-for"); + if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { + // 多次反向代理后会有多个ip值,第一个ip才是真实ip + if (ip.indexOf(",") != -1) { + ip = ip.split(",")[0]; + } + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + System.out.println("Proxy-Client-IP ip: " + ip); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + System.out.println("WL-Proxy-Client-IP ip: " + ip); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + System.out.println("HTTP_CLIENT_IP ip: " + ip); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + System.out.println("HTTP_X_FORWARDED_FOR ip: " + ip); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Real-IP"); + System.out.println("X-Real-IP ip: " + ip); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + System.out.println("getRemoteAddr ip: " + ip); + } + return ip == null ? "unknown" : ip; + } + +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/HttpUtilManager.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/HttpUtilManager.java new file mode 100644 index 0000000..ffe0fe3 --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/HttpUtilManager.java @@ -0,0 +1,160 @@ +package com.blockchain.common.base.util; + +import org.apache.commons.io.IOUtils; +import org.apache.http.Consts; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.conn.ConnectionKeepAliveStrategy; +import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.protocol.HttpContext; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class HttpUtilManager { + + private static HttpClient client; + private static long startTime = System.currentTimeMillis(); + public static PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); + private static ConnectionKeepAliveStrategy keepAliveStrat = new DefaultConnectionKeepAliveStrategy() { + public long getKeepAliveDuration( + HttpResponse response, + HttpContext context) { + long keepAlive = super.getKeepAliveDuration(response, context); + + if (keepAlive == -1) { + keepAlive = 5000; + } + return keepAlive; + } + + }; + + private HttpUtilManager() { + client = HttpClients.custom().setConnectionManager(cm).setKeepAliveStrategy(keepAliveStrat).build(); + } + + public static void IdleConnectionMonitor() { + + if (System.currentTimeMillis() - startTime > 30000) { + startTime = System.currentTimeMillis(); + cm.closeExpiredConnections(); + cm.closeIdleConnections(30, TimeUnit.SECONDS); + } + } + + private static RequestConfig requestConfig = RequestConfig.custom() + .setSocketTimeout(20000) + .setConnectTimeout(20000) + .setConnectionRequestTimeout(20000) + .build(); + + + public static HttpUtilManager getInstance() { + return new HttpUtilManager(); + } + + public HttpClient getHttpClient() { + return client; + } + + private HttpPost httpPostMethod(String url) { + return new HttpPost(url); + } + + private HttpRequestBase httpGetMethod(String url) { + return new HttpGet(url); + } + + public String requestHttpGet(String url){ + return this.requestHttpGet(url,null); + } + + public String requestHttpGet(String url, String param){ + InputStream is = null; + String responseData = ""; + try { + IdleConnectionMonitor(); + if (param != null && !param.equals("")) { + if (url.endsWith("?")) { + url = url + param; + } else { + url = url + "?" + param; + } + } + HttpRequestBase method = this.httpGetMethod(url); + method.setConfig(requestConfig); + HttpResponse response = client.execute(method); + HttpEntity entity = response.getEntity(); + if (entity == null) { + return ""; + } + is = entity.getContent(); + responseData = IOUtils.toString(is, "UTF-8"); + } catch (IOException e) { + e.printStackTrace(); + }finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return responseData; + } + + public String requestHttpPost(String url, Map params) throws IOException { + InputStream is = null; + String responseData = ""; + IdleConnectionMonitor(); + + HttpPost method = this.httpPostMethod(url); + List valuePairs = this.convertMap2PostParams(params); + + UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(valuePairs, Consts.UTF_8); + method.setEntity(urlEncodedFormEntity); + method.setConfig(requestConfig); + HttpResponse response = client.execute(method); + HttpEntity entity = response.getEntity(); + if (entity == null) { + return ""; + } + is = entity.getContent(); + responseData = IOUtils.toString(is, "UTF-8"); + return responseData; + + } + + private List convertMap2PostParams(Map params) { + List keys = new ArrayList(params.keySet()); + if (keys.isEmpty()) { + return null; + } + int keySize = keys.size(); + List data = new LinkedList(); + for (int i = 0; i < keySize; i++) { + String key = keys.get(i); + String value = params.get(key); + data.add(new BasicNameValuePair(key, value)); + } + return data; + } + +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/JsonUtils.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/JsonUtils.java new file mode 100644 index 0000000..80d0713 --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/JsonUtils.java @@ -0,0 +1,273 @@ +package com.blockchain.common.base.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.*; + +public class JsonUtils { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + /** + * 将对象转换成json字符串。 + * + * @param data + * @return + */ + public static String objectToJson(Object data) { + try { + String string = MAPPER.writeValueAsString(data); + return string; + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 将json结果集转化为对象 + * + * @param jsonData json数据 + * @param beanType 对象中的object类型 + * @param + * @return + */ + public static T jsonToPojo(String jsonData, Class beanType) { + try { + T t = MAPPER.readValue(jsonData, beanType); + return t; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 将json数据转换成pojo对象list + * + * @param jsonData + * @param beanType + * @param + * @return + */ + public static List jsonToList(String jsonData, Class beanType) { + JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType); + try { + List list = MAPPER.readValue(jsonData, javaType); + return list; + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + /** + * set属性的值到Bean + * + * @param bean + * @param valMap + */ + public synchronized static void setFieldValue(Object bean, Map valMap) { + Class cls = bean.getClass(); + // 取出bean里的所有方法 + Method[] methods = cls.getDeclaredMethods(); + Field[] fields = cls.getDeclaredFields(); + + for (Field field : fields) { + try { + String fieldSetName = parSetName(field.getName(), false); + if (!checkSetMet(methods, fieldSetName)) { + fieldSetName = parSetName(field.getName(), true); + if (!checkSetMet(methods, fieldSetName)) { + continue; + } + } + Method fieldSetMet = cls.getMethod(fieldSetName, field.getType()); + String value = valMap.get(field.getName()); + if (null != value && !"".equals(value)) { + String fieldType = field.getType().getSimpleName(); + if ("String".equals(fieldType)) { + fieldSetMet.invoke(bean, value); + } else if ("Date".equals(fieldType)) { + Date temp = parseDate(value); + fieldSetMet.invoke(bean, temp); + } else if ("Integer".equals(fieldType) || "int".equals(fieldType)) { + Integer intval = Integer.parseInt(value); + fieldSetMet.invoke(bean, intval); + } else if ("Long".equalsIgnoreCase(fieldType)) { + Long temp = Long.parseLong(value); + fieldSetMet.invoke(bean, temp); + } else if ("Double".equalsIgnoreCase(fieldType)) { + Double temp = Double.parseDouble(value); + fieldSetMet.invoke(bean, temp); + } else if ("Boolean".equalsIgnoreCase(fieldType)) { + Boolean temp = Boolean.parseBoolean(value); + fieldSetMet.invoke(bean, temp); + } else if ("Date".equalsIgnoreCase(fieldType)) { + Date temp = parseDate(value); + fieldSetMet.invoke(bean, temp); + } else if ("BigDecimal".equals(fieldType)) { + BigDecimal temp = new BigDecimal(value); + fieldSetMet.invoke(bean, temp); + } else { + // throw new Jc("500","not supper type" + fieldType); + } + } + } catch (Exception e) { + continue; + } + } + + } + + /** + * 拼接某属性的 get方法 + * + * @param fieldName + * @return String + */ + private static String parGetName(String fieldName, boolean firstLow) { + if (null == fieldName || "".equals(fieldName)) { + return null; + } + if (firstLow && fieldName.length() > 1) { //判断是否为 第一个字母小写,第二个字母大写 + if (Character.isLowerCase(fieldName.charAt(0)) && Character.isUpperCase(fieldName.charAt(1))) { + return "get" + fieldName; + } + } + return "get" + fieldName.substring(0, 1).toUpperCase() + + fieldName.substring(1); + } + + /** + * 拼接在某属性的 set方法 + * + * @param fieldName + * @return String + */ + private static String parSetName(String fieldName, boolean firstLow) { + if (null == fieldName || "".equals(fieldName)) { + return null; + } + if (firstLow && fieldName.length() > 1) { //判断是否为 第一个字母小写,第二个字母大写 + if (Character.isLowerCase(fieldName.charAt(0)) && Character.isUpperCase(fieldName.charAt(1))) { + return "set" + fieldName; + } + } + return "set" + fieldName.substring(0, 1).toUpperCase() + + fieldName.substring(1); + } + + /** + * 格式化string为Date + * + * @param datestr + * @return date + */ + private static Date parseDate(String datestr) { + if (null == datestr || "".equals(datestr)) { + return null; + } + try { + String fmtstr = null; + if (datestr.indexOf(':') > 0) { + fmtstr = "yyyy-MM-dd HH:mm:ss"; + } else { + fmtstr = "yyyy-MM-dd"; + } + SimpleDateFormat sdf = new SimpleDateFormat(fmtstr, Locale.UK); + return sdf.parse(datestr); + } catch (Exception e) { + return null; + } + } + + /** + * 日期转化为String + * + * @param date + * @return date string + */ + private static String fmtDate(Date date) { + if (null == date) { + return null; + } + try { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); + return sdf.format(date); + } catch (Exception e) { + return null; + } + } + + /** + * 判断是否存在某属性的 set方法 + * + * @param methods + * @param fieldSetMet + * @return boolean + */ + private static boolean checkSetMet(Method[] methods, String fieldSetMet) { + for (Method met : methods) { + if (fieldSetMet.equals(met.getName())) { + return true; + } + } + return false; + } + + /** + * 判断是否存在某属性的 get方法 + * + * @param methods + * @param fieldGetMet + * @return boolean + */ + private static boolean checkGetMet(Method[] methods, String fieldGetMet) { + for (Method met : methods) { + if (fieldGetMet.equals(met.getName())) { + return true; + } + } + return false; + } + + /** + * 使用json的方式转换两个pojo类(源是包含下划线属性的) + * + * @param source 源对象 + * @param destCls 目标类 + * @param + * @return 目标对象 + */ + public static D mapLowerCaseWithUnderscoresSource(Object source, Class destCls) { + String sourceJson = objectToJson(source); + ObjectMapper objectMapper = new ObjectMapper(); + /** 设置映射下划线 */ + objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + try { + return objectMapper.readValue(sourceJson, destCls); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public static Map jsonToHashMap(String sourceJson, Class sourceClazz, Class destClazz) { + try { + return MAPPER.readValue(sourceJson, MAPPER.getTypeFactory().constructMapType(HashMap.class, sourceClazz, destClazz)); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/MD5Utils.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/MD5Utils.java new file mode 100644 index 0000000..124e2ef --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/MD5Utils.java @@ -0,0 +1,38 @@ +package com.blockchain.common.base.util; + +import java.security.MessageDigest; + +/** + * \*

Desciption:MD5加密工具类

+ * \* CreateTime: 2018/3/12 12:29 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +public class MD5Utils { + public final static String MD5(String s) { + char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + try { + byte[] btInput = s.getBytes(); + // 获得MD5摘要算法的 MessageDigest 对象 + MessageDigest mdInst = MessageDigest.getInstance("MD5"); + // 使用指定的字节更新摘要 + mdInst.update(btInput); + // 获得密文 + byte[] md = mdInst.digest(); + // 把密文转换成十六进制的字符串形式 + int j = md.length; + char str[] = new char[j * 2]; + int k = 0; + for (int i = 0; i < j; i++) { + byte byte0 = md[i]; + str[k++] = hexDigits[byte0 >>> 4 & 0xf]; + str[k++] = hexDigits[byte0 & 0xf]; + } + return new String(str); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/RSACoderUtils.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/RSACoderUtils.java new file mode 100644 index 0000000..b3a4b7c --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/RSACoderUtils.java @@ -0,0 +1,265 @@ +package com.blockchain.common.base.util; + +import com.blockchain.common.base.dto.TokenDTO; +import com.blockchain.common.base.enums.BaseResultEnums; +import com.blockchain.common.base.exception.BaseException; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.StringUtils; + +import javax.crypto.Cipher; +import java.security.*; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +/** + * \*

Desciption:

+ * \* CreateTime: 2018/12/1 11:18 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +public class RSACoderUtils { + //非对称密钥算法 + private static final String KEY_ALGORITHM = "RSA"; + + /** + * 密钥长度,DH算法的默认密钥长度是1024 + * 密钥长度必须是64的倍数,在512到65536位之间 + */ + private static final int KEY_SIZE = 1024; + + //公钥 + public static final byte[] publicKey = {48, -127, -97, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, + 3, -127, -115, 0, 48, -127, -119, 2, -127, -127, 0, -128, 48, -25, 69, -64, -57, -59, -64, -116, -5, -4, + -26, -5, 109, 17, 59, 68, -69, -16, -110, 19, -21, -78, 25, 77, -40, 18, 77, 2, 70, -45, 15, 112, 57, 111 + , 14, 78, -69, 18, -115, 21, 9, -38, 126, -107, -116, -124, -95, -116, 6, 18, 57, -73, -25, -9, 46, 69, 1 + , 127, -27, -92, 2, -119, 106, -33, -78, -35, -26, 72, -65, 116, 61, -53, 85, 94, 68, -84, -121, 55, -68, + 77, -128, 6, -99, 81, -5, 80, 52, 68, 53, -27, 79, -14, 37, 41, -18, 70, 96, -65, -32, -82, 75, -23, -77, + -39, -36, -46, 120, 7, 67, 101, 107, 58, 97, 66, 72, -52, -128, -62, 39, -121, -47, -73, 73, -100, 49, 9, + 113, 2, 3, 1, 0, 1}; + //私钥 + public static final byte[] privateKey = {48, -126, 2, 118, 2, 1, 0, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, + 1, 1, 5, 0, 4, -126, 2, 96, 48, -126, 2, 92, 2, 1, 0, 2, -127, -127, 0, -128, 48, -25, 69, -64, -57, -59, + -64, -116, -5, -4, -26, -5, 109, 17, 59, 68, -69, -16, -110, 19, -21, -78, 25, 77, -40, 18, 77, 2, 70, + -45, 15, 112, 57, 111, 14, 78, -69, 18, -115, 21, 9, -38, 126, -107, -116, -124, -95, -116, 6, 18, 57, + -73, -25, -9, 46, 69, 1, 127, -27, -92, 2, -119, 106, -33, -78, -35, -26, 72, -65, 116, 61, -53, 85, 94, + 68, -84, -121, 55, -68, 77, -128, 6, -99, 81, -5, 80, 52, 68, 53, -27, 79, -14, 37, 41, -18, 70, 96, -65, + -32, -82, 75, -23, -77, -39, -36, -46, 120, 7, 67, 101, 107, 58, 97, 66, 72, -52, -128, -62, 39, -121, + -47, -73, 73, -100, 49, 9, 113, 2, 3, 1, 0, 1, 2, -127, -128, 103, -35, 55, -59, -64, -119, 28, -91, 2, + -106, 57, 55, 61, -120, 5, 106, 44, 42, -54, -92, -47, 23, 43, 90, 109, 68, 32, -81, -36, -92, 93, -26, + 40, 91, -96, -85, -53, 6, -81, -27, 55, -94, -96, 49, -24, 33, -50, 100, -59, -5, 53, 81, 38, -68, -1, -3 + , -79, 83, -95, -71, 2, -58, 59, 103, -13, -78, -15, 56, 113, 53, -107, -98, 6, 54, -95, -110, -115, 88, + 2, -122, -111, 9, 29, 22, -78, -21, 105, -95, -74, -62, 109, -31, -48, -75, 101, 112, 103, 26, 11, -126, + 97, 48, 115, -74, 47, 122, -14, 97, -55, 77, 49, 7, -105, -87, 58, 21, -117, 98, -28, 27, -106, 113, -30, + -28, 45, -101, -109, 1, 2, 65, 0, -7, -37, 63, 25, -106, 28, 88, 14, -22, -47, -26, -117, 4, 55, -10, 37, + -73, 111, 75, 24, -123, 104, 42, -20, -46, -95, -119, -41, -82, -85, -11, -27, 41, -8, 38, 22, 112, -51, + 35, 112, 23, -66, 4, -55, -6, 33, -121, -98, 35, 2, -64, -79, 118, -85, -83, -33, -103, -107, -33, 127, + -52, -115, 93, 25, 2, 65, 0, -125, 87, -47, -128, 76, -6, -66, 113, -107, 27, -58, -46, -15, -54, 59, -74 + , -98, -95, 16, -43, 109, 81, -79, 26, -57, 120, -82, 95, 66, 47, 105, 49, -92, -28, 61, 81, -60, 115, + -18, -84, 8, 99, 25, -123, -109, 46, -48, 115, -37, -13, 56, 6, -9, 36, -36, 112, -42, -8, 62, -87, 21, + 70, -62, 25, 2, 65, 0, -16, -26, -89, 76, 48, 35, 91, -13, -26, 12, 67, 80, 61, -35, 7, 3, 14, 125, -53, + -43, -12, -86, -98, -40, 127, -83, 40, -114, 63, -25, -92, -54, 51, 81, 2, -56, 24, 50, 113, -68, -99, + -25, -92, 14, 105, -112, -14, -123, 82, 20, 81, 93, -55, -95, 117, -97, 101, 33, -49, -64, 20, -91, 39, + -31, 2, 64, 7, 23, 44, -106, 50, -111, -82, -54, 78, -12, 106, -19, 100, 100, 56, -119, 9, 83, 68, -89, + 96, -7, 114, 8, 50, 16, -113, -55, 80, -73, 98, -124, 109, -108, 108, -61, 7, 74, 2, -18, -126, -99, 102, + -7, 81, 18, -53, -22, 21, 75, -78, 16, -98, 50, -3, 59, -110, 63, 96, -110, -100, 53, 111, -79, 2, 64, 80 + , -113, -19, 28, -46, -110, -75, 123, -29, -94, -19, -28, -64, -124, 87, 5, -4, 125, 35, -68, 68, -4, -60 + , -38, 123, -28, -34, -1, 1, 52, -11, 19, -32, -24, 83, 119, -4, 66, -93, -43, -3, 105, -106, 22, -39, + -55, 116, 91, -124, 39, -106, 37, -83, -39, 26, 118, 78, -87, 22, 76, 12, -110, 112, 77}; + + /** + * 初始化密钥对 + * + * @return + */ + public static KeyPair initKey() throws Exception { + //实例化密钥生成器 + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM); + //初始化密钥生成器 + keyPairGenerator.initialize(KEY_SIZE); + //生成密钥对 + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + return keyPair; + } + + + /** + * 私钥加密 + * + * @param data 待加密数据 + * @param key 密钥 + * @return byte[] 加密数据 + */ + public static byte[] encryptByPrivateKey(byte[] data, byte[] key) throws Exception { + + //取得私钥 + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + //生成私钥 + PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec); + //数据加密 + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + return cipher.doFinal(data); + } + + /** + * 公钥加密 + * + * @param data 待加密数据 + * @param key 密钥 + * @return byte[] 加密数据 + */ + public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception { + + //实例化密钥工厂 + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + //初始化公钥 + //密钥材料转换 + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key); + //产生公钥 + PublicKey pubKey = keyFactory.generatePublic(x509KeySpec); + + //数据加密 + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.ENCRYPT_MODE, pubKey); + return cipher.doFinal(data); + } + + /** + * 私钥解密 + * + * @param data 待解密数据 + * @param key 密钥 + * @return byte[] 解密数据 + */ + public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception { + //取得私钥 + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + //生成私钥 + PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec); + //数据解密 + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + return cipher.doFinal(data); + } + + /** + * 公钥解密 + * + * @param data 待解密数据 + * @param key 密钥 + * @return byte[] 解密数据 + */ + public static byte[] decryptByPublicKey(byte[] data, byte[] key) throws Exception { + + //实例化密钥工厂 + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + //初始化公钥 + //密钥材料转换 + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key); + //产生公钥 + PublicKey pubKey = keyFactory.generatePublic(x509KeySpec); + //数据解密 + Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); + cipher.init(Cipher.DECRYPT_MODE, pubKey); + return cipher.doFinal(data); + } + + /** + * 加密成token + * + * @param dto + * @return + */ + public static String encryptToken(TokenDTO dto) { + try { + //转成json数据 +// String json = JsonUtils.objectToJson(dto); + String json = dto.getTel() + "-" + dto.getTimestamp() + "-" + dto.getTokenType(); + //用私钥加密 + byte[] encrypt = RSACoderUtils.encryptByPrivateKey(json.getBytes(), privateKey); + //转成字符串 + return Base64.encodeBase64String(encrypt); + } catch (Exception e) { + throw new BaseException(BaseResultEnums.RSA_ERROR); + } + } + + /** + * 解密获取用户数据 + * + * @param token + * @return + */ + public static TokenDTO decryptToken(String token) { + try { + //转成数组 + byte[] decrypt = Base64.decodeBase64(StringUtils.getBytesUtf8(token)); + //解密 + byte[] bytes = RSACoderUtils.decryptByPublicKey(decrypt, publicKey); + //转成json数据 + String[] json = new String(bytes).split("-"); + TokenDTO tokenDTO = new TokenDTO(); + tokenDTO.setTel(json[0]); + tokenDTO.setTimestamp(new Long(json[1])); + tokenDTO.setTokenType(json[2]); + //返回token对象 + return tokenDTO; + } catch (Exception e) { + return null; + } + } + + /** + * 加密私钥密码 + * + * @param data 私钥/密码 + * @return + */ + public static String encryptPassword(String data) { + try { + //用私钥加密 + byte[] encrypt = RSACoderUtils.encryptByPrivateKey(data.getBytes(), privateKey); + //转成字符串 + return Base64.encodeBase64String(encrypt); + } catch (Exception e) { + throw new BaseException(BaseResultEnums.RSA_ERROR); + } + } + /** + * 解密获取密码 + * + * @param data 待解密数据 + * @return 密码 + */ + public static String decryptPassword(String data) { + try { + byte[] dataByte = Base64.decodeBase64(data); + byte[] password = RSACoderUtils.decryptByPublicKey(dataByte, publicKey); + return new String(password); + } catch (Exception e) { + throw new BaseException(BaseResultEnums.PASSWORD_ERROR); + } + } + + /** + * 根据私钥解密获取密码 + * + * @param data 待解密数据 + * @param key 私钥 + * @return 密码 + */ + public static String decryptPassword(String data, String key) { + try { + byte[] dataByte = Base64.decodeBase64(data); + byte[] keyByte = Base64.decodeBase64(key); + byte[] password = RSACoderUtils.decryptByPrivateKey(dataByte, keyByte); + return new String(password); + } catch (Exception e) { + throw new BaseException(BaseResultEnums.PASSWORD_ERROR); + } + } + +} diff --git a/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/SSOHelper.java b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/SSOHelper.java new file mode 100644 index 0000000..3c1be68 --- /dev/null +++ b/blockchain-common/blockchain-common-base/src/main/java/com/blockchain/common/base/util/SSOHelper.java @@ -0,0 +1,162 @@ +package com.blockchain.common.base.util; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.constant.TokenTypeEnums; +import com.blockchain.common.base.dto.SessionUserDTO; +import com.blockchain.common.base.dto.TokenDTO; +import com.blockchain.common.base.enums.BaseResultEnums; +import com.blockchain.common.base.exception.BaseException; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.redis.core.RedisTemplate; + +import javax.servlet.http.HttpServletRequest; +import java.util.concurrent.TimeUnit; + +/** + * 单点登录工具类,使用token获取、设置用户信息 + */ +public class SSOHelper { + private static final int TOKEN_INVALID_DAYS = 30;//token过期时间 + private static final int TOKEN_PC_INVALID_DAYS = 30;//token过期时间 + + /** + * 获取登录用户id + * + * @param redisTemplate redis + */ + public static String getUserId(RedisTemplate redisTemplate, HttpServletRequest request) { + SessionUserDTO user = getUser(redisTemplate, request); +// resetTokenTTL(redisTemplate, user.getTel(), user);//重新设置过期时间 + return user.getId(); + } + + /*** + * 获取登录用户缓存信息 + * 已登录返回用户id + * 未登录返回null + * @param redisTemplate + * @param request + * @return + */ + public static String getUserIdIsExits(RedisTemplate redisTemplate, HttpServletRequest request) { + SessionUserDTO effectiveUserIfExist = getEffectiveUserIfExist(redisTemplate, request); + if (effectiveUserIfExist == null) { + return null; + } + return effectiveUserIfExist.getId(); + } + + /** + * 检查用户登录状态 + */ + public static void checkUser(RedisTemplate redisTemplate, HttpServletRequest request) { + getUser(redisTemplate, request); + } + + /** + * 获取登录用户缓存信息 + * + * @param redisTemplate redis + */ + public static SessionUserDTO getUser(RedisTemplate redisTemplate, HttpServletRequest request) { + SessionUserDTO userDTO = getEffectiveUserIfExist(redisTemplate, request); + if (userDTO == null) { + throw new BaseException(BaseResultEnums.NO_LOGIN); + } + return userDTO; + } + + /** + * 设置用户 + * + * @param user 用户信息 + * @param redisTemplate redis + */ + public static void setUser(SessionUserDTO user, RedisTemplate redisTemplate) { + setUser(user, redisTemplate, TokenTypeEnums.APP.getValue()); + } + + /** + * 设置用户 + * + * @param user 用户信息 + * @param redisTemplate redis + * @param tokenType tokenType + */ + public static void setUser(SessionUserDTO user, RedisTemplate redisTemplate, String tokenType) { + if (StringUtils.isBlank(user.getTel())) { + throw new BaseException(BaseResultEnums.DEFAULT); + } + resetTokenTTL(redisTemplate, user.getTel(), user, tokenType); + } + + /** + * 移除用户token信息 + */ + public static void removeUser(HttpServletRequest request, RedisTemplate redisTemplate) { + removeUser(request, redisTemplate, TokenTypeEnums.APP.getValue()); + } + + /** + * 移除用户token信息 + */ + public static void removeUser(HttpServletRequest request, RedisTemplate redisTemplate, String tokenType) { + SessionUserDTO user = getEffectiveUserIfExist(redisTemplate, request); + if (user == null) { + return; + } + String tel = user.getTel(); + if (!StringUtils.isEmpty(tel)) { + String key = BaseConstant.REDIS_TOKEN_KEY + tel; + if (tokenType.equals(TokenTypeEnums.PC.getValue())) { + key = BaseConstant.REDIS_TOKEN_PC_KEY + tel; + } + if (redisTemplate.hasKey(key)) { + redisTemplate.delete(key); + } + } + } + + /** + * 重置token过期时间 + * + * @param redisTemplate redis缓存 + * @param tel tel + * @param user 用户信息 + */ + public static void resetTokenTTL(RedisTemplate redisTemplate, String tel, SessionUserDTO user, String tokenType) { + if (tokenType.equals(TokenTypeEnums.APP.getValue())) { + redisTemplate.opsForValue().set(BaseConstant.REDIS_TOKEN_KEY + tel, user, TOKEN_INVALID_DAYS, TimeUnit.DAYS); + } else { + redisTemplate.opsForValue().set(BaseConstant.REDIS_TOKEN_PC_KEY + tel, user, TOKEN_PC_INVALID_DAYS, TimeUnit.HOURS); + } + } + + /** + * 获取登录用户缓存信息 + * + * @param redisTemplate redis + */ + private static SessionUserDTO getEffectiveUserIfExist(RedisTemplate redisTemplate, HttpServletRequest request) { + String token = request.getHeader(BaseConstant.SSO_TOKEN_HEADER); + if (StringUtils.isEmpty(token)) { + return null; + } + TokenDTO tokenDTO = RSACoderUtils.decryptToken(token); + if (tokenDTO == null) { + return null; + } + String key = BaseConstant.REDIS_TOKEN_KEY + tokenDTO.getTel(); + if (tokenDTO.getTokenType().equals(TokenTypeEnums.PC.getValue())) { + key = BaseConstant.REDIS_TOKEN_PC_KEY + tokenDTO.getTel(); + } + if (redisTemplate.hasKey(key)) {//如果有token,拿到用户信息 + SessionUserDTO user = (SessionUserDTO) redisTemplate.opsForValue().get(key); + if (!user.getTimestamp().equals(tokenDTO.getTimestamp())) { + throw new BaseException(BaseResultEnums.LOGIN_REPLACED);//如果时间戳不对,则提示账号在别处登录 + } + return user; + } + return null; + } +} diff --git a/blockchain-common/blockchain-common-pom/pom.xml b/blockchain-common/blockchain-common-pom/pom.xml new file mode 100644 index 0000000..010c046 --- /dev/null +++ b/blockchain-common/blockchain-common-pom/pom.xml @@ -0,0 +1,230 @@ + + + + blockchain-common + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + blockchain-common-pom + + + + 1.3.2 + 1.3.5 + 4.0.3 + + + 5.1.1 + 1.2.3 + + + 5.1.39 + 1.1.9 + + + 1.0-SNAPSHOT + + 2.7.0 + 2.7.0 + + 1.11.3 + 3.7 + 1.2.47 + 23.0 + 1.10 + + + 4.3.5 + + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.boot + spring-boot-devtools + true + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.session + spring-session-data-redis + + + org.springframework.boot + spring-boot-starter-cache + + + + + + + org.springframework.cloud + spring-cloud-starter-config + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + org.springframework.cloud + spring-cloud-starter-netflix-hystrix + + + + org.springframework.cloud + spring-cloud-commons + + + + + + + + io.springfox + springfox-swagger-ui + ${swagger-ui.version} + + + + io.springfox + springfox-swagger2 + ${swagger2.version} + + + + + + + mysql + mysql-connector-java + ${mysql-connector.version} + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${mybatis-starter-version} + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${springboot.pagehelper.version} + + + tk.mybatis + mapper + ${tkmybatis.version} + + + + com.alibaba + druid-spring-boot-starter + ${druid-starter-version} + + + + + + com.alibaba + fastjson + ${fastjson-version} + + + org.apache.commons + commons-lang3 + ${commons-lang3-version} + + + + com.google.guava + guava + ${google-guava-version} + + + + org.jsoup + jsoup + ${jsoup-version} + + + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + + + + org.apache.commons + commons-lang3 + ${commons-lang3-version} + + + commons-codec + commons-codec + ${commons-codec-version} + + + + + org.springframework.boot + spring-boot-starter-websocket + + + + + com.github.briandilley.jsonrpc4j + jsonrpc4j + 1.5.3 + + + + + com.aliyun + aliyun-java-sdk-core + 4.0.3 + + + + + \ No newline at end of file diff --git a/blockchain-common/blockchain-common-tx/pom.xml b/blockchain-common/blockchain-common-tx/pom.xml new file mode 100644 index 0000000..20be5a9 --- /dev/null +++ b/blockchain-common/blockchain-common-tx/pom.xml @@ -0,0 +1,45 @@ + + + + blockchain-common + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + blockchain-common-tx + + + + 4.2.0-SNAPSHOT + 4.1.12.Final + + + + + com.codingapi + transaction-springcloud + ${lcn.last.version} + + + org.slf4j + * + + + + + com.codingapi + tx-plugins-db + ${lcn.last.version} + + + org.slf4j + * + + + + + + \ No newline at end of file diff --git a/blockchain-common/blockchain-common-tx/src/main/resources/README.md b/blockchain-common/blockchain-common-tx/src/main/resources/README.md new file mode 100644 index 0000000..fd4d267 --- /dev/null +++ b/blockchain-common/blockchain-common-tx/src/main/resources/README.md @@ -0,0 +1,8 @@ +##分布式事务配置: +如果模块需要分布式事务 +1. 引入pom依赖 + + com.blockchain + blockchain-common-tx + 1.0-SNAPSHOT + diff --git a/blockchain-common/pom.xml b/blockchain-common/pom.xml new file mode 100644 index 0000000..8624ef4 --- /dev/null +++ b/blockchain-common/pom.xml @@ -0,0 +1,22 @@ + + + + blockchain + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + blockchain-common + 1.0-SNAPSHOT + pom + + blockchain-common-base + blockchain-common-pom + blockchain-common-tx + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-base/pom.xml b/blockchain-server/blockchain-server-base/pom.xml new file mode 100644 index 0000000..e071ff6 --- /dev/null +++ b/blockchain-server/blockchain-server-base/pom.xml @@ -0,0 +1,15 @@ + + + + blockchain-server + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + blockchain-server-base + 1.0-SNAPSHOT + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/BaseConf.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/BaseConf.java new file mode 100644 index 0000000..d441c78 --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/BaseConf.java @@ -0,0 +1,8 @@ +package com.blockchain.server.base; + +/** + * @author huangxl + * @create 2018-11-12 13:53 + */ +public class BaseConf { +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/annotation/BypassedFeign.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/annotation/BypassedFeign.java new file mode 100644 index 0000000..ce03cac --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/annotation/BypassedFeign.java @@ -0,0 +1,13 @@ +package com.blockchain.server.base.annotation; + +import java.lang.annotation.*; + +/*** + * 用于注解不需要处理feign返回值的类或方法 + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface BypassedFeign { + String value() default ""; +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/aop/FeignAop.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/aop/FeignAop.java new file mode 100644 index 0000000..118111e --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/aop/FeignAop.java @@ -0,0 +1,68 @@ +package com.blockchain.server.base.aop; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.enums.BaseResultEnums; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.common.base.exception.RPCException; +import com.blockchain.server.base.annotation.BypassedFeign; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +@Aspect +@Component +public class FeignAop { + private Logger LOG = LoggerFactory.getLogger(getClass()); + + //Feign调用成功返回码 + private static final int REQUEST_SUCCESS = BaseConstant.REQUEST_SUCCESS; + + /*** + * 设置切入点 + * 切所有模块的Feign包的所有方法 + */ + @Pointcut("execution(* com.blockchain.server.*.feign..*.*(..))") + public void handleResultDTO() { + } + + /*** + * 请求结束返回时处理 + * @param resultDTO + */ + @AfterReturning(returning = "resultDTO", pointcut = "handleResultDTO()") + public void doAfterReturning(JoinPoint joinPoint, ResultDTO resultDTO) { + //调用的方法名 + String methodName = joinPoint.getSignature().getName(); + //调用的类名 + String simpleName = joinPoint.getTarget().getClass().getSimpleName(); + //获取方法对象 + Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); + //判断该方法上注解是否存在 + boolean isAnno = method.isAnnotationPresent(BypassedFeign.class); + LOG.info(method.getName() + resultDTO.toString()); + //如果存在注解,则不处理 + if (isAnno) { + LOG.error("BypassedFeign不处理的内部接口:返回信息{},类名{},方法名{}", resultDTO.toString(), simpleName, methodName); + return; + } + //返回值为空 + if (resultDTO == null) { + LOG.error("feign返回为空:类名{},方法名{}", simpleName, methodName); + throw new BaseException(BaseResultEnums.BUSY); + } + //RPC调用返回码不等于200,抛出异常 + if (resultDTO.getCode() != REQUEST_SUCCESS) { + LOG.error("返回信息{},类名{},方法名{}", resultDTO.toString(), simpleName, methodName); + throw new RPCException(resultDTO); + } + } + +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/aop/MethodAop.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/aop/MethodAop.java new file mode 100644 index 0000000..35c3ca4 --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/aop/MethodAop.java @@ -0,0 +1,66 @@ +package com.blockchain.server.base.aop; + +import com.blockchain.common.base.util.JsonUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.util.Arrays; + +@Aspect +@Component +public class MethodAop { + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Pointcut("execution(* com.blockchain.server.*.service.*.update*(..))," + + "execution(* com.blockchain.server.*.service.*.delete*(..))") + public void aopPoint() { + } + + @Around("aopPoint()") + public Object around(ProceedingJoinPoint joinPoint) throws Throwable { + /** 获取方法名 */ + String methodName = joinPoint.getSignature().getName(); + logger.info("=================== {}方法开始执行 ===================", methodName); + logger.info("=================== 执行的参数有:{} ===================", Arrays.toString(joinPoint.getArgs())); + long start = System.currentTimeMillis(); + Object proceed = joinPoint.proceed(); + long end = System.currentTimeMillis(); + logger.info("=================== {}方法执行时使用了{}毫秒 ===================", methodName, (end - start)); + + logger.info("=================== {}方法执行后返回的数据是{} ===================", JsonUtils.objectToJson(proceed)); + + logger.info("=================== {}方法结束执行 ===================", methodName); + + return proceed; + } + + @Pointcut("execution(* com.blockchain.server.*.service.*.*.*(..))") + public void aopPoint2() { + } + + @AfterThrowing(throwing = "ex", value = "aopPoint2()") + public void errorAop(JoinPoint joinPoint, Throwable ex) { + /* 出现异常的类名 */ + String className = joinPoint.getTarget().getClass().getSimpleName(); + /*出现异常的方法名*/ + String methodName = joinPoint.getSignature().getName(); + /*传递的参数*/ + Object[] args = joinPoint.getArgs(); + /*异常类*/ + String exName = ex.getClass().getName(); + /*异常信息*/ + String exMsg = ex.getMessage(); + + Object[] params = {className, methodName, exName, args, exMsg}; + + logger.error("{}类的{}出现了{}异常,参数是:{},异常信息是:{}", params); +// logger.error(exMsg, ex); + } +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/DateConvertConfig.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/DateConvertConfig.java new file mode 100644 index 0000000..c0f957d --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/DateConvertConfig.java @@ -0,0 +1,26 @@ +package com.blockchain.server.base.conf; + +import com.blockchain.server.base.interceptor.StringToDateConverter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; + +import javax.annotation.PostConstruct; + +@Configuration +public class DateConvertConfig { + @Autowired + private RequestMappingHandlerAdapter handlerAdapter; + + @PostConstruct + public void addDateConvert() { + ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) handlerAdapter.getWebBindingInitializer(); + if (initializer != null) { + System.out.println("===============DateConvertConfig==================="); + GenericConversionService conversionService = (GenericConversionService) initializer.getConversionService(); + conversionService.addConverter(new StringToDateConverter()); + } + } +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/FeginConf.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/FeginConf.java new file mode 100644 index 0000000..befbfb3 --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/FeginConf.java @@ -0,0 +1,32 @@ +package com.blockchain.server.base.conf; + +import feign.RequestInterceptor; +import feign.RequestTemplate; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.util.Enumeration; + +@Configuration +public class FeginConf implements RequestInterceptor { + + @Override + public void apply(RequestTemplate requestTemplate) { + requestTemplate.header("X-Real-IP", "127.0.0.1"); + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (attributes == null) { + return; + } + HttpServletRequest request = attributes.getRequest(); + Enumeration headerNames = request.getHeaderNames(); + if (headerNames != null) { + while (headerNames.hasMoreElements()) { + String name = headerNames.nextElement(); + String values = request.getHeader(name); + requestTemplate.header(name, values); + } + } + } +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/GlobalExceptionHandle.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/GlobalExceptionHandle.java new file mode 100644 index 0000000..6ad42fa --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/GlobalExceptionHandle.java @@ -0,0 +1,73 @@ +package com.blockchain.server.base.conf; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.common.base.exception.RPCException; +import com.blockchain.common.base.util.HttpRequestUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Date; + +/** + * @author huangxl + * 全局拦截器 + */ +@RestController +@ControllerAdvice +public class GlobalExceptionHandle { + private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandle.class); + + /** + * 处理参数异常 + * + * @param e 异常信息 + * @return 返回内容 + */ + @ExceptionHandler(BaseException.class) + public ResultDTO handleInvalidArgumentException(BaseException e) { + e.printStackTrace(); + return new ResultDTO(e.getCode(), e.getMsg(), null); + } + + /** + * 处理rpc调用异常 + * + * @param e 异常信息 + * @return 返回内容 + */ + @ExceptionHandler(RPCException.class) + public ResultDTO handleRPCException(RPCException e) { + e.printStackTrace(); + return new ResultDTO(e.getCode(), e.getMsg(), null); + } + + /** + * 处理未知异常 + * + * @param e 异常信息 + * @return 返回内容 + */ + @ExceptionHandler(Exception.class) + public ResultDTO handleUnknownException(Exception e) { + LOG.error("{}系统抛出异常,异常是:{},异常信息是:{}", new Date(), e.getClass().getName(), e.getMessage()); + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); + if (request != null) { + LOG.error("请求路径:{}", request.getRequestURL().toString()); + } + if (response != null) { + LOG.error("响应头:{}", response.getHeader("Content-Type")); + } + e.printStackTrace(); + BaseException base = new BaseException(); + return new ResultDTO(base.getCode(), base.getMsg(), null); + } +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/InnerInterceptorConf.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/InnerInterceptorConf.java new file mode 100644 index 0000000..b103cb8 --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/InnerInterceptorConf.java @@ -0,0 +1,27 @@ +package com.blockchain.server.base.conf; + +import com.blockchain.server.base.interceptor.InnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +//@Configuration +public class InnerInterceptorConf implements WebMvcConfigurer { + @Bean + public InnerInterceptor innerInterceptor() { + return new InnerInterceptor(); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(innerInterceptor()) + .addPathPatterns("/inner/**"); + } + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**").allowedOrigins("*"); + } +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/RedisConf.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/RedisConf.java new file mode 100644 index 0000000..bf83cf4 --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/RedisConf.java @@ -0,0 +1,91 @@ +package com.blockchain.server.base.conf; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; + +import java.lang.reflect.Method; +import java.time.Duration; + +/** + * @author huangxl + * Created on 2018/8/26 + */ +@Configuration +@EnableCaching +@EnableRedisHttpSession +public class RedisConf extends CachingConfigurerSupport { + @Value("${spring.redis.cache.timeout-seconds}") + private long redisDefaultTtl; + + @Bean + public KeyGenerator keyGenerator() { + return new KeyGenerator() { + @Override + public Object generate(Object target, Method method, Object... params) { + StringBuilder sb = new StringBuilder(); + sb.append(target.getClass().getName()); + sb.append(method.getName()); + for (Object obj : params) { + sb.append(obj.toString()); + } + return sb.toString(); + } + }; + } + + @Bean + public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { + RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofMillis(redisDefaultTtl)); // 设置缓存有效期一小时 + return RedisCacheManager + .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory)) + .cacheDefaults(redisCacheConfiguration).build(); + } + + /** + * 设置redis 键、值的序列化,防止乱码 + */ + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + + // 使用Jackson2JsonRedisSerialize 替换默认序列化 + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); + + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + + jackson2JsonRedisSerializer.setObjectMapper(objectMapper); + + // 设置value的序列化规则和 key的序列化规则 + redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + + redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer); + redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); + + redisTemplate.setDefaultSerializer(jackson2JsonRedisSerializer); + redisTemplate.setEnableDefaultSerializer(true); + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } + +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/RestTemplateConf.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/RestTemplateConf.java new file mode 100644 index 0000000..c606d77 --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/RestTemplateConf.java @@ -0,0 +1,24 @@ +package com.blockchain.server.base.conf; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +/** + * RestTemplate配置类 + */ +@Configuration +public class RestTemplateConf { + @Bean + public RestTemplate restTemplate(ClientHttpRequestFactory factory){ + return new RestTemplate(factory); + } + @Bean + public ClientHttpRequestFactory simpleClientHttpRequestFactory(){ + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + factory.setReadTimeout(5000);//单位为ms + factory.setConnectTimeout(5000);//单位为ms + return factory; + } +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/SpringCloudConf.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/SpringCloudConf.java new file mode 100644 index 0000000..3e1c4f1 --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/SpringCloudConf.java @@ -0,0 +1,19 @@ +package com.blockchain.server.base.conf; + +import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Configuration; + +/** + * @author huangxl + * @create 2018-11-13 13:48 + */ +@Configuration +@EnableEurekaClient //注册客户端 +@EnableFeignClients(basePackages = "com.blockchain.server.*.feign") +@EnableCircuitBreaker //熔断器 +@EnableDiscoveryClient //服务发现 +public class SpringCloudConf { +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/SwaggerConf.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/SwaggerConf.java new file mode 100644 index 0000000..a63656d --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/SwaggerConf.java @@ -0,0 +1,40 @@ +package com.blockchain.server.base.conf; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + *

Desciption:

+ * CreateTime: 2018/3/19 11:26 + * User: XianChaoWei + * Version: V1.0 + */ +@Configuration +@EnableSwagger2 +public class SwaggerConf { + + @Bean + public Docket createRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo()) + .select() + .apis(RequestHandlerSelectors.basePackage("com.blockchain.server")) + .paths(PathSelectors.any()) + .build(); + } + + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("接口文档API") + .description("接口文档") + .version("1.0") + .build(); + } +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/TkMybatisConf.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/TkMybatisConf.java new file mode 100644 index 0000000..f765153 --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/TkMybatisConf.java @@ -0,0 +1,17 @@ +package com.blockchain.server.base.conf; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import tk.mybatis.spring.mapper.MapperScannerConfigurer; + +@Configuration +public class TkMybatisConf { + private static final String MAPPER_PACKAGE = "com.blockchain.server.*.mapper"; + + @Bean + public MapperScannerConfigurer mapperScannerConfigurer(){ + MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); + mapperScannerConfigurer.setBasePackage(MAPPER_PACKAGE); + return mapperScannerConfigurer; + } +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/XssFilterConfig.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/XssFilterConfig.java new file mode 100644 index 0000000..3b4a336 --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/conf/XssFilterConfig.java @@ -0,0 +1,45 @@ +package com.blockchain.server.base.conf; + +import com.blockchain.server.base.filter.XssFilter; +import com.google.common.collect.Maps; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.servlet.DispatcherType; +import java.util.Map; + +/** + * XssFilter配置 + */ +@Configuration +public class XssFilterConfig +{ + @Value("${xss.enabled}") + private String enabled; + + @Value("${xss.excludes}") + private String excludes; + + @Value("${xss.urlPatterns}") + private String urlPatterns; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + public FilterRegistrationBean xssFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); + registration.setName("xssFilter"); + registration.setOrder(Integer.MAX_VALUE); + Map initParameters = Maps.newHashMap(); + initParameters.put("excludes", excludes); + initParameters.put("enabled", enabled); + registration.setInitParameters(initParameters); + return registration; + } +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/controller/BaseController.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/controller/BaseController.java new file mode 100644 index 0000000..2753420 --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/controller/BaseController.java @@ -0,0 +1,28 @@ +package com.blockchain.server.base.controller; + +import com.blockchain.common.base.dto.PageDTO; +import com.blockchain.common.base.dto.ResultDTO; +import com.github.pagehelper.PageInfo; + +import java.util.List; + +public class BaseController { + + /** + * 构造分页对象 + *

+ * 构造控制层返回分页对象 + *

+ * 后台控制层统一返回该方法 + * + * @param list + * @return + */ + protected ResultDTO generatePage(List list) { + //通过构造器生成一个分页信息对象 + PageInfo pageInfo = new PageInfo(list); + //构造返回前端分页信息DTO + PageDTO pageDTO = new PageDTO(pageInfo.getTotal(), pageInfo.getPageNum(), pageInfo.getPageSize(), pageInfo.getList()); + return ResultDTO.requstSuccess(pageDTO); + } +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/filter/XssFilter.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/filter/XssFilter.java new file mode 100644 index 0000000..81b5e45 --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/filter/XssFilter.java @@ -0,0 +1,92 @@ +package com.blockchain.server.base.filter; + +import org.apache.commons.lang3.StringUtils; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 防止XSS攻击的过滤器 + */ +public class XssFilter implements Filter +{ + /** + * 排除链接 + */ + public List excludes = new ArrayList<>(); + + /** + * xss过滤开关 + */ + public boolean enabled = false; + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + String tempExcludes = filterConfig.getInitParameter("excludes"); + String tempEnabled = filterConfig.getInitParameter("enabled"); + if (StringUtils.isNotEmpty(tempExcludes)) + { + String[] url = tempExcludes.split(","); + for (int i = 0; url != null && i < url.length; i++) + { + excludes.add(url[i]); + } + } + if (StringUtils.isNotEmpty(tempEnabled)) + { + enabled = Boolean.valueOf(tempEnabled); + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) + { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) + { + if (!enabled) + { + return true; + } + if (excludes == null || excludes.isEmpty()) + { + return false; + } + String url = request.getServletPath(); + for (String pattern : excludes) + { + Pattern p = Pattern.compile("^" + pattern); + Matcher m = p.matcher(url); + if (m.find()) + { + return true; + } + } + return false; + } + + @Override + public void destroy() + { + + } + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/filter/XssHttpServletRequestWrapper.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/filter/XssHttpServletRequestWrapper.java new file mode 100644 index 0000000..8da89c8 --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/filter/XssHttpServletRequestWrapper.java @@ -0,0 +1,40 @@ +package com.blockchain.server.base.filter; + +import org.jsoup.Jsoup; +import org.jsoup.safety.Whitelist; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; + +/** + * XSS过滤处理 + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper +{ + + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) + { + super(request); + } + + @Override + public String[] getParameterValues(String name) + { + String[] values = super.getParameterValues(name); + if (values != null) + { + int length = values.length; + String[] escapseValues = new String[length]; + for (int i = 0; i < length; i++) + { + // 防xss攻击和过滤前后空格 + escapseValues[i] = Jsoup.clean(values[i], Whitelist.relaxed()).trim(); + } + return escapseValues; + } + return super.getParameterValues(name); + } +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/interceptor/InnerInterceptor.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/interceptor/InnerInterceptor.java new file mode 100644 index 0000000..604cc2d --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/interceptor/InnerInterceptor.java @@ -0,0 +1,34 @@ +package com.blockchain.server.base.interceptor; + +import com.blockchain.common.base.enums.BaseResultEnums; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.server.base.conf.GlobalExceptionHandle; +import com.blockchain.server.base.redis.IpInnerCache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class InnerInterceptor extends HandlerInterceptorAdapter { + + private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandle.class); + + @Autowired + private IpInnerCache innerCache; + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + //经过nginx代理后用户的真实ip + String ip = request.getHeader("X-Real-IP"); + System.out.println("------------内部访问接口IP-----------"+ip); + String innerIps = innerCache.getIpInner(); + //若不是内部访问返回链接错误 + if(innerIps != null && !innerIps.equals("") && innerIps.indexOf(ip) == -1){ + LOG.info("被拦截ip:"+ ip); + throw new BaseException(BaseResultEnums.NOT_FOUND); + } + return super.preHandle(request, response, handler); + } +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/interceptor/LoginInterceptor.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/interceptor/LoginInterceptor.java new file mode 100644 index 0000000..ce33248 --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/interceptor/LoginInterceptor.java @@ -0,0 +1,20 @@ +package com.blockchain.server.base.interceptor; + +import com.blockchain.common.base.util.SSOHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class LoginInterceptor extends HandlerInterceptorAdapter { + @Autowired + private RedisTemplate redisTemplate; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { +// SSOHelper.checkUser(redisTemplate, request); + return super.preHandle(request, response, handler); + } +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/interceptor/StringToDateConverter.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/interceptor/StringToDateConverter.java new file mode 100644 index 0000000..0e9fe4d --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/interceptor/StringToDateConverter.java @@ -0,0 +1,46 @@ +package com.blockchain.server.base.interceptor; + +import com.blockchain.common.base.enums.BaseResultEnums; +import com.blockchain.common.base.exception.BaseException; +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.convert.converter.Converter; + +import java.text.SimpleDateFormat; +import java.util.Date; + +public class StringToDateConverter implements Converter { + private static final String dateFormat = "yyyy-MM-dd HH:mm:ss"; + private static final String shortDateFormat = "yyyy-MM-dd"; + private static final String dateFormat2 = "yyyy/MM/dd HH:mm:ss"; + private static final String shortDateFormat2 = "yyyy/MM/dd"; + @Override + public Date convert(String source) { + if (StringUtils.isBlank(source)) { + return null; + } + source = source.trim(); + try { + SimpleDateFormat formatter; + if (source.contains("-")) { + if (source.contains(":")) { + formatter = new SimpleDateFormat(dateFormat); + } else { + formatter = new SimpleDateFormat(shortDateFormat); + } + Date dtDate = formatter.parse(source); + return dtDate; + } else if (source.contains("/")) { + if (source.contains(":")) { + formatter = new SimpleDateFormat(dateFormat2); + } else { + formatter = new SimpleDateFormat(shortDateFormat2); + } + Date dtDate = formatter.parse(source); + return dtDate; + } + } catch (Exception e) { + throw new BaseException(BaseResultEnums.NO_LOGIN); + } + throw new RuntimeException(String.format("parser %s to Date fail", source)); + } +} diff --git a/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/redis/IpInnerCache.java b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/redis/IpInnerCache.java new file mode 100644 index 0000000..8061730 --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/java/com/blockchain/server/base/redis/IpInnerCache.java @@ -0,0 +1,28 @@ +package com.blockchain.server.base.redis; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +/** + * \*

Desciption:

+ * \* CreateTime: 2019/2/12 10:08 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +@Component +public class IpInnerCache { + private static final String IP_INNER_CACHE = "ip:inner"; + + @Autowired + private RedisTemplate redisTemplate; + + public String getIpInner(){ + if (redisTemplate.hasKey(IP_INNER_CACHE)) {//如果有token信息,更新token的有效时间 + String ipInner = redisTemplate.opsForValue().get(IP_INNER_CACHE).toString(); + return ipInner; + } + return null; + } +} diff --git a/blockchain-server/blockchain-server-base/src/main/resources/README.md b/blockchain-server/blockchain-server-base/src/main/resources/README.md new file mode 100644 index 0000000..e7cbdca --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/resources/README.md @@ -0,0 +1,35 @@ +##使用: +将依赖加入pom文件 +``` + + + com.blockchain + blockchain-server-base + 1.0-SNAPSHOT + + +``` + +##注意事项: +1. 需要将MessagesBundle_default.properties内容拷贝到相应国际化文件,目录位于resource下面的i18n文件夹 +2. 服务端使用@SpringBootApplication(scanBasePackageClasses={BaseConf.class})将所有配置扫描并交给到spring容器管理 +3. 如果不需要Xss拦截,可以使用@SpringBootApplication(scanBasePackageClasses={BaseConf.class},exclude={XssConfig.class}) +4. 每个微服务应该设置自己独立的登录拦截器,指定哪些接口需要登录才能使用,哪些接口不需要 +5. 对于分布式事务,发起方的service需要加上注解@TxTransaction(isStart = true), + 接收方的service需要实现ITxTransaction,并使用@Transactional +6. 微服务文件目录固定为 +``` + com.blockchain.server.服务名 + --common + --conf 登录拦截器、自定义配置等 + -- + ... + --controller + --service + --feign + --mapper + --entity + --dto + ... +``` + diff --git a/blockchain-server/blockchain-server-base/src/main/resources/i18n/MessgesBundle_default.properties b/blockchain-server/blockchain-server-base/src/main/resources/i18n/MessgesBundle_default.properties new file mode 100644 index 0000000..f5db252 --- /dev/null +++ b/blockchain-server/blockchain-server-base/src/main/resources/i18n/MessgesBundle_default.properties @@ -0,0 +1,5 @@ +#\u516C\u5171 +ERROR_PARAMETER_NOTNULL=\u53C2\u6570\u4E0D\u80FD\u4E3A\u7A7A,\u8BF7\u68C0\u67E5\u53C2\u6570 +RESULT_ERROR_MSG=\u672A\u77E5\u5F02\u5E38 +LOGIN_INVALID_MSG=\u767B\u5F55\u4FE1\u606F\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55 +SYSTEM_BUSY_MSG=\u7F51\u7EDC\u7E41\u5FD9\uFF01 \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/pom.xml b/blockchain-server/blockchain-server-btc/pom.xml new file mode 100644 index 0000000..278a64d --- /dev/null +++ b/blockchain-server/blockchain-server-btc/pom.xml @@ -0,0 +1,35 @@ + + + + blockchain-server + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + blockchain-server-btc + + + + com.blockchain + blockchain-server-base + 1.0-SNAPSHOT + + + com.blockchain + blockchain-common-tx + 1.0-SNAPSHOT + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/BtcApplication.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/BtcApplication.java new file mode 100644 index 0000000..12890bf --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/BtcApplication.java @@ -0,0 +1,49 @@ +package com.blockchain.server.btc; + +import com.blockchain.server.base.BaseConf; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.context.annotation.Bean; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; + +@ServletComponentScan +@SpringBootApplication(scanBasePackageClasses = {BaseConf.class, BtcApplication.class}) +public class BtcApplication { + public static void main(String[] args) { +// initSystemConfig(); + SpringApplication.run(BtcApplication.class, args); + } + + /** + * 初始化系统配置 + */ + private static void initSystemConfig() { + System.setProperty("log.root", "ALL,CONSOLE,info,error,DEBUG"); + System.setProperty("service.id", "dapp-btc-server"); + } + +// @Bean +// public Docket createRestApi() { +// return new Docket(DocumentationType.SWAGGER_2) +// .apiInfo(apiInfo()) +// .select() +// .apis(RequestHandlerSelectors.basePackage("com.blockchain.server.btc.controller")) +// .paths(PathSelectors.any()) +// .build(); +// } +// +// private ApiInfo apiInfo() { +// return new ApiInfoBuilder() +// .title("比特币托管账户系统(dapp-eth-server)RESTful APIs") +// .description("") +// .termsOfServiceUrl("") +// .version("1.0") +// .build(); +// } +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/config/InterceptorConf.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/config/InterceptorConf.java new file mode 100644 index 0000000..6e0e02c --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/config/InterceptorConf.java @@ -0,0 +1,32 @@ +package com.blockchain.server.btc.common.config; + +import com.blockchain.server.base.interceptor.LoginInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class InterceptorConf implements WebMvcConfigurer { + @Bean + public LoginInterceptor loginInterceptor() { + return new LoginInterceptor(); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(loginInterceptor()) + .addPathPatterns("/**") + .excludePathPatterns("/inner/**") + .excludePathPatterns("/webjars/**") + .excludePathPatterns("/swagger-ui.html") + .excludePathPatterns("/swagger-resources/**") + .excludePathPatterns("/v2/api-docs/**"); + } + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**").allowedOrigins("*"); + } +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcAddressConstans.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcAddressConstans.java new file mode 100644 index 0000000..8fe4c89 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcAddressConstans.java @@ -0,0 +1,14 @@ +package com.blockchain.server.btc.common.constants; + +/** + * @author hugq + * @date 2019/2/16 0016 16:31 + */ +public class BtcAddressConstans { + + //redis 存储地址列表的key值,用于解析用户充值区块信息 + public static final String BTC_ADDRESS_KEY = "btc:adderss:addressSet"; + //redis 存储地址列表的时间,天数 + public static final int BTC_ADDRESS_TIME = 30; + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcApplicationConstans.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcApplicationConstans.java new file mode 100644 index 0000000..030f732 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcApplicationConstans.java @@ -0,0 +1,13 @@ +package com.blockchain.server.btc.common.constants; + +/** + * @author hugq + * @date 2019/2/16 0016 16:31 + */ +public class BtcApplicationConstans { + + //钱包类型 币币交易 + public static final String TYPE_CCT = "CCT"; + + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcBlockNumberConstans.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcBlockNumberConstans.java new file mode 100644 index 0000000..971959f --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcBlockNumberConstans.java @@ -0,0 +1,20 @@ +package com.blockchain.server.btc.common.constants; + +import com.blockchain.common.base.util.DateTimeUtils; + +/** + * @author hugq + * @date 2019/2/16 0016 16:31 + */ +public class BtcBlockNumberConstans { + //同步区块高度状态 等待 + public static final String STATUS_W = "W"; + //同步区块高度状态 失败 + public static final String STATUS_N = "N"; + //同步区块高度状态 成功 + public static final String STATUS_Y = "Y"; + + //查询遗漏区块高度时间差 毫秒数 + public static final long TIME_DIFFERENCE = 10 * DateTimeUtils.MILLISECOND_OF_MINUTE; + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcConfigConstants.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcConfigConstants.java new file mode 100644 index 0000000..fa6c13e --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcConfigConstants.java @@ -0,0 +1,15 @@ +package com.blockchain.server.btc.common.constants; + +/** + * BTC Config配置常量参数 + */ +public class BtcConfigConstants { + + public static final String MODULE_TYPE = "btc"; // 所属于钱包模块 + public static final int STATUS_DISABLED = 0; // 禁用 + public static final int STATUS_NORMAL = 1; // 启用 + + public static final String GAS_CONFIG = "btc_gas_config_"; + + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcConstans.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcConstans.java new file mode 100644 index 0000000..7931280 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcConstans.java @@ -0,0 +1,12 @@ +package com.blockchain.server.btc.common.constants; + +/** + * @author hugq + * @date 2019/2/16 0016 16:31 + */ +public class BtcConstans { + //btc币种id + public static final int BTC_PROPERTY_ID = 0; + //btc币种名称 + public static final String BTC_SYMBOL = "BTC"; +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcTransferConstans.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcTransferConstans.java new file mode 100644 index 0000000..13500a5 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/BtcTransferConstans.java @@ -0,0 +1,38 @@ +package com.blockchain.server.btc.common.constants; + +/** + * @author hugq + * @date 2019/2/16 0016 16:31 + */ +public class BtcTransferConstans { + + //交易类型 充值 + public static final String TYPE_IN = "IN"; + //交易类型 提现 + public static final String TYPE_OUT = "OUT"; + //交易类型 币币交易 + public static final String TYPE_CCT = "CCT"; + //交易类型 手续费 + public static final String TYPE_GAS = "GAS"; + //交易类型 转内快速转账 + public static final String TYPE_FAST = "FAST"; + + + //交易状态 失败 + public static final int STATUS_FILE = 0; + //交易状态 成功 + public static final int STATUS_SUCCESS = 1; + //交易状态 待初审 + public static final int STATUS_FIRST_TRIAL = 2; + //交易状态 待复审 + public static final int STATUS_SECOND_TRIAL = 3; + //交易状态 待出币 + public static final int STATUS_STAY_OUT = 4; + //交易状态 已出币 + public static final int STATUS_ALREADY_OUT = 5; + //交易状态 出币失败 + public static final int STATUS_OUT_FILE = 6; + //交易状态 审核不通过 + public static final int STATUS_TRIAL_FILE = 7; + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/UsdtConstans.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/UsdtConstans.java new file mode 100644 index 0000000..3705414 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/constants/UsdtConstans.java @@ -0,0 +1,27 @@ +package com.blockchain.server.btc.common.constants; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; + +@Component +public class UsdtConstans { + + //注入配置的usdt币种id,避免忘记修改 + @Value("${btc.usdt_property_id}") + private Integer USDT_PROPERTY_ID_CONF; + + @PostConstruct + public void init() { + this.USDT_PROPERTY_ID = USDT_PROPERTY_ID_CONF; + } + + + //usdt币种id 默认正式网31 + public static int USDT_PROPERTY_ID = 31; + + //usdt币种名称 + public static final String USDT_SYMBOL = "USDT"; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/enums/BtcEnums.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/enums/BtcEnums.java new file mode 100644 index 0000000..bb77d9d --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/enums/BtcEnums.java @@ -0,0 +1,73 @@ +package com.blockchain.server.btc.common.enums; + +public enum BtcEnums { + ADDRESS_ERROR(7000, "请输入有效的地址", "Please enter a valid address.", "請輸入有效的地址"), + AMOUNT_NULL(7001, "请输入数量", "Please input quantity.", "請輸入數量"), + SENDTOADDRESS_ERROR(7002, "交易失败", "Transaction failure.", "交易失敗"), + GET_NEW_ADDRESS_ERROR(7003, "生成地址失败", "Request success", "生成地址失敗"), + LIST_UNSPENT_ERROR(7004, "获取UTXO失败", "Failed to get UTXO", "獲取UTXO失敗"), + CREATE_WALLET_ERROR(7005, "创建钱包失败", "Failed to create wallet.", "創建錢包失敗"), + PARSE_TRANSFER_IN_ERROR(7006, "解析区块充值信息到数据库失败", "Failed to parse block recharge information to database.", "解析區塊充值信息到數據庫失敗"), + INSERT_BLOCKNUMBER_ERROR(7007, "插入同步区块号失败", "Failed to insert synchronized block number.", "插入同步區塊號失敗"), + WITHDRAW_ERROR(7008, "提现失败", "Failed to withdrawal.", "提現失敗"), + FREEBALANCE_NOT_ENOUGH(7009, "钱包可用余额不足", "Insufficient balance available in wallet.", "錢包可用餘額不足"), + INSERT_AUDITING_ERROR(7010, "插入审核记录失败", "Failed to insert audit record.", "插入審覈記錄失敗"), + INSERT_WALLET_OUT_ERROR(7011, "插入提现资金钱包失败", "Failed to insert withdrawal fund wallet.", "插入提現資金錢包失敗"), + UPDATE_BLOCK_NUMBER_STATUS_ERROR(7012, "修改同步区块状态失败", "Failed to modify synchronized block state.", "修改同步區塊狀態失敗"), + TRANSFER_ERROR(7013, "交易失败", "Failed to transact.", "交易失敗"), + BALANCE_NOT_ENOUGH(7014, "钱包余额不足", "Insufficient balance of wallet.", "錢包餘額不足"), + INEXISTENCE_TOKENNAME(7015, "不识别该币种名称", "The currency name is not recognized.", "不識別該幣種名稱"), + INEXISTENCE_TOKENID(7016, "不识别该币种类型", "The currency type is not recognized.", "不識別該幣種類型"), + INEXISTENCE_WALLETTYPE(7017, "该钱包类型不存在", "The wallet type does not exist.", "該錢包類型不存在"), + LOW_WITHDRAW_AMOUNT_ERROR(7018, "提现数量过低", "The withdrawal amount is too low.", "提現數量過低"), + NO_WALLET_ERROR(7019, "钱包信息未找到", "Wallet information not found", "錢包信息未找到"), + SERVER_IS_TOO_BUSY(15000, "服务器繁忙,请稍后重试", "The server is busy, please try again later", "服務器繁忙,請稍後重試"), + INEXISTENCE_WALLET(7017, "该钱包不存在", "The wallet does not exist.", "該錢包不存在"), + + ; + + private int code; + private String cnmsg; + private String enMsg; + private String hkmsg; + + BtcEnums(int code, String cnmsg, String enMsg, String hkmsg) { + this.code = code; + this.cnmsg = cnmsg; + this.enMsg = enMsg; + this.hkmsg = hkmsg; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getHkmsg() { + return hkmsg; + } + + public void setHkmsg(String hkmsg) { + this.hkmsg = hkmsg; + } + + public String getEnMsg() { + return enMsg; + } + + public void setEnMsg(String enMsg) { + this.enMsg = enMsg; + } + + public String getCnmsg() { + return cnmsg; + } + + public void setCnmsg(String cnmsg) { + this.cnmsg = cnmsg; + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/exception/BtcException.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/exception/BtcException.java new file mode 100644 index 0000000..c33aafe --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/exception/BtcException.java @@ -0,0 +1,40 @@ +package com.blockchain.server.btc.common.exception; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.btc.common.enums.BtcEnums; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +public class BtcException extends BaseException { + + public BtcException(BtcEnums rs) { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + //可能是定时器调用,避免获取request空指针 + if (servletRequestAttributes == null) { + this.code = rs.getCode(); + this.msg = rs.getCnmsg(); + } else { + HttpServletRequest request = servletRequestAttributes.getRequest(); + String userLocale = HttpRequestUtil.getUserLocale(request); + String msg = ""; + switch (userLocale) { + case BaseConstant.USER_LOCALE_EN_US: + msg = rs.getEnMsg(); + break; + case BaseConstant.USER_LOCALE_ZH_HK: + msg = rs.getHkmsg(); + break; + default: + msg = rs.getCnmsg(); + break; + } + this.code = rs.getCode(); + this.msg = msg; + } + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/BtcAddressSetRedisUtils.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/BtcAddressSetRedisUtils.java new file mode 100644 index 0000000..bb5bea5 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/BtcAddressSetRedisUtils.java @@ -0,0 +1,53 @@ +package com.blockchain.server.btc.common.util; + +import com.blockchain.server.btc.common.constants.BtcAddressConstans; +import com.blockchain.server.btc.service.BtcWalletService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.Set; + +@Component +public class BtcAddressSetRedisUtils { + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private BtcWalletService btcWalletService; + + /** + * 保存地址集合到redis + * + * @param address 新增地址 + */ + public void insert(String address) { + redisTemplate.opsForSet().add(BtcAddressConstans.BTC_ADDRESS_KEY, address); + } + + /** + * 从redis中获取地址集合 + * + * @return + */ + public Set get() { + return redisTemplate.opsForSet().members(BtcAddressConstans.BTC_ADDRESS_KEY); + } + + /** + * 判断缓存中是否存在该地址 + * + * @param addr 地址 + * @return + */ + public boolean isExistsAddr(String addr) { + Set addressSet = get(); + if (addressSet == null || addressSet.size() == 0) { + addressSet = btcWalletService.getAllWalletAddr(); + redisTemplate.opsForSet().add(BtcAddressConstans.BTC_ADDRESS_KEY, addressSet.toArray()); + } + + return addressSet.contains(addr); + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/BtcBlockRedisUtils.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/BtcBlockRedisUtils.java new file mode 100644 index 0000000..0b7a023 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/BtcBlockRedisUtils.java @@ -0,0 +1,43 @@ +package com.blockchain.server.btc.common.util; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +@Component +public class BtcBlockRedisUtils { + @Autowired + private RedisTemplate redisTemplate; + + private static final String ETH_BLOCKCHAIN_OPT_HASH = "btc:blockchain:opt:block"; + + /** + * 根据区块高度,获取爬取的次数 + * + * @param blockHeight 区块高度 + * @return + */ + public int getBlockOptMapCount(int blockHeight) { + Integer val = (Integer) redisTemplate.opsForHash().get(ETH_BLOCKCHAIN_OPT_HASH, blockHeight); + return val != null ? val : 0; + } + + /** + * 爬取次数加1 + * + * @param blockHeight 区块高度 + */ + public void incrementBlockOptMapCount(int blockHeight) { + redisTemplate.opsForHash().increment(ETH_BLOCKCHAIN_OPT_HASH, blockHeight, 1); + } + + /** + * 移除区块的爬取记录 + * + * @param blockHeight 区块高度 + */ + public void delBlockOptMapCount(int blockHeight) { + redisTemplate.opsForHash().delete(ETH_BLOCKCHAIN_OPT_HASH, blockHeight); + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/BtcRASUtils.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/BtcRASUtils.java new file mode 100644 index 0000000..fd0ee6c --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/BtcRASUtils.java @@ -0,0 +1,60 @@ +package com.blockchain.server.btc.common.util; + +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Cipher; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +/** + * @author hugq + * @date 2019/2/21 10:17 + */ +public class BtcRASUtils { + //非对称密钥算法 + private static final String KEY_ALGORITHM = "RSA"; + + //公钥 + private static final byte[] PUBLIC_KEY = {48, -127, -97, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -127, -115, 0, 48, -127, -119, 2, -127, -127, 0, -128, 48, -25, 69, -64, -57, -59, -64, -116, -5, -4, -26, -5, 109, 17, 59, 68, -69, -16, -110, 19, -21, -78, 25, 77, -40, 18, 77, 2, 70, -45, 15, 112, 57, 111, 14, 78, -69, 18, -115, 21, 9, -38, 126, -107, -116, -124, -95, -116, 6, 18, 57, -73, -25, -9, 46, 69, 1, 127, -27, -92, 2, -119, 106, -33, -78, -35, -26, 72, -65, 116, 61, -53, 85, 94, 68, -84, -121, 55, -68, 77, -128, 6, -99, 81, -5, 80, 52, 68, 53, -27, 79, -14, 37, 41, -18, 70, 96, -65, -32, -82, 75, -23, -77, -39, -36, -46, 120, 7, 67, 101, 107, 58, 97, 66, 72, -52, -128, -62, 39, -121, -47, -73, 73, -100, 49, 9, 113, 2, 3, 1, 0, 1}; + + //私钥 + private static final byte[] PRIVATE_KEY = {48, -126, 2, 118, 2, 1, 0, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 4, -126, 2, 96, 48, -126, 2, 92, 2, 1, 0, 2, -127, -127, 0, -128, 48, -25, 69, -64, -57, -59, -64, -116, -5, -4, -26, -5, 109, 17, 59, 68, -69, -16, -110, 19, -21, -78, 25, 77, -40, 18, 77, 2, 70, -45, 15, 112, 57, 111, 14, 78, -69, 18, -115, 21, 9, -38, 126, -107, -116, -124, -95, -116, 6, 18, 57, -73, -25, -9, 46, 69, 1, 127, -27, -92, 2, -119, 106, -33, -78, -35, -26, 72, -65, 116, 61, -53, 85, 94, 68, -84, -121, 55, -68, 77, -128, 6, -99, 81, -5, 80, 52, 68, 53, -27, 79, -14, 37, 41, -18, 70, 96, -65, -32, -82, 75, -23, -77, -39, -36, -46, 120, 7, 67, 101, 107, 58, 97, 66, 72, -52, -128, -62, 39, -121, -47, -73, 73, -100, 49, 9, 113, 2, 3, 1, 0, 1, 2, -127, -128, 103, -35, 55, -59, -64, -119, 28, -91, 2, -106, 57, 55, 61, -120, 5, 106, 44, 42, -54, -92, -47, 23, 43, 90, 109, 68, 32, -81, -36, -92, 93, -26, 40, 91, -96, -85, -53, 6, -81, -27, 55, -94, -96, 49, -24, 33, -50, 100, -59, -5, 53, 81, 38, -68, -1, -3, -79, 83, -95, -71, 2, -58, 59, 103, -13, -78, -15, 56, 113, 53, -107, -98, 6, 54, -95, -110, -115, 88, 2, -122, -111, 9, 29, 22, -78, -21, 105, -95, -74, -62, 109, -31, -48, -75, 101, 112, 103, 26, 11, -126, 97, 48, 115, -74, 47, 122, -14, 97, -55, 77, 49, 7, -105, -87, 58, 21, -117, 98, -28, 27, -106, 113, -30, -28, 45, -101, -109, 1, 2, 65, 0, -7, -37, 63, 25, -106, 28, 88, 14, -22, -47, -26, -117, 4, 55, -10, 37, -73, 111, 75, 24, -123, 104, 42, -20, -46, -95, -119, -41, -82, -85, -11, -27, 41, -8, 38, 22, 112, -51, 35, 112, 23, -66, 4, -55, -6, 33, -121, -98, 35, 2, -64, -79, 118, -85, -83, -33, -103, -107, -33, 127, -52, -115, 93, 25, 2, 65, 0, -125, 87, -47, -128, 76, -6, -66, 113, -107, 27, -58, -46, -15, -54, 59, -74, -98, -95, 16, -43, 109, 81, -79, 26, -57, 120, -82, 95, 66, 47, 105, 49, -92, -28, 61, 81, -60, 115, -18, -84, 8, 99, 25, -123, -109, 46, -48, 115, -37, -13, 56, 6, -9, 36, -36, 112, -42, -8, 62, -87, 21, 70, -62, 25, 2, 65, 0, -16, -26, -89, 76, 48, 35, 91, -13, -26, 12, 67, 80, 61, -35, 7, 3, 14, 125, -53, -43, -12, -86, -98, -40, 127, -83, 40, -114, 63, -25, -92, -54, 51, 81, 2, -56, 24, 50, 113, -68, -99, -25, -92, 14, 105, -112, -14, -123, 82, 20, 81, 93, -55, -95, 117, -97, 101, 33, -49, -64, 20, -91, 39, -31, 2, 64, 7, 23, 44, -106, 50, -111, -82, -54, 78, -12, 106, -19, 100, 100, 56, -119, 9, 83, 68, -89, 96, -7, 114, 8, 50, 16, -113, -55, 80, -73, 98, -124, 109, -108, 108, -61, 7, 74, 2, -18, -126, -99, 102, -7, 81, 18, -53, -22, 21, 75, -78, 16, -98, 50, -3, 59, -110, 63, 96, -110, -100, 53, 111, -79, 2, 64, 80, -113, -19, 28, -46, -110, -75, 123, -29, -94, -19, -28, -64, -124, 87, 5, -4, 125, 35, -68, 68, -4, -60, -38, 123, -28, -34, -1, 1, 52, -11, 19, -32, -24, 83, 119, -4, 66, -93, -43, -3, 105, -106, 22, -39, -55, 116, 91, -124, 39, -106, 37, -83, -39, 26, 118, 78, -87, 22, 76, 12, -110, 112, 77}; + + /** + * 加密数据 + * + * @param encrypt 待加密数据 + * @return + */ + public static String encrypt(String encrypt) throws Exception { + //公钥加密 + X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(PUBLIC_KEY); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] result = cipher.doFinal(encrypt.getBytes()); + return Base64.encodeBase64String(result); + } + + /** + * 解密数据 + * + * @param decrypt 待解密数据 + * @return + */ + public static String decrypt(String decrypt) throws Exception { + //私钥解密 + PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(PRIVATE_KEY); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); + Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] result = cipher.doFinal(Base64.decodeBase64(decrypt)); + return new String(result); + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/CheckEthFeginResult.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/CheckEthFeginResult.java new file mode 100644 index 0000000..d2dbbe4 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/common/util/CheckEthFeginResult.java @@ -0,0 +1,22 @@ +package com.blockchain.server.btc.common.util; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.exception.RPCException; + +/*** + * 校验ethFegin返回值 + */ +public class CheckEthFeginResult { + + /*** + * 检查isPassword接口返回值 + * @param resultDTO isPassword接口返回值 + */ + public static void checkIsPassword(ResultDTO resultDTO) { + if (BaseConstant.REQUEST_SUCCESS != resultDTO.getCode()) { + throw new RPCException(resultDTO); + } + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/BtcTokenController.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/BtcTokenController.java new file mode 100644 index 0000000..d8c9f7f --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/BtcTokenController.java @@ -0,0 +1,35 @@ +package com.blockchain.server.btc.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.btc.common.constants.BtcConstans; +import com.blockchain.server.btc.controller.api.BtcTokenApi; +import com.blockchain.server.btc.service.BtcTokenService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Api(BtcTokenApi.MARKET_CONTROLLER_API) +@RestController +@RequestMapping("/walletToken") +public class BtcTokenController { + @Autowired + private BtcTokenService btcTokenService; + + @ApiOperation(value = BtcTokenApi.ListToken.METHOD_API_NAME, notes = BtcTokenApi.ListToken.METHOD_API_NOTE) + @GetMapping("/listToken") + public ResultDTO listToken() { + return ResultDTO.requstSuccess(btcTokenService.listToken()); + } + + @ApiOperation(value = BtcTokenApi.GetToken.METHOD_API_NAME, notes = BtcTokenApi.GetToken.METHOD_API_NOTE) + @GetMapping("/getToken") + public ResultDTO getToken(@ApiParam(BtcTokenApi.GetToken.TOKENID) @RequestParam(value = "tokenId", defaultValue = BtcConstans.BTC_PROPERTY_ID + "") Integer tokenId) { + return ResultDTO.requstSuccess(btcTokenService.selectTokenById(tokenId)); + } + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/BtcWalletController.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/BtcWalletController.java new file mode 100644 index 0000000..7e3ac04 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/BtcWalletController.java @@ -0,0 +1,54 @@ +package com.blockchain.server.btc.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.btc.common.constants.BtcApplicationConstans; +import com.blockchain.server.btc.controller.api.BtcWalletApi; +import com.blockchain.server.btc.service.BtcWalletService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; + +@Api(BtcWalletApi.MARKET_CONTROLLER_API) +@RestController +@RequestMapping("/wallet") +public class BtcWalletController { + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private BtcWalletService btcWalletService; + + + @ApiOperation(value = BtcWalletApi.GetWallet.METHOD_API_NAME, notes = BtcWalletApi.GetWallet.METHOD_API_NOTE) + @GetMapping("/getWallet") + public ResultDTO getWallet(HttpServletRequest request, + @ApiParam(BtcWalletApi.GetWallet.WALLETTYPE) @RequestParam(value = "walletType", defaultValue = BtcApplicationConstans.TYPE_CCT) String walletType, + @ApiParam(BtcWalletApi.GetWallet.TOKENID) @RequestParam("tokenId") Integer tokenId) { + String userOpenId = SSOHelper.getUserId(redisTemplate, request); + return ResultDTO.requstSuccess(btcWalletService.selectByUserOpenId(userOpenId, tokenId, walletType)); + } + + @ApiOperation(value = BtcWalletApi.GetWallets.METHOD_API_NAME, notes = BtcWalletApi.GetWallets.METHOD_API_NOTE) + @GetMapping("/getWallets") + public ResultDTO getWallets(@ApiParam(BtcWalletApi.GetWallets.WALLETTYPE) @RequestParam(name = "walletType", defaultValue = BtcApplicationConstans.TYPE_CCT) String walletType, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + return ResultDTO.requstSuccess(btcWalletService.selectAllByUserOpenId(userId, walletType)); + } + @ApiOperation(value = BtcWalletApi.Transfer.METHOD_API_NAME, notes = BtcWalletApi.Transfer.METHOD_API_NOTE) + @PostMapping("/transfer") + public ResultDTO handleTransfer(HttpServletRequest request, + @ApiParam(BtcWalletApi.Transfer.METHOD_API_FROMTYPE) @RequestParam("fromType") String fromType, + @ApiParam(BtcWalletApi.Transfer.METHOD_API_TOTYPE) @RequestParam("toType") String toType, + @ApiParam(BtcWalletApi.Transfer.METHOD_API_COINNAME) @RequestParam("coinName") String coinName, + @ApiParam(BtcWalletApi.Transfer.METHOD_API_AMOUNT) @RequestParam("amount") Double amount) { + String userOpenId = SSOHelper.getUserId(redisTemplate, request); + return ResultDTO.requstSuccess(btcWalletService.handleTransfer(userOpenId,fromType, toType, coinName, amount)); + } +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/BtcWalletTransferController.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/BtcWalletTransferController.java new file mode 100644 index 0000000..5bd120c --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/BtcWalletTransferController.java @@ -0,0 +1,72 @@ +package com.blockchain.server.btc.controller; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.base.controller.BaseController; +import com.blockchain.server.btc.common.constants.BtcApplicationConstans; +import com.blockchain.server.btc.controller.api.BtcWalletTransferApi; +import com.blockchain.server.btc.dto.BtcWalletTransferDTO; +import com.blockchain.server.btc.service.BtcWalletTransferService; +import com.github.pagehelper.PageHelper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +@Api(BtcWalletTransferApi.MARKET_CONTROLLER_API) +@RestController +@RequestMapping("/walletTransfer") +public class BtcWalletTransferController extends BaseController { + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private BtcWalletTransferService btcWalletTransferService; + + + @ApiOperation(value = BtcWalletTransferApi.GetTransfer.METHOD_API_NAME, notes = BtcWalletTransferApi.GetTransfer.METHOD_API_NOTE) + @GetMapping("/getTransfer") + public ResultDTO getTransfer(HttpServletRequest request, + @ApiParam(BtcWalletTransferApi.PAGENUM) @RequestParam(name = "pageNum", defaultValue = BaseConstant.PAGE_DEFAULT_INDEX) Integer pageNum, + @ApiParam(BtcWalletTransferApi.PAGESIZE) @RequestParam(name = "pageSize", defaultValue = BaseConstant.PAGE_DEFAULT_SIZE) Integer pageSize, + @ApiParam(BtcWalletTransferApi.GetTransfer.TOKENID) @RequestParam("tokenId") Integer tokenId, + @ApiParam(BtcWalletTransferApi.GetTransfer.WALLET_TYPE) @RequestParam(value = "walletType", defaultValue = BtcApplicationConstans.TYPE_CCT) String walletType) { + String userOpenId = SSOHelper.getUserId(redisTemplate, request); + //分页查询记录 + PageHelper.startPage(pageNum, pageSize); + return ResultDTO.requstSuccess(btcWalletTransferService.selectTransfer(userOpenId, tokenId, walletType)); + } + + @ApiOperation(value = BtcWalletTransferApi.pcGetTransfer.METHOD_API_NAME, notes = BtcWalletTransferApi.pcGetTransfer.METHOD_API_NOTE) + @GetMapping("/pcGetTransfer") + public ResultDTO pcGetTransfer(HttpServletRequest request, + @ApiParam(BtcWalletTransferApi.PAGENUM) @RequestParam(name = "pageNum", defaultValue = BaseConstant.PAGE_DEFAULT_INDEX) Integer pageNum, + @ApiParam(BtcWalletTransferApi.PAGESIZE) @RequestParam(name = "pageSize", defaultValue = BaseConstant.PAGE_DEFAULT_SIZE) Integer pageSize, + @ApiParam(BtcWalletTransferApi.pcGetTransfer.TOKENID) @RequestParam("tokenId") Integer tokenId, + @ApiParam(BtcWalletTransferApi.pcGetTransfer.WALLET_TYPE) @RequestParam(value = "walletType", defaultValue = BtcApplicationConstans.TYPE_CCT) String walletType) { + String userOpenId = SSOHelper.getUserId(redisTemplate, request); + //分页查询记录 + PageHelper.startPage(pageNum, pageSize); + List list = btcWalletTransferService.selectTransfer(userOpenId, tokenId, walletType); + return generatePage(list); + } + + @ApiOperation(value = BtcWalletTransferApi.Withdraw.METHOD_API_NAME, notes = BtcWalletTransferApi.Withdraw.METHOD_API_NOTE) + @PostMapping("/withdraw") + public ResultDTO withdraw(HttpServletRequest request, + @ApiParam(BtcWalletTransferApi.Withdraw.TOADDR) @RequestParam("toAddr") String toAddr, + @ApiParam(BtcWalletTransferApi.Withdraw.PASSWORD) @RequestParam("password") String password, + @ApiParam(BtcWalletTransferApi.Withdraw.AMOUNT) @RequestParam("amount") Double amount, + @ApiParam(BtcWalletTransferApi.Withdraw.TOKENID) @RequestParam("tokenId") Integer tokenId, + @ApiParam(BtcWalletTransferApi.Withdraw.WALLET_TYPE) @RequestParam(value = "walletType", defaultValue = BtcApplicationConstans.TYPE_CCT) String walletType) { + String userOpenId = SSOHelper.getUserId(redisTemplate, request); + return ResultDTO.requstSuccess(btcWalletTransferService.handleWithdraw(userOpenId, password, toAddr, tokenId, amount, walletType)); + } + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/ConfigWalletParamController.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/ConfigWalletParamController.java new file mode 100644 index 0000000..498d458 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/ConfigWalletParamController.java @@ -0,0 +1,29 @@ +package com.blockchain.server.btc.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.btc.common.constants.BtcConstans; +import com.blockchain.server.btc.controller.api.ConfigWalletParamApi; +import com.blockchain.server.btc.service.ConfigWalletParamService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Api(ConfigWalletParamApi.MARKET_CONTROLLER_API) +@RestController +@RequestMapping("/walletParam") +public class ConfigWalletParamController { + @Autowired + private ConfigWalletParamService walletParamService; + + @ApiOperation(value = ConfigWalletParamApi.GetGasConfig.METHOD_API_NAME, notes = ConfigWalletParamApi.GetGasConfig.METHOD_API_NOTE) + @GetMapping("/getGasConfig") + public ResultDTO getGasConfig(@ApiParam(ConfigWalletParamApi.GetGasConfig.TOKENSYMBOL) @RequestParam(value = "tokenSymbol", defaultValue = BtcConstans.BTC_SYMBOL) String tokenSymbol) { + return ResultDTO.requstSuccess(walletParamService.getGasConfig(tokenSymbol)); + } + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/BtcTokenApi.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/BtcTokenApi.java new file mode 100644 index 0000000..aadaefa --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/BtcTokenApi.java @@ -0,0 +1,17 @@ +package com.blockchain.server.btc.controller.api; + +public class BtcTokenApi { + public static final String MARKET_CONTROLLER_API = "比特币币种控制器"; + + public static class ListToken { + public static final String METHOD_API_NAME = "获取币种列表信息"; + public static final String METHOD_API_NOTE = "获取币种列表信息"; + } + + public static class GetToken { + public static final String METHOD_API_NAME = "获取币种,根据tokenid"; + public static final String METHOD_API_NOTE = "获取币种,根据tokenid"; + public static final String TOKENID = "币种id"; + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/BtcWalletApi.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/BtcWalletApi.java new file mode 100644 index 0000000..ac89a05 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/BtcWalletApi.java @@ -0,0 +1,29 @@ +package com.blockchain.server.btc.controller.api; + +public class BtcWalletApi { + public static final String MARKET_CONTROLLER_API = "比特币钱包控制器"; + + public static final String PAGENUM = "当前页数"; + public static final String PAGESIZE = "页数展示条数"; + + public static class GetWallet { + public static final String METHOD_API_NAME = "获取用户钱包"; + public static final String METHOD_API_NOTE = "获取用户钱包"; + public static final String TOKENID = "币种id"; + public static final String WALLETTYPE = "应用类型"; + } + + public static class GetWallets { + public static final String METHOD_API_NAME = "获取用户钱包所有币种信息"; + public static final String METHOD_API_NOTE = "获取用户钱包所有币种信息"; + public static final String WALLETTYPE = "应用类型"; + } + public static class Transfer { + public static final String METHOD_API_NAME = "钱包划转"; + public static final String METHOD_API_NOTE = "钱包划转"; + public static final String METHOD_API_FROMTYPE = "支付方钱包类型"; + public static final String METHOD_API_TOTYPE = "收款钱包类型"; + public static final String METHOD_API_COINNAME = "划转币种的名称"; + public static final String METHOD_API_AMOUNT = "金额"; + } +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/BtcWalletTransferApi.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/BtcWalletTransferApi.java new file mode 100644 index 0000000..cab2f28 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/BtcWalletTransferApi.java @@ -0,0 +1,33 @@ +package com.blockchain.server.btc.controller.api; + +public class BtcWalletTransferApi { + public static final String MARKET_CONTROLLER_API = "比特币交易控制器"; + + public static final String PAGENUM = "当前页数"; + public static final String PAGESIZE = "页数展示条数"; + + public static class GetTransfer { + public static final String METHOD_API_NAME = "app端查询所有交易记录"; + public static final String METHOD_API_NOTE = "app端查询所有交易记录"; + public static final String TOKENID = "币种id"; + public static final String WALLET_TYPE = "应用类型"; + } + + public static class pcGetTransfer { + public static final String METHOD_API_NAME = "pc端查询所有交易记录"; + public static final String METHOD_API_NOTE = "pc端查询所有交易记录"; + public static final String TOKENID = "币种id"; + public static final String WALLET_TYPE = "应用类型"; + } + + public static class Withdraw { + public static final String METHOD_API_NAME = "提现"; + public static final String METHOD_API_NOTE = "提现"; + public static final String TOKENID = "币种id"; + public static final String WALLET_TYPE = "应用类型"; + public static final String TOADDR = "接收地址"; + public static final String AMOUNT = "提现数量"; + public static final String PASSWORD = "加密密码"; + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/ConfigWalletParamApi.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/ConfigWalletParamApi.java new file mode 100644 index 0000000..6ee352b --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/controller/api/ConfigWalletParamApi.java @@ -0,0 +1,12 @@ +package com.blockchain.server.btc.controller.api; + +public class ConfigWalletParamApi { + public static final String MARKET_CONTROLLER_API = "钱包配置表控制器"; + + public static class GetGasConfig { + public static final String METHOD_API_NAME = "获取币种手续费配置"; + public static final String METHOD_API_NOTE = "获取币种手续费配置"; + public static final String TOKENSYMBOL = "币种名称"; + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcApplicationDTO.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcApplicationDTO.java new file mode 100644 index 0000000..026c397 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcApplicationDTO.java @@ -0,0 +1,21 @@ +package com.blockchain.server.btc.dto; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * BtcApplicationDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcApplicationDTO extends BaseModel { + private String appId; + private String appName; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcBlockNumberDTO.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcBlockNumberDTO.java new file mode 100644 index 0000000..d060242 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcBlockNumberDTO.java @@ -0,0 +1,23 @@ +package com.blockchain.server.btc.dto; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * BtcBlockNumberDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcBlockNumberDTO extends BaseModel { + private Integer blockNumber; + private java.util.Date createTime; + private java.util.Date updateTime; + private String status; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcTokenDTO.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcTokenDTO.java new file mode 100644 index 0000000..c0c2b53 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcTokenDTO.java @@ -0,0 +1,25 @@ +package com.blockchain.server.btc.dto; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * BtcTokenDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcTokenDTO extends BaseModel { + private Integer tokenId; + private String tokenSymbol; + private java.util.Date issueTime; + private String totalSupply; + private String totalCirculation; + private String descr; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcTransferAuditingDTO.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcTransferAuditingDTO.java new file mode 100644 index 0000000..1f11dd1 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcTransferAuditingDTO.java @@ -0,0 +1,25 @@ +package com.blockchain.server.btc.dto; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * BtcTransferAuditingDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcTransferAuditingDTO extends BaseModel { + private String id; + private String sysUserId; + private String ipAddr; + private String transferId; + private String transferStatus; + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletDTO.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletDTO.java new file mode 100644 index 0000000..7d48077 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletDTO.java @@ -0,0 +1,29 @@ +package com.blockchain.server.btc.dto; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * BtcWalletDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcWalletDTO extends BaseModel { + private String addr; + private Integer tokenId; + private String userOpenId; + private String tokenSymbol; + private Double balance; + private Double freeBalance; + private Double freezeBalance; + private java.util.Date createTime; + private java.util.Date updateTime; + private String walletType; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletKeyDTO.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletKeyDTO.java new file mode 100644 index 0000000..51150b2 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletKeyDTO.java @@ -0,0 +1,21 @@ +package com.blockchain.server.btc.dto; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * BtcWalletKeyDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcWalletKeyDTO extends BaseModel { + private String addr; + private String privateKey; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletOutDTO.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletOutDTO.java new file mode 100644 index 0000000..137fab0 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletOutDTO.java @@ -0,0 +1,26 @@ +package com.blockchain.server.btc.dto; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * BtcWalletOutDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcWalletOutDTO extends BaseModel { + private String id; + private String addr; + private Integer tokenId; + private String tokenSymbol; + private String privateKey; + private String password; + private String remark; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletTransferDTO.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletTransferDTO.java new file mode 100644 index 0000000..f2365b9 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/BtcWalletTransferDTO.java @@ -0,0 +1,35 @@ +package com.blockchain.server.btc.dto; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * BtcWalletTransferDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcWalletTransferDTO extends BaseModel { + private String id; + private String hash; + private String fromAddr; + private String toAddr; + private Double amount; + private Integer tokenId; + private String tokenSymbol; + private Double gasPrice; + private String gasTokenType; + private String gasTokenName; + private String gasTokenSymbol; + private String transferType; + private Integer status; + private String remark; + private java.util.Date createTime; + private java.util.Date updateTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/ConfigWalletParamDTO.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/ConfigWalletParamDTO.java new file mode 100644 index 0000000..f2d6288 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/dto/ConfigWalletParamDTO.java @@ -0,0 +1,24 @@ +package com.blockchain.server.btc.dto; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * EthApplication 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ConfigWalletParamDTO extends BaseModel { + private String id; + private String moduleType; + private String paramName; + private String paramValue; + private String paramDescr; + private Integer status; +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcApplication.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcApplication.java new file mode 100644 index 0000000..a5e05e6 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcApplication.java @@ -0,0 +1,29 @@ +package com.blockchain.server.btc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * BtcApplicationDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Table(name = "dapp_btc_application") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcApplication extends BaseModel { + @Id + @Column(name = "app_id") + private String appId; + @Column(name = "app_name") + private String appName; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcBlockNumber.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcBlockNumber.java new file mode 100644 index 0000000..08dc545 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcBlockNumber.java @@ -0,0 +1,33 @@ +package com.blockchain.server.btc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * BtcBlockNumberDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Table(name = "dapp_btc_block_number") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcBlockNumber extends BaseModel { + @Id + @Column(name = "block_number") + private Integer blockNumber; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "update_time") + private java.util.Date updateTime; + @Column(name = "status") + private String status; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcToken.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcToken.java new file mode 100644 index 0000000..fffab5f --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcToken.java @@ -0,0 +1,37 @@ +package com.blockchain.server.btc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * BtcTokenDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Table(name = "dapp_btc_token") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcToken extends BaseModel { + @Id + @Column(name = "token_id") + private Integer tokenId; + @Column(name = "token_symbol") + private String tokenSymbol; + @Column(name = "issue_time") + private java.util.Date issueTime; + @Column(name = "total_supply") + private String totalSupply; + @Column(name = "total_circulation") + private String totalCirculation; + @Column(name = "descr") + private String descr; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcTransferAuditing.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcTransferAuditing.java new file mode 100644 index 0000000..7e75b8c --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcTransferAuditing.java @@ -0,0 +1,37 @@ +package com.blockchain.server.btc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * BtcTransferAuditingDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Table(name = "dapp_btc_transfer_auditing") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcTransferAuditing extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "sys_user_id") + private String sysUserId; + @Column(name = "ip_addr") + private String ipAddr; + @Column(name = "transfer_id") + private String transferId; + @Column(name = "transfer_status") + private String transferStatus; + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWallet.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWallet.java new file mode 100644 index 0000000..fa15a05 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWallet.java @@ -0,0 +1,43 @@ +package com.blockchain.server.btc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Table; + +/** + * BtcWalletDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Table(name = "dapp_btc_wallet") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcWallet extends BaseModel { + @Column(name = "addr") + private String addr; + @Column(name = "token_id") + private Integer tokenId; + @Column(name = "user_open_id") + private String userOpenId; + @Column(name = "token_symbol") + private String tokenSymbol; + @Column(name = "balance") + private Double balance; + @Column(name = "free_balance") + private Double freeBalance; + @Column(name = "freeze_balance") + private Double freezeBalance; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "update_time") + private java.util.Date updateTime; + @Column(name = "wallet_type") + private String walletType; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWalletKey.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWalletKey.java new file mode 100644 index 0000000..f9cc2d7 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWalletKey.java @@ -0,0 +1,29 @@ +package com.blockchain.server.btc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * BtcWalletKeyDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Table(name = "dapp_btc_wallet_key") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcWalletKey extends BaseModel { + @Id + @Column(name = "addr") + private String addr; + @Column(name = "private_key") + private String privateKey; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWalletOut.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWalletOut.java new file mode 100644 index 0000000..dc50ea1 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWalletOut.java @@ -0,0 +1,39 @@ +package com.blockchain.server.btc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * BtcWalletOutDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Table(name = "dapp_btc_wallet_out") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcWalletOut extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "addr") + private String addr; + @Column(name = "token_id") + private Integer tokenId; + @Column(name = "token_symbol") + private String tokenSymbol; + @Column(name = "private_key") + private String privateKey; + @Column(name = "password") + private String password; + @Column(name = "remark") + private String remark; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWalletTransfer.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWalletTransfer.java new file mode 100644 index 0000000..d6407a8 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/BtcWalletTransfer.java @@ -0,0 +1,57 @@ +package com.blockchain.server.btc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * BtcWalletTransferDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Table(name = "dapp_btc_wallet_transfer") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BtcWalletTransfer extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "hash") + private String hash; + @Column(name = "from_addr") + private String fromAddr; + @Column(name = "to_addr") + private String toAddr; + @Column(name = "amount") + private Double amount; + @Column(name = "token_id") + private Integer tokenId; + @Column(name = "token_symbol") + private String tokenSymbol; + @Column(name = "gas_price") + private Double gasPrice; + @Column(name = "gas_token_type") + private String gasTokenType; + @Column(name = "gas_token_name") + private String gasTokenName; + @Column(name = "gas_token_symbol") + private String gasTokenSymbol; + @Column(name = "transfer_type") + private String transferType; + @Column(name = "status") + private Integer status; + @Column(name = "remark") + private String remark; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "update_time") + private java.util.Date updateTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/ConfigWalletParam.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/ConfigWalletParam.java new file mode 100644 index 0000000..7319a51 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/entity/ConfigWalletParam.java @@ -0,0 +1,34 @@ +package com.blockchain.server.btc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Table; + +/** + * EthApplication 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Table(name = "config_wallet_param") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ConfigWalletParam extends BaseModel { + @Column(name = "id") + private String id; + @Column(name = "module_type") + private String moduleType; + @Column(name = "param_name") + private String paramName; + @Column(name = "param_value") + private String paramValue; + @Column(name = "param_descr") + private String paramDescr; + @Column(name = "status") + private Integer status; +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/feign/CoinFegin.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/feign/CoinFegin.java new file mode 100644 index 0000000..8b3d2dd --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/feign/CoinFegin.java @@ -0,0 +1,31 @@ +package com.blockchain.server.btc.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient("dapp-ore-server") +public interface CoinFegin { + + + /** + * 挖矿激活 + * + * @param inviteUserIdHex 邀请码 + * @return + */ + @PostMapping("/orePower/insertPower") + ResultDTO insertPower(@RequestParam("inviteUserIdHex") String inviteUserIdHex); + + /** + * 提取矿金交易状态回调 + * + * @param hash 交易hash + * @param status 交易状态 + * @return + */ + @PostMapping("/oreDigedCoin/extractCoinCallBack") + ResultDTO extractCoinCallBack(@RequestParam("hash") String hash, @RequestParam("status") Boolean status); + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/feign/EthServerFegin.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/feign/EthServerFegin.java new file mode 100644 index 0000000..e9b1529 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/feign/EthServerFegin.java @@ -0,0 +1,20 @@ +package com.blockchain.server.btc.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient("dapp-eth-server") +public interface EthServerFegin { + + /** + * 验证钱包密码 调用eth + * + * @param password 加密密码 + * @return + */ + @GetMapping("inner/wallet/isPassword") + ResultDTO isPassword(@RequestParam(name = "password", required = false) String password); + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/feign/UserServerFegin.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/feign/UserServerFegin.java new file mode 100644 index 0000000..6572fd8 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/feign/UserServerFegin.java @@ -0,0 +1,46 @@ +package com.blockchain.server.btc.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient("dapp-user-server") +public interface UserServerFegin { + + /** + * 通过手机号获取用户id + * + * @param phone + * @return + */ + @PostMapping("/login/getUserIdByPhone") + ResultDTO getUserIdByPhone(@RequestParam("phone") String phone); + + + /** + * 校验验证码 + * + * @param phone + * @param verifyCode + * @return + */ + @PostMapping("/inner/validateSmsg") + ResultDTO validateSmsg(@RequestParam("phone") String phone, @RequestParam("verifyCode") String verifyCode); + + /** + * 禁止提现 + * @param userId + * @return + */ + @PostMapping("/inner/verifyBanWithdraw") + ResultDTO verifyBanWithdraw(@RequestParam("userId") String userId); + + /** + * 免提现手续费 + * @param userOpenId + * @return + */ + @PostMapping("/inner/verifyFreeWithdraw") + ResultDTO verifyFreeWithdraw(@RequestParam("userId") String userOpenId); +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/BtcWalletInner.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/BtcWalletInner.java new file mode 100644 index 0000000..b30d97b --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/BtcWalletInner.java @@ -0,0 +1,29 @@ +package com.blockchain.server.btc.inner; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.btc.inner.api.BtcWalletInnerApi; +import com.blockchain.server.btc.service.BtcWalletService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Api(BtcWalletInnerApi.MARKET_CONTROLLER_API) +@RestController +@RequestMapping("/inner/wallet") +public class BtcWalletInner { + + @Autowired + private BtcWalletService btcWalletService; + + @ApiOperation(value = BtcWalletInnerApi.CreateWallet.METHOD_API_NAME, notes = BtcWalletInnerApi.CreateWallet.METHOD_API_NOTE) + @GetMapping("/createWallet") + public ResultDTO createWallet(@ApiParam(BtcWalletInnerApi.CreateWallet.USEROPENID) @RequestParam("userOpenId") String userOpenId) { + return ResultDTO.requstSuccess(btcWalletService.insertWallet(userOpenId)); + } + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/BtcWalletTransferInner.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/BtcWalletTransferInner.java new file mode 100644 index 0000000..9675ecf --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/BtcWalletTransferInner.java @@ -0,0 +1,41 @@ +package com.blockchain.server.btc.inner; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import com.blockchain.server.btc.inner.api.BtcWalletTransferInnerApi; +import com.blockchain.server.btc.service.BtcWalletTransferService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Api(BtcWalletTransferInnerApi.MARKET_CONTROLLER_API) +@RestController +@RequestMapping("/inner/walletTx") +public class BtcWalletTransferInner { + private static final Logger LOG = LoggerFactory.getLogger(BtcWalletTransferInner.class); + @Autowired + private BtcWalletTransferService btcWalletTransferService; + + @ApiOperation(value = BtcWalletTransferInnerApi.Order.METHOD_API_NAME, notes = BtcWalletTransferInnerApi.Order.METHOD_API_NOTE) + @PostMapping("/order") + public ResultDTO order(@ApiParam(BtcWalletTransferInnerApi.Order.WALLETORDERDTO) @RequestBody WalletOrderDTO walletOrderDTO) { + LOG.info("冻结、解冻余额,walletOrderDTO:" + walletOrderDTO.toString()); + return ResultDTO.requstSuccess(btcWalletTransferService.handleOrder(walletOrderDTO)); + } + + @ApiOperation(value = BtcWalletTransferInnerApi.Change.METHOD_API_NAME, notes = BtcWalletTransferInnerApi.Change.METHOD_API_NOTE) + @PostMapping("/change") + public ResultDTO change(@ApiParam(BtcWalletTransferInnerApi.Change.WALLETCHANGEDTO) @RequestBody WalletChangeDTO walletChangeDTO) { + LOG.info("扣除、增加,walletChangeDTO:" + walletChangeDTO.toString()); + return ResultDTO.requstSuccess(btcWalletTransferService.handleChange(walletChangeDTO)); + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/api/BtcWalletInnerApi.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/api/BtcWalletInnerApi.java new file mode 100644 index 0000000..d06dad1 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/api/BtcWalletInnerApi.java @@ -0,0 +1,12 @@ +package com.blockchain.server.btc.inner.api; + +public class BtcWalletInnerApi { + public static final String MARKET_CONTROLLER_API = "比特币钱包控制器,内部调用"; + + public static class CreateWallet { + public static final String METHOD_API_NAME = "创建钱包"; + public static final String METHOD_API_NOTE = "创建钱包"; + public static final String USEROPENID = "用户id"; + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/api/BtcWalletTransferInnerApi.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/api/BtcWalletTransferInnerApi.java new file mode 100644 index 0000000..c462c83 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/inner/api/BtcWalletTransferInnerApi.java @@ -0,0 +1,18 @@ +package com.blockchain.server.btc.inner.api; + +public class BtcWalletTransferInnerApi { + public static final String MARKET_CONTROLLER_API = "btc钱包转账控制器,内部调用"; + + public static class Order { + public static final String METHOD_API_NAME = "币币交易,冻结、解冻接口"; + public static final String METHOD_API_NOTE = "币币交易,冻结、解冻接口"; + public static final String WALLETORDERDTO = "交易参数"; + } + + public static class Change { + public static final String METHOD_API_NAME = "钱包余额变动接口"; + public static final String METHOD_API_NOTE = "钱包余额变动接口"; + public static final String WALLETCHANGEDTO = "变动参数"; + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcApplicationMapper.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcApplicationMapper.java new file mode 100644 index 0000000..cdab70f --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcApplicationMapper.java @@ -0,0 +1,26 @@ +package com.blockchain.server.btc.mapper; + +import com.blockchain.server.btc.dto.BtcApplicationDTO; +import com.blockchain.server.btc.entity.BtcApplication; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * BtcApplicationMapper 数据访问类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Repository +public interface BtcApplicationMapper extends Mapper { + + /** + * 获取应用列表 + * + * @return + */ + List listApplication(); + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcBlockNumberMapper.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcBlockNumberMapper.java new file mode 100644 index 0000000..6b6edfd --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcBlockNumberMapper.java @@ -0,0 +1,35 @@ +package com.blockchain.server.btc.mapper; + +import com.blockchain.server.btc.dto.BtcBlockNumberDTO; +import com.blockchain.server.btc.entity.BtcBlockNumber; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * BtcBlockNumberMapper 数据访问类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Repository +public interface BtcBlockNumberMapper extends Mapper { + + /** + * 查询已经同步的最大区块号 + * + * @return + */ + Integer selectBigest(); + + /** + * 根据状态获取同步区块 + * + * @param status 状态 + * @return + */ + List listByStatus(@Param("status") String status); + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcTokenMapper.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcTokenMapper.java new file mode 100644 index 0000000..77ab8e0 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcTokenMapper.java @@ -0,0 +1,34 @@ +package com.blockchain.server.btc.mapper; + +import com.blockchain.server.btc.dto.BtcTokenDTO; +import com.blockchain.server.btc.entity.BtcToken; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * BtcTokenMapper 数据访问类 + * @date 2019-02-16 15:08:16 + * @version 1.0 + */ +@Repository +public interface BtcTokenMapper extends Mapper { + + /** + * 获取币种 + * + * @return + */ + List listToken(); + + /** + * 获取币种,根据tokenid + * + * @param tokenId 币种id + * @return + */ + BtcTokenDTO selectTokenById(@Param("tokenId") Integer tokenId); + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcTransferAuditingMapper.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcTransferAuditingMapper.java new file mode 100644 index 0000000..d9a665f --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcTransferAuditingMapper.java @@ -0,0 +1,14 @@ +package com.blockchain.server.btc.mapper; + +import com.blockchain.server.btc.entity.BtcTransferAuditing; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * BtcTransferAuditingMapper 数据访问类 + * @date 2019-02-16 15:08:16 + * @version 1.0 + */ +@Repository +public interface BtcTransferAuditingMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletKeyMapper.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletKeyMapper.java new file mode 100644 index 0000000..3ebc8ff --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletKeyMapper.java @@ -0,0 +1,14 @@ +package com.blockchain.server.btc.mapper; + +import com.blockchain.server.btc.entity.BtcWalletKey; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * BtcWalletKeyMapper 数据访问类 + * @date 2019-02-16 15:08:16 + * @version 1.0 + */ +@Repository +public interface BtcWalletKeyMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletMapper.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletMapper.java new file mode 100644 index 0000000..72a7b9d --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletMapper.java @@ -0,0 +1,70 @@ +package com.blockchain.server.btc.mapper; + +import com.blockchain.server.btc.dto.BtcWalletDTO; +import com.blockchain.server.btc.entity.BtcWallet; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.Date; +import java.util.List; +import java.util.Set; + +/** + * BtcWalletMapper 数据访问类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Repository +public interface BtcWalletMapper extends Mapper { + + /** + * 获取用户钱包 + * + * @param userOpenId 用户id + * @param tokenId 币种id + * @param walletType 应用类型,币币交易等 + * @return + */ + BtcWalletDTO selectByUserOpenId(@Param("userOpenId") String userOpenId, @Param("tokenId") Integer tokenId, @Param("walletType") String walletType); + + /** + * 获取用户钱包,通过地址 + * + * @param addr 用户id + * @param tokenId 币种id + * @param walletType 应用类型,币币交易等 + * @return + */ + BtcWalletDTO selectByAddr(@Param("addr") String addr, @Param("tokenId") Integer tokenId, @Param("walletType") String walletType); + + /** + * 加减钱包地址可用余额、冻结余额、总额 + * + * @param address 地址 + * @param tokenId 币种id + * @param freeAmount 可用余额加减数量 + * @param freezeAmount 冻结余额加减数量 + * @param totalAmount 总额加减数量 + * @return + */ + Integer updateBalanceByAddrInRowLock(@Param("address") String address, @Param("tokenId") Integer tokenId, @Param("freeAmount") Double freeAmount, + @Param("freezeAmount") Double freezeAmount, @Param("totalAmount") Double totalAmount, @Param("modifyTime") Date modifyTime); + + /** + * 获取所有用户托管钱包地址 + * + * @return + */ + Set getAllWalletAddr(); + + /** + * 获取用户某应用钱包的所有区块 + * + * @param userOpenId 用户id + * @param walletType 应用类型,币币交易等 + * @return + */ + List selectAllByUserOpenId(@Param("userOpenId") String userOpenId, @Param("walletType") String walletType); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletOutMapper.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletOutMapper.java new file mode 100644 index 0000000..d656020 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletOutMapper.java @@ -0,0 +1,14 @@ +package com.blockchain.server.btc.mapper; + +import com.blockchain.server.btc.entity.BtcWalletOut; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * BtcWalletOutMapper 数据访问类 + * @date 2019-02-16 15:08:16 + * @version 1.0 + */ +@Repository +public interface BtcWalletOutMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletTransferMapper.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletTransferMapper.java new file mode 100644 index 0000000..c95f327 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/BtcWalletTransferMapper.java @@ -0,0 +1,37 @@ +package com.blockchain.server.btc.mapper; + +import com.blockchain.server.btc.dto.BtcWalletTransferDTO; +import com.blockchain.server.btc.entity.BtcWalletTransfer; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * BtcWalletTransferMapper 数据访问类 + * + * @version 1.0 + * @date 2019-02-16 15:08:16 + */ +@Repository +public interface BtcWalletTransferMapper extends Mapper { + + /** + * 通过交易hash获取数据库记录id + * + * @param hash 交易id + * @return + */ + String selectIdByTxIdAndType(@Param("hash") String hash, @Param("transferType") String transferType); + + /** + * 获取用户钱包记录 + * + * @param addr 地址 + * @param tokenId 币种id + * @return + */ + List selectTransfer(@Param("addr") String addr, @Param("tokenId") Integer tokenId); + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/ConfigWalletParamMapper.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/ConfigWalletParamMapper.java new file mode 100644 index 0000000..9ade19b --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/mapper/ConfigWalletParamMapper.java @@ -0,0 +1,15 @@ +package com.blockchain.server.btc.mapper; + +import com.blockchain.server.btc.entity.ConfigWalletParam; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * ConfigWalletParam 数据访问类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Repository +public interface ConfigWalletParamMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/rpc/BtcOmniRpcClient.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/rpc/BtcOmniRpcClient.java new file mode 100644 index 0000000..5232d7e --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/rpc/BtcOmniRpcClient.java @@ -0,0 +1,44 @@ +package com.blockchain.server.btc.rpc; + +import com.googlecode.jsonrpc4j.JsonRpcHttpClient; +import org.apache.commons.codec.binary.Base64; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +/** + * @author hugq + * @date 2019/2/16 15:54 + */ +@Component +public class BtcOmniRpcClient { + + private Logger LOG = LoggerFactory.getLogger(getClass()); + + @Value("${btc.rpc.user}") + public String RPC_USER; //验证用户名 + @Value("${btc.rpc.password}") + public String RPC_PASSWORD; //验证密码 + @Value("${btc.rpc.url}") + public String RPC_URL; //验证地址 + + // 比特币RPC身份认证 + public JsonRpcHttpClient getClient() { + JsonRpcHttpClient client = null; + try { + String cred = new Base64().encodeToString((RPC_USER + ":" + RPC_PASSWORD).getBytes("UTF-8")); + Map headers = new HashMap(1); + headers.put("Authorization", "Basic " + cred); + client = new JsonRpcHttpClient(new URL(RPC_URL), headers); + } catch (Exception e) { + LOG.info("=== getClient:{} btc client !===", e.getMessage(), e); + } + return client; + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/rpc/BtcUtils.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/rpc/BtcUtils.java new file mode 100644 index 0000000..594c2b7 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/rpc/BtcUtils.java @@ -0,0 +1,553 @@ +package com.blockchain.server.btc.rpc; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.blockchain.server.btc.common.enums.BtcEnums; +import com.blockchain.server.btc.common.exception.BtcException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +/** + * @author hugq + * @date 2019/2/16 15:54 + */ +@Component +public class BtcUtils { + + private Logger LOG = LoggerFactory.getLogger(getClass()); + + @Autowired + private BtcOmniRpcClient client; + + /** + * 生成新的钱包地址 + * + * @return + * @throws Exception + */ + public String getNewAddress() throws Exception { + try { + return client.getClient().invoke("getnewaddress", null, String.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.getNewAddress(String):{} ===", e.getMessage(), e); + throw new BtcException(BtcEnums.GET_NEW_ADDRESS_ERROR); + } + } + + /** + * 获取地址的未花费列表 + * + * @param address 地址 + * @return + * @throws Exception + */ + public JSONArray listUnspent(String address) throws Exception { + try { + return client.getClient().invoke("listunspent", new Object[]{1, Integer.MAX_VALUE, new Object[]{address}}, JSONArray.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.listUnspent(String):{} ===", e.getMessage(), e); + throw new BtcException(BtcEnums.LIST_UNSPENT_ERROR); + } + } + + /** + * 验证是否有效地址 + * + * @param address 地址 + * @return + * @throws Exception + */ + public JSONObject validateAddress(String address) throws Exception { + try { + return client.getClient().invoke("validateaddress", new Object[]{address}, JSONObject.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.validateAddress(String):{} ===", e.getMessage(), e); + throw new BtcException(BtcEnums.ADDRESS_ERROR); + } + } + + /** + * 获取指定账户/整个钱包BTC余额 + * + * @param account 账户名,为空则获取整个钱包余额 + * @return + * @throws Exception + */ + public double getBalance(String account) throws Exception { + double balance; + try { + if (!StringUtils.isEmpty(account)) { + balance = client.getClient().invoke("getbalance", new Object[]{account}, Double.class); + } else { + balance = client.getClient().invoke("getbalance", new Object[]{}, Double.class); + } + } catch (Throwable e) { + LOG.info("=== BtcUtils.getBalance(String...):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: account=%s", account)); + } + return balance; + } + + /** + * 从钱包内向指定的地址发送指定数量的比特币,简单交易,钱包内随机指定或创建地址作为发送地址、找零地址 + * + * @param toAddress 地址 + * @param amount 数量 + * @return 交易ID + * @throws Exception + */ + public String sendToAddress(String toAddress, String amount) throws Exception { + try { + return client.getClient().invoke("sendtoaddress", new Object[]{toAddress, amount}, String.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.sendtoaddress(String, double):{} ===", e.getMessage(), e); + throw new BtcException(BtcEnums.SENDTOADDRESS_ERROR); + } + } + + public Object sendMany(String fromaccount, Object target) throws Exception { + try { + String txId = client.getClient().invoke("sendmany", new Object[]{fromaccount, target}, Object.class).toString(); + return txId; + } catch (Throwable e) { + LOG.info("=== BtcUtils.signSendToAddress(String, double):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: fromaccount=%s,target=%s", fromaccount, target)); + } + } + + public Object getRawTransaction(String txId, int verbose) throws Exception { + try { + return client.getClient().invoke("getrawtransaction", new Object[]{txId, verbose}, Object.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.getRawTransaction(String):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: txId=%s", txId)); + } + } + + /** + * 获取指定钱包内交易的详细信息 + * + * @param txId 交易ID + * @return + * @throws Exception + */ + public JSONObject getTransaction(String txId) throws Exception { + try { + return client.getClient().invoke("gettransaction", new Object[]{txId}, JSONObject.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.getTransaction(String):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: txId=%s", txId)); + } + } + + public Object setAccount(String address, String account) throws Exception { + try { + return client.getClient().invoke("setaccount", new Object[]{address, account}, Object.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.setAccount(String, String):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: address=%s,account=%s", address, account)); + } + } + + /** + * 调用返回各地址收到的比特币数量 + * + * @param minconf 计入统计结果的交易所需的最小确认数,默认值:1 + * @return + * @throws Exception + */ + public Object listReceivedByAddress(int minconf) throws Exception { + try { + return client.getClient().invoke("listreceivedbyaccount", new Object[]{minconf}, Object.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.listReceivedByAddress(int minconf):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: minconf=%s", minconf)); + } + } + + /** + * 调用设置钱包交易支付时采用的每千字节手续费率 + * + * @param account 每千字节的手续费 + * @return true + * @throws Exception + */ + public boolean setTxFee(double account) throws Exception { + try { + return client.getClient().invoke("settxfee", new Object[]{account + ""}, Boolean.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.settxfee(double):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: account=%s", account)); + } + } + + public Object encryptWallet(String passphrase) throws Exception { + try { + return client.getClient().invoke("encryptwallet", new Object[]{passphrase}, Object.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.encryptwallet(String) ===", e.getMessage(), e); + throw new Exception(e.getMessage()); + } + } + + public Object walletPassphrase(String passphrase, int timeout) throws Exception { + try { + return client.getClient().invoke("walletpassphrase", new Object[]{passphrase, timeout}, Object.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.walletpassphrase(String, int):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: passphrase=%s,timeout=%s", passphrase, timeout)); + } + } + + /** + * 调用可以将钱包文件wallet.data安全地拷贝到指定文件或目录 + * + * @param destination 备份文件路径名称 + * @return 成功时返回null + * @throws Exception + */ + public boolean backupWallet(String destination) throws Exception { + try { + Object obj = client.getClient().invoke("backupwallet ", new Object[]{destination}, Object.class); + return obj == null; + } catch (Throwable e) { + LOG.info("=== BtcUtils.backupWallet(String):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: destination=%s", destination)); + } + } + + /** + * 调用可以导入钱包转储文件(通过dumpwallet调用获得)。该文件中的私钥将添加到节点钱包中。由于加入了新的私钥,该调用可能 需要重新扫描区块链。 + * + * @param filename 要导入的钱包转储文件名 + * @return + * @throws Exception + */ + public boolean importWallet(String filename) throws Exception { + try { + Object obj = client.getClient().invoke("importwallet ", new Object[]{filename}, Object.class); + return obj == null; + } catch (Throwable e) { + LOG.info("=== BtcUtils.importWallet(String):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: filename=%s", filename)); + } + } + + /** + * 创建一个未签名的裸交易 + * + * @param utxo 交易输入数组,成员对象的结构: UTXO的交易id, UTXO的输出序号 + * @param toAddress 交易输出对象,键为地址 + * @param amonut 交易输出对象,值为金额 + * @return 返回生成的未签名交易的序列化字符串 + * @throws Exception + */ + public String createRawTransaction(Object utxo, String toAddress, double amonut) throws Exception { + String _amount = String.valueOf(amonut); + Map outputs = new HashMap(); + outputs.put(toAddress, _amount); + try { + return client.getClient().invoke("createrawtransaction", new Object[]{utxo, outputs}, String.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.createRawTransaction(Object):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: inputs=%s,outputs=%s", utxo.toString(), outputs.toString())); + } + } + + /** + * 为裸交易交易增加输入,主要用于指定找零地址 + * + * @param hexString 裸交易字符串 + * @param address 额外的可选项,结构: changeAddress:找零地址,如果不设置则自动从地址池中选择一个新地址 + * @return 返回更新后的交易信息 + * @throws Exception + */ + public String fundRawTransaction(String hexString, String address) throws Exception { + Map outputs = new HashMap(); + outputs.put("changeAddress", address); + try { + JSONObject jsonObject = client.getClient().invoke("fundrawtransaction", new Object[]{hexString, outputs}, JSONObject.class); + return jsonObject.getString("hex"); + } catch (Throwable e) { + LOG.info("=== BtcUtils.fundRawTransaction(Object):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: hexString=%s,outputs=%s", hexString, outputs.toString())); + } + } + + /** + * 签名交易 + * + * @param hexString 交易串 + * @return + * @throws Exception + */ + public String signRawTransaction(String hexString) throws Exception { + try { + JSONObject jsonObject = client.getClient().invoke("signrawtransaction", new Object[]{hexString}, JSONObject.class); + return jsonObject.getString("hex"); + } catch (Throwable e) { + LOG.info("=== BtcUtils.signrawTransaction(String):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: hexstring=%s", hexString)); + } + } + + /** + * 调用验证指定交易并将其广播到P2P网络中 + * + * @param hexHash 序列化的交易码流 + * @return + * @throws Exception + */ + public String sendRawTransaction(String hexHash) throws Exception { + try { + return client.getClient().invoke("sendrawtransaction", new Object[]{hexHash}, String.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.sendRawTransaction(String):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: hexHash=%s", hexHash)); + } + } + + /** + * 调用将一个序列化的交易字符串解码为JSON对象 + * + * @param hex 要解码的裸交易字符串 + * @return + * @throws Exception + */ + public JSONObject decodeRawTransaction(String hex) throws Exception { + try { + return client.getClient().invoke("decoderawtransaction", new Object[]{hex}, JSONObject.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.decoderawtransaction(String):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: hex=%s", hex)); + } + } + + /** + * 通过区块高度获取该区块hash + * + * @param index + * @return + * @throws Exception + */ + public String getBlockHash(int index) throws Exception { + try { + return client.getClient().invoke("getblockhash", new Object[]{index}, String.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.getBlockHash(int):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: index=%s", index)); + } + } + + /** + * 获取最新区块高度 + * + * @return + * @throws Exception + */ + public Integer getBlockCount() throws Exception { + try { + return client.getClient().invoke("getblockcount", null, Integer.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.getBlockHeader():{} ===", e.getMessage(), e); + throw new Exception(e.getMessage()); + } + } + + /** + * 获取区块内交易id列表集合 + * + * @param blockHash 区块hash + * @return + * @throws Exception + */ + public ArrayList getBlock(String blockHash) throws Exception { + try { + JSONObject blockJson = client.getClient().invoke("getblock", new Object[]{blockHash}, JSONObject.class); + return (ArrayList) blockJson.get("tx"); + } catch (Throwable e) { + LOG.info("=== BtcUtils.getblock(String):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: blockHash=%s", blockHash)); + } + } + + /** + * 通过地址获取私钥 + * + * @param address 地址 + * @return + * @throws Exception + */ + public String getPrivateKeyByAddress(String address) throws Exception { + try { + return client.getClient().invoke("dumpprivkey", new Object[]{address}, String.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.getPrivateKeyByAddress(String):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: blockHash=%s", address)); + } + } + + /** + * 通过区块hash获取区块高度 + * + * @param headerHash 区块hash + * @return + * @throws Exception + */ + public int getBlockHeader(String headerHash) throws Exception { + try { + JSONObject blockObject = client.getClient().invoke("getblockheader", new Object[]{headerHash}, JSONObject.class); + return blockObject.getInteger("height"); + } catch (Throwable e) { + LOG.info("=== BtcUtils.getBlockHeader(String):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: headerHash=%s", headerHash)); + } + } + + /** + * 调用返回指定区块之后发生的与钱包相关的所有交易 + * + * @param headerHash 区块hash + * @return + * @throws Exception + */ + public JSONArray listSinceBlock(String headerHash) throws Exception { + try { + JSONObject blockObject = client.getClient().invoke("listsinceblock", new Object[]{headerHash}, JSONObject.class); + return blockObject.getJSONArray("transactions"); + } catch (Throwable e) { + LOG.info("=== BtcUtils.listSinceBlock(String):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: headerHash=%s", headerHash)); + } + } + + /** + * 用裸交易方式进行转账,可以指定找零地址,没有指定则默认输出地址 + * + * @param fromAddress 输出地址 + * @param toAddress 收入地址 + * @param amonut 数量 + * @param changeAddress 找零地址 + * @return + * @throws Exception + */ + public String sendWithRaw(String fromAddress, String toAddress, double amonut, String changeAddress) throws Exception { + String txId = ""; + + //如果没指定找零地址则默认输出地址 + if (StringUtils.isEmpty(changeAddress)) { + changeAddress = fromAddress; + } + try { + //获取输出地址的UTXO + JSONArray UTXOArr = listUnspent(fromAddress); + //创建裸交易 + String hexString = createRawTransaction(UTXOArr, toAddress, amonut); + //指定找零地址 + String fundHexString = fundRawTransaction(hexString, changeAddress); + //签名交易 + String signHex = signRawTransaction(fundHexString); + //广播交易 + txId = sendRawTransaction(signHex); + } catch (Throwable e) { + LOG.info("=== BtcUtils.sendWithRaw(String):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: fromAddress=%s,toAddress=%s,amonut=%s,changeAddress=%s", fromAddress, toAddress, amonut, changeAddress)); + } + return txId; + } + + /** + * 调用返回节点钱包中未确认总余额 + * + * @return + * @throws Exception + */ + public double getUnconfirmedBalance() throws Exception { + try { + return client.getClient().invoke("getunconfirmedbalance", null, Double.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.getUnconfirmedBalance():{} ===", e.getMessage(), e); + throw new Exception(e.getMessage()); + } + } + + /** + * 调用按地址分组显示余额信息 + * + * @return + * @throws Exception + */ + public JSONArray listAddressGroupings() throws Exception { + try { + return client.getClient().invoke("listaddressgroupings", null, JSONArray.class); + } catch (Throwable e) { + LOG.info("=== BtcUtils.listaddressgroupings():{} ===", e.getMessage(), e); + throw new Exception(e.getMessage()); + } + } + + /** + * 通过地址获取BTC余额 + * + * @param addr 地址 + * @return + * @throws Exception + */ + public double getBalanceByAddr(String addr) throws Exception { + try { + JSONArray jsonArray = listAddressGroupings(); + for (int i = 0; i < jsonArray.size(); i++) { + JSONArray groupArr = jsonArray.getJSONArray(i); + for (int j = 0; j < groupArr.size(); j++) { + JSONArray addrArr = groupArr.getJSONArray(j); + String addrThis = addrArr.getString(0); + if (addr.equals(addrThis)) { + return addrArr.getDoubleValue(1); + } + } + } + return 0.0; + +// double balance = 0; +// JSONArray jsonArray = listUnspent(addr); +// int len = jsonArray.size(); +// for (int i = 0; i < len; i++) { +// JSONObject jsonObject = jsonArray.getJSONObject(i); +// balance += jsonObject.getDoubleValue("amount"); +// } +// return balance; + } catch (Throwable e) { + LOG.info("=== BtcUtils.getBalanceByAddr(String):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: addr=%s", addr)); + } + } + + + /** + * 调用暂时锁定/解锁指定的交易输出。一个被锁定的交易输出将不会被自动选中作为交易输入。锁定仅保持在内存中,因此节点重新启动后将自动解除锁定。 + * + * @return + * @throws Exception + */ + public void unLockUnspent() throws Exception { + try { + //调用返回当前暂时不可用(锁定)的UTXO清单 + JSONArray jsonArray = client.getClient().invoke("listlockunspent", null, JSONArray.class); + if (jsonArray.size() > 0) { + //解锁之前被锁定的交易输出项,设置为true解锁交易输出,否则加锁 + client.getClient().invoke("lockunspent", new Object[]{true, jsonArray}); + } + } catch (Throwable e) { + LOG.info("=== BtcUtils.unLockUnspent():{} ===", e.getMessage(), e); + throw new Exception(e.getMessage()); + } + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/rpc/UsdtUtils.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/rpc/UsdtUtils.java new file mode 100644 index 0000000..c2a0aeb --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/rpc/UsdtUtils.java @@ -0,0 +1,113 @@ +package com.blockchain.server.btc.rpc; + +import com.alibaba.fastjson.JSONObject; +import com.blockchain.server.btc.common.constants.UsdtConstans; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author hugq + * @date 2019/2/16 15:54 + */ +@Component +public class UsdtUtils { + + private Logger LOG = LoggerFactory.getLogger(getClass()); + + @Autowired + private BtcOmniRpcClient client; + + /** + * 创建并广播发送一个简单usdt交易 + * + * @param fromAddress 发起地址 + * @param toAddress 接受地址 + * @param amount 数量 + * @return 返回结果为16进制字符串表示的交易哈希 + * @throws Exception + */ + public String send(String fromAddress, String toAddress, double amount) throws Exception { + String _amount = String.valueOf(amount); + try { + return client.getClient().invoke("omni_send", new Object[]{fromAddress, toAddress, UsdtConstans.USDT_PROPERTY_ID, _amount}, String.class); + } catch (Throwable e) { + LOG.info("=== BtcOmniUtils.send(String fromAddress, String toAddress, double amount):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: fromAddress=%s,toAddress=%s,amount=%s", fromAddress, toAddress, _amount)); + } + } + + /** + * 创建并广播发送一个简单usdt交易,指定支付手续费地址 + * + * @param fromAddress 发起地址 + * @param toAddress 接受地址 + * @param amount 数量 + * @param feeAddress 支付手续费的地址 + * @return 返回结果为16进制字符串表示的交易哈希 + * @throws Exception + */ + public String fundedSend(String fromAddress, String toAddress, double amount, String feeAddress) throws Exception { + String _amount = String.valueOf(amount); + try { + return client.getClient().invoke("omni_funded_send", new Object[]{fromAddress, toAddress, UsdtConstans.USDT_PROPERTY_ID, _amount, feeAddress}, String.class); + } catch (Throwable e) { + LOG.info("=== BtcOmniUtils.fundedSend(String fromAddress, String toAddress, double amount, String feeaddress):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: fromAddress=%s,toAddress=%s,propertyId=%s,amount=%s,feeaddress=%s", fromAddress, toAddress, UsdtConstans.USDT_PROPERTY_ID, amount, feeAddress)); + } + } + + /** + * 调用创建并广播一个交易,将所有可用代币转入指定生态系统中的接收地址 + * + * @param fromAddress 发起地址 + * @param toAddress 接受地址 + * @param ecosystem 生态系统编码,数值,1 - 主生态,2 - 测试生态 + * @param feeaddress 支付手续费的地址 + * @return 返回结果为16进制字符串表示的交易哈希 + * @throws Exception + */ + public String fundedSendAll(String fromAddress, String toAddress, int ecosystem, String feeaddress) throws Exception { + try { + return client.getClient().invoke("omni_funded_sendall", new Object[]{fromAddress, toAddress, ecosystem, feeaddress}, String.class); + } catch (Throwable e) { + LOG.info("=== BtcOmniUtils.fundedSendAll(String fromAddress, String toAddress, int ecosystem, String feeaddress):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: fromAddress=%s,toAddress=%s,ecosystem=%s,feeaddress=%s", fromAddress, toAddress, ecosystem, feeaddress)); + } + } + + /** + * 调用返回指定地址和资产的代币余额 + * + * @param address 地址 + * @return + * @throws Exception + */ + public double getBalance(String address) throws Exception { + try { + JSONObject balanceJson = client.getClient().invoke("omni_getbalance", new Object[]{address, UsdtConstans.USDT_PROPERTY_ID}, JSONObject.class); + return balanceJson.getDoubleValue("balance"); + } catch (Throwable e) { + LOG.info("=== BtcOmniUtils.getBalance(String address):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: address=%s", address)); + } + } + + /** + * 获取指定Omni交易的详细信息 + * + * @param txId 交易哈希 + * @return + * @throws Exception + */ + public JSONObject getTransaction(String txId) throws Exception { + try { + return client.getClient().invoke("omni_gettransaction", new Object[]{txId}, JSONObject.class); + } catch (Throwable e) { + LOG.info("=== BtcOmniUtils.getTransaction(String):{} ===", e.getMessage(), e); + throw new Exception(e.getMessage() + String.format("[params]: txId=%s", txId)); + } + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/scheduleTask/RechargeBtcAndUsdtBlockTimer.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/scheduleTask/RechargeBtcAndUsdtBlockTimer.java new file mode 100644 index 0000000..c8cae77 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/scheduleTask/RechargeBtcAndUsdtBlockTimer.java @@ -0,0 +1,280 @@ +package com.blockchain.server.btc.scheduleTask; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.blockchain.server.btc.common.constants.BtcBlockNumberConstans; +import com.blockchain.server.btc.common.constants.BtcConstans; +import com.blockchain.server.btc.common.constants.BtcTransferConstans; +import com.blockchain.server.btc.common.constants.UsdtConstans; +import com.blockchain.server.btc.common.util.BtcAddressSetRedisUtils; +import com.blockchain.server.btc.common.util.BtcBlockRedisUtils; +import com.blockchain.server.btc.dto.BtcBlockNumberDTO; +import com.blockchain.server.btc.entity.BtcWalletTransfer; +import com.blockchain.server.btc.rpc.BtcUtils; +import com.blockchain.server.btc.rpc.UsdtUtils; +import com.blockchain.server.btc.service.BtcBlockNumberService; +import com.blockchain.server.btc.service.BtcWalletTransferService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.List; +import java.util.UUID; + +//@Component +public class RechargeBtcAndUsdtBlockTimer { + + @Autowired + private BtcAddressSetRedisUtils btcAddressSetRedisUtils; + + @Autowired + private BtcBlockRedisUtils btcBlockRedisUtils; + + @Autowired + private BtcUtils btcUtils; + + @Autowired + private UsdtUtils usdtUtils; + + @Autowired + private BtcBlockNumberService btcBlockNumberService; + + @Autowired + private BtcWalletTransferService btcWalletTransferService; + + /** + * 查询区块链充值信息 + */ + @Scheduled(cron = "0/30 * * * * ?") //30秒每次 + public void rechargeBtcAndUsdtBlock() { + //获取已经同步的最大区块号 + int bigestBlock = btcBlockNumberService.selectBigest(); + //区块链最新区块高度 + int blockHeightRpc = 0; + //获取该区块hash + String blockHash = ""; + //交易信息 + JSONArray transferArr = null; + try { + blockHeightRpc = btcUtils.getBlockCount(); + //是否已经同步到最新区块 + if (bigestBlock >= blockHeightRpc) { + return; + } +// //判断是否有未确认交易 +// if (btcUtils.getUnconfirmedBalance() != 0) { +// return; +// } + + blockHash = btcUtils.getBlockHash(bigestBlock); + transferArr = btcUtils.listSinceBlock(blockHash); + } catch (Exception e) { + return; + } + + //区块号自增 + bigestBlock++; + //保存已经同步区块高度的到数据库 + btcBlockNumberService.insertBlockNumber(bigestBlock, BtcBlockNumberConstans.STATUS_W); + + int transferArrLen = transferArr.size(); + for (int i = 0; i < transferArrLen; i++) { + JSONObject transferObj = transferArr.getJSONObject(i); + if ("receive".equals(transferObj.getString("category"))) { + String toAddr = transferObj.getString("address"); + if (btcAddressSetRedisUtils.isExistsAddr(toAddr)) { + String txId = transferObj.getString("txid"); + + try { + //先用usdt解析,如果不能解析,则用btc解析 + JSONObject transferUsdt = usdtUtils.getTransaction(txId); + try { + //修改该usdt钱包余额,并插入一条充值记录 + parseUsdtTransfer(transferUsdt, txId); + } catch (Exception e) { + return; + } + } catch (Exception e) { + //btc解析 + try { + JSONObject transferBtc = btcUtils.getTransaction(txId); + parseBtcTransfer(transferBtc, txId); + } catch (Exception e1) { + return; + } + } + } + } + } + + //修改区块同步状态 + btcBlockNumberService.updateStatus(bigestBlock, BtcBlockNumberConstans.STATUS_Y); + + } + + /** + * 查询区块链遗漏的充值信息 + */ + @Scheduled(cron = "0 0/2 * * * ?") //2分钟每次 + public void crawlOmitTxIn() { + long nowTime = System.currentTimeMillis(); + //获取等待区块列表 + List blockNumberDTOList = btcBlockNumberService.listByStatus(BtcBlockNumberConstans.STATUS_W); + for (BtcBlockNumberDTO blockNumberDTO : blockNumberDTOList) { + //判断时间差 + if (nowTime - blockNumberDTO.getCreateTime().getTime() < BtcBlockNumberConstans.TIME_DIFFERENCE) { + continue; + } + //获取爬取次数 + int count = btcBlockRedisUtils.getBlockOptMapCount(blockNumberDTO.getBlockNumber()); + if (count >= 5) { // 爬取次数超过五次跳出 + //删除缓存 + btcBlockRedisUtils.delBlockOptMapCount(blockNumberDTO.getBlockNumber()); + //记录改为失败 + btcBlockNumberService.updateStatus(blockNumberDTO.getBlockNumber(), BtcBlockNumberConstans.STATUS_N); + continue; + } + + try { + //本区块hash + String thisBlockHash = btcUtils.getBlockHash(blockNumberDTO.getBlockNumber()); + //上一个区块hash + String previousBlockHash = btcUtils.getBlockHash(blockNumberDTO.getBlockNumber() - 1); + //交易数组 + JSONArray transferArr = btcUtils.listSinceBlock(previousBlockHash); + + int transferArrLen = transferArr.size(); + for (int i = 0; i < transferArrLen; i++) { + JSONObject transferObj = transferArr.getJSONObject(i); + //不为本次区块交易,不需要解析 + if (!thisBlockHash.equals(transferObj.getString("blockhash"))) { + continue; + } + if ("receive".equals(transferObj.getString("category"))) { + String toAddr = transferObj.getString("address"); + if (btcAddressSetRedisUtils.isExistsAddr(toAddr)) { + String txId = transferObj.getString("txid"); + + try { + //先用usdt解析,如果不能解析,则用btc解析 + JSONObject transferUsdt = usdtUtils.getTransaction(txId); + try { + //修改该usdt钱包余额,并插入一条充值记录 + parseUsdtTransfer(transferUsdt, txId); + } catch (Exception e) { + continue; + } + } catch (Exception e) { + //btc解析 + try { + JSONObject transferBtc = btcUtils.getTransaction(txId); + parseBtcTransfer(transferBtc, txId); + } catch (Exception e1) { + continue; + } + } + } + } + } + } catch (Exception e) { + continue; + } + + //爬取次数加1 + btcBlockRedisUtils.incrementBlockOptMapCount(blockNumberDTO.getBlockNumber()); + try { + //修改区块同步状态 + btcBlockNumberService.updateStatus(blockNumberDTO.getBlockNumber(), BtcBlockNumberConstans.STATUS_Y); + //删除缓存 + btcBlockRedisUtils.delBlockOptMapCount(blockNumberDTO.getBlockNumber()); + } catch (Exception e) { + } + + } + } + + /** + * 解析btc交易 + * + * @param transferBtc btc交易 + * @param txId 交易id + */ + private void parseBtcTransfer(JSONObject transferBtc, String txId) { + //交易金额,正数表示该交易增加钱包余额,负数表示该交易减少钱包余额 + if (transferBtc.getDouble("amount") <= 0) { + return; + } + + JSONArray details = transferBtc.getJSONArray("details"); + String toAddr = null; + String fromAddr = null; + double amount = 0; + double fee = 0; + + for (int i = 0; i < details.size(); i++) { + JSONObject jsonObject = details.getJSONObject(i); // 遍历 jsonarray 数组,把每一个对象转成 json 对象 + String category = jsonObject.getString("category"); + if ("receive".equals(category)) { + toAddr = jsonObject.getString("address"); + amount = jsonObject.getDouble("amount"); + } else if ("send".equals(category)) { + fromAddr = jsonObject.getString("address"); + fee = Math.abs(jsonObject.getDouble("fee")); + } + } + + if (btcAddressSetRedisUtils.isExistsAddr(toAddr)) { + //修改该钱包余额,并插入一条充值记录 + BtcWalletTransfer btcWalletTransfer = new BtcWalletTransfer(); + btcWalletTransfer.setId(UUID.randomUUID().toString()); + btcWalletTransfer.setHash(txId); + btcWalletTransfer.setFromAddr(fromAddr); + btcWalletTransfer.setToAddr(toAddr); + btcWalletTransfer.setAmount(Math.abs(amount)); + btcWalletTransfer.setGasPrice(fee); + btcWalletTransfer.setTokenId(BtcConstans.BTC_PROPERTY_ID); + btcWalletTransfer.setTokenSymbol(BtcConstans.BTC_SYMBOL); + btcWalletTransfer.setTransferType(BtcTransferConstans.TYPE_IN); + btcWalletTransfer.setStatus(BtcTransferConstans.STATUS_SUCCESS); + btcWalletTransfer.setCreateTime(new Date()); + btcWalletTransfer.setUpdateTime(btcWalletTransfer.getCreateTime()); + btcWalletTransferService.handleBlockRecharge(btcWalletTransfer); + } + } + + /** + * 解析usdt交易 + * + * @param transferUsdt usdt交易 + * @param txId 交易id + */ + private void parseUsdtTransfer(JSONObject transferUsdt, String txId) { + //交易是否有效交易 + if (transferUsdt.getBoolean("valid")) { + //是否USDT交易 + if (transferUsdt.getInteger("propertyid") == UsdtConstans.USDT_PROPERTY_ID) { + //是否充值到本地钱包地址 + String toAddr = transferUsdt.getString("referenceaddress"); + if (btcAddressSetRedisUtils.isExistsAddr(toAddr)) { + //修改该钱包余额,并插入一条充值记录 + BtcWalletTransfer btcWalletTransfer = new BtcWalletTransfer(); + btcWalletTransfer.setId(UUID.randomUUID().toString()); + btcWalletTransfer.setHash(txId); + btcWalletTransfer.setFromAddr(transferUsdt.getString("sendingaddress")); + btcWalletTransfer.setToAddr(toAddr); + btcWalletTransfer.setAmount(Math.abs(transferUsdt.getDouble("amount"))); + btcWalletTransfer.setGasPrice(transferUsdt.getDouble("fee")); + btcWalletTransfer.setTokenId(UsdtConstans.USDT_PROPERTY_ID); + btcWalletTransfer.setTokenSymbol(UsdtConstans.USDT_SYMBOL); + btcWalletTransfer.setTransferType(BtcTransferConstans.TYPE_IN); + btcWalletTransfer.setStatus(BtcTransferConstans.STATUS_SUCCESS); + btcWalletTransfer.setCreateTime(new Date()); + btcWalletTransfer.setUpdateTime(btcWalletTransfer.getCreateTime()); + btcWalletTransferService.handleBlockRecharge(btcWalletTransfer); + } + } + } + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/scheduleTask/WallertTimerTask.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/scheduleTask/WallertTimerTask.java new file mode 100644 index 0000000..874a305 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/scheduleTask/WallertTimerTask.java @@ -0,0 +1,86 @@ +package com.blockchain.server.btc.scheduleTask; + +import com.alibaba.fastjson.JSONObject; +import com.blockchain.server.btc.common.constants.BtcConstans; +import com.blockchain.server.btc.common.constants.BtcTransferConstans; +import com.blockchain.server.btc.entity.BtcWalletTransfer; +import com.blockchain.server.btc.rpc.BtcUtils; +import com.blockchain.server.btc.rpc.UsdtUtils; +import com.blockchain.server.btc.service.BtcWalletService; +import com.blockchain.server.btc.service.BtcWalletTransferService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * 钱包业务处理的定时器 + */ +//@Component +public class WallertTimerTask { + + + // 钱包提现处理 + private static ExecutorService txOutExecutorService = Executors.newSingleThreadExecutor(); + // 日志 + private static final Logger LOG = LoggerFactory.getLogger(WallertTimerTask.class); + + @Autowired + BtcWalletTransferService walletTransferService; + @Autowired + BtcWalletService btcWalletService; + @Autowired + BtcUtils btcUtils; + @Autowired + UsdtUtils usdtUtils; + + @Scheduled(cron = "0/30 * * * * ?") + public void crawlTxOut() { + txOutExecutorService.execute(new Runnable() { + @Override + public void run() { + crawlTxOutDispose(); // 爬取提现记录 + } + }); + } + + void crawlTxOutDispose() { + // 查询提现的未处理的数据 + List txs = walletTransferService.selectByTxTypeAndStatus(BtcTransferConstans.TYPE_OUT, + BtcTransferConstans.STATUS_ALREADY_OUT, 0, 99); + for (BtcWalletTransfer tx : txs) { + try { + if (BtcConstans.BTC_SYMBOL.equalsIgnoreCase(tx.getTokenSymbol())) { + JSONObject obj = btcUtils.getTransaction(tx.getHash()); + int confirmations = new Integer(obj.get("confirmations").toString()).intValue(); + if (confirmations >= 1) { + btcWalletService.updateTxOutSuccess(tx); + }else{ + btcWalletService.updateTxOutError(tx); + + } + } else { + JSONObject obj = usdtUtils.getTransaction(tx.getHash()); + int confirmations = new Integer(obj.get("confirmations").toString()).intValue(); + boolean valid = Boolean.getBoolean(obj.get("valid").toString()); + if (!valid) { + btcWalletService.updateTxOutError(tx); + } else if (valid && confirmations >= 1) { + btcWalletService.updateTxOutSuccess(tx); + } + } + } catch (Exception e) { + e.printStackTrace(); + continue; + } + } + + } + + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcApplicationService.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcApplicationService.java new file mode 100644 index 0000000..a3fffac --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcApplicationService.java @@ -0,0 +1,23 @@ +package com.blockchain.server.btc.service; + +import com.blockchain.server.btc.dto.BtcApplicationDTO; + +import java.util.List; + +public interface BtcApplicationService { + + /** + * 获取应用列表 + * + * @return + */ + List listApplication(); + + /** + * 验证是否有该应用体系的钱包 + * + * @param walletType 应用标识 + */ + void checkWalletType(String walletType); + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcBlockNumberService.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcBlockNumberService.java new file mode 100644 index 0000000..519ff13 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcBlockNumberService.java @@ -0,0 +1,40 @@ +package com.blockchain.server.btc.service; + +import com.blockchain.server.btc.dto.BtcBlockNumberDTO; + +import java.util.List; + +public interface BtcBlockNumberService { + + /** + * 新增已经同步的区块,不成功报异常 + * + * @param blockNumber 区块高度 + * @param status 状态 + * @return + */ + void insertBlockNumber(int blockNumber, String status); + + /** + * 修改区块装填,不成功报异常 + * + * @param blockNumber 区块高度 + * @return + */ + void updateStatus(int blockNumber, String status); + + /** + * 根据状态获取同步区块 + * + * @param status 状态 + * @return + */ + List listByStatus(String status); + + /** + * 查询已经同步的最大区块号 + * + * @return + */ + int selectBigest(); +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcTokenService.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcTokenService.java new file mode 100644 index 0000000..8befbc2 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcTokenService.java @@ -0,0 +1,48 @@ +package com.blockchain.server.btc.service; + +import com.blockchain.server.btc.dto.BtcTokenDTO; +import com.blockchain.server.btc.entity.BtcToken; + +import java.util.List; + +public interface BtcTokenService { + + /** + * 获取币种 + * + * @return + */ + List listToken(); + + /** + * 获取币种,根据tokenid + * + * @param tokenId 币种id + * @return + */ + BtcTokenDTO selectTokenById(Integer tokenId); + + /** + * 通过币种名称获取币种id,仅限系统 0-BTC,31-USDT + * + * @param tokenName 币种名称 + * @return + */ + int getAndVerifyTokenIdByName(String tokenName); + + /** + * 通过币种id获取币种名称,仅限系统 0-BTC,31-USDT + * + * @param tokenId 币种id + * @return + */ + String getAndVerifyTokenNameById(int tokenId); + + /** + * 獲取币种,根据名称 + * @param coinName + * @return + */ + BtcToken selectByTokenName(String coinName); + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcTransferAuditingService.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcTransferAuditingService.java new file mode 100644 index 0000000..e80a183 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcTransferAuditingService.java @@ -0,0 +1,16 @@ +package com.blockchain.server.btc.service; + +import com.blockchain.server.btc.entity.BtcTransferAuditing; + +public interface BtcTransferAuditingService { + + /** + * 插入一条交易审核记录 + * + * @param BtcTransferAuditing 审核记录 + * @return + */ + void insertAuditing(BtcTransferAuditing BtcTransferAuditing); + + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletKeyService.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletKeyService.java new file mode 100644 index 0000000..865ac64 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletKeyService.java @@ -0,0 +1,7 @@ +package com.blockchain.server.btc.service; + +public interface BtcWalletKeyService { + + void insertWalletKey(String address, String privateKey); + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletOutService.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletOutService.java new file mode 100644 index 0000000..25de3e5 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletOutService.java @@ -0,0 +1,14 @@ +package com.blockchain.server.btc.service; + +import com.blockchain.server.btc.entity.BtcWalletOut; + +public interface BtcWalletOutService { + + /** + * 插入一条资金提现资金钱包 + * + * @param btcWalletOut + */ + void InsertWalletOut(BtcWalletOut btcWalletOut); + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletService.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletService.java new file mode 100644 index 0000000..15e970c --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletService.java @@ -0,0 +1,105 @@ +package com.blockchain.server.btc.service; + +import com.blockchain.server.btc.dto.BtcWalletDTO; +import com.blockchain.server.btc.entity.BtcWallet; +import com.blockchain.server.btc.entity.BtcWalletTransfer; + +import java.util.Date; +import java.util.List; +import java.util.Set; + +public interface BtcWalletService { + + /** + * 创建托管账户 + * + * @param userOpenId 系统用户ID + * @return + */ + Integer insertWallet(String userOpenId); + + /** + * 获取用户某应用钱包的所有区块 + * + * @param userOpenId 用户id + * @param walletType 应用类型,币币交易等 + * @return + */ + List selectAllByUserOpenId(String userOpenId, String walletType); + + /** + * 获取用户钱包 + * + * @param userOpenId 用户id + * @param tokenId 币种id + * @param walletType 应用类型,币币交易等 + * @return + */ + BtcWalletDTO selectByUserOpenId(String userOpenId, Integer tokenId, String walletType); + + /** + * 获取用户钱包,通过地址 + * + * @param addr 用户id + * @param tokenId 币种id + * @param walletType 应用类型,币币交易等 + * @return + */ + BtcWalletDTO selectByAddr(String addr, Integer tokenId, String walletType); + + /** + * 加减钱包地址可用余额、冻结余额、总额 + * + * @param address 地址 + * @param tokenId 币种id + * @param freeAmount 可用余额加减数量 + * @param freezeAmount 冻结余额加减数量 + * @param totalAmount 总额加减数量 + * @param modifyTime 修改时间 + * @return + */ + Integer updateBalanceByAddrInRowLock(String address, Integer tokenId, Double freeAmount, Double freezeAmount, Double totalAmount, Date modifyTime); + + /** + * 获取所有用户托管钱包地址 + * + * @return + */ + Set getAllWalletAddr(); + + /** + * 应用提现失败业务 + * + * @param btcWalletTransfer 提现记录 + */ + void updateTxOutError(BtcWalletTransfer btcWalletTransfer); + + /** + * 应用提现成功业务 + * + * @param btcWalletTransfer 提现记录 + */ + void updateTxOutSuccess(BtcWalletTransfer btcWalletTransfer); + + /** + * 根据用户ID,钱包类型,币种名称查询钱包信息 + * + * @param userId + * @param walletType + * @param coinName + * @return + */ + BtcWallet findWallet(String userId, String walletType, String coinName); + + /** + * 划转 + * + * @param userId 用户ID + * @param fromType 钱包类型 + * @param toType 钱包类型 + * @param coinName 币种名称 + * @param amount 币种金额 + * @return + */ + BtcWalletTransfer handleTransfer(String userId, String fromType, String toType, String coinName, Double amount); +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletTransferService.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletTransferService.java new file mode 100644 index 0000000..0632ea0 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/BtcWalletTransferService.java @@ -0,0 +1,103 @@ +package com.blockchain.server.btc.service; + +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import com.blockchain.server.btc.dto.BtcWalletDTO; +import com.blockchain.server.btc.dto.BtcWalletTransferDTO; +import com.blockchain.server.btc.entity.BtcWalletTransfer; + +import java.util.Date; +import java.util.List; + +public interface BtcWalletTransferService { + + /** + * 插入一条钱包历史记录 + * + * @param btcWalletTransfer 记录对象 + * @return + */ + Integer insertTransfer(BtcWalletTransfer btcWalletTransfer); + + /** + * 获取用户钱包记录 + * + * @param userOpenId 用户id + * @param tokenId 币种id + * @param walletType 应用类型,CCT + * @return + */ + List selectTransfer(String userOpenId, Integer tokenId, String walletType); + + /** + * 修改钱包历史记录状态 + * + * @param id 记录id + * @param status 状态 + * @return + */ + Integer updateStatusById(String id, int status); + + /** + * 数据库不存在该笔交易 + * + * @param txId 交易id + * @return + */ + Boolean isNotExistsTransferIn(String txId); + + /** + * 处理同步区块充值 + * + * @param btcWalletTransfer 记录信息 + * @return + */ + void handleBlockRecharge(BtcWalletTransfer btcWalletTransfer); + + /** + * 处理CCT交易可用、冻结余额加减 + * + * @param walletOrderDTO 币币交易,冻结、解冻参数 + * @return + */ + BtcWalletDTO handleOrder(WalletOrderDTO walletOrderDTO); + + /** + * 处理加减可用、冻结余额,及其总额 + * + * @param walletChangeDTO 变动参数 + * @return + */ + Integer handleChange(WalletChangeDTO walletChangeDTO); + + /** + * 提现 + * + * @param userOpenId 用户id + * @param password 加密密码 + * @param toAddress 接收地址 + * @param tokenId 币种id + * @param amount 金额 + * @param walletType 应用类型,币币交易等 + */ + BtcWalletDTO handleWithdraw(String userOpenId, String password, String toAddress, Integer tokenId, double amount, String walletType); + + /** + * 根据记录类型与记录状态查询记录 + * + * @param transferType 记录类型 + * @param Status 记录状态 + * @param pageNum 当前页数 + * @param pageSize 页数展示条数 + * @return + */ + List selectByTxTypeAndStatus(String transferType, int Status, int pageNum, int pageSize); + + /** + * 修改记录状态 + * + * @param id + * @param status 记录状态 + */ + void updateStatus(String id, int status, Date date); +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/ConfigWalletParamService.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/ConfigWalletParamService.java new file mode 100644 index 0000000..f1b1108 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/ConfigWalletParamService.java @@ -0,0 +1,18 @@ +package com.blockchain.server.btc.service; + +import com.blockchain.common.base.dto.GasDTO; + +/** + * 配置表——业务接口 + */ +public interface ConfigWalletParamService { + + /** + * 获取手续费详情 + * + * @param tokenSymbol 币种 + * @return + */ + GasDTO getGasConfig(String tokenSymbol); + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcApplicationServiceImpl.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcApplicationServiceImpl.java new file mode 100644 index 0000000..236bb57 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcApplicationServiceImpl.java @@ -0,0 +1,34 @@ +package com.blockchain.server.btc.service.impl; + +import com.blockchain.server.btc.common.enums.BtcEnums; +import com.blockchain.server.btc.common.exception.BtcException; +import com.blockchain.server.btc.dto.BtcApplicationDTO; +import com.blockchain.server.btc.mapper.BtcApplicationMapper; +import com.blockchain.server.btc.service.BtcApplicationService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class BtcApplicationServiceImpl implements BtcApplicationService { + @Autowired + private BtcApplicationMapper btcApplicationMapper; + + @Override + public List listApplication() { + return btcApplicationMapper.listApplication(); + } + + @Override + public void checkWalletType(String walletType) { + List list = listApplication(); + for (BtcApplicationDTO row : list) { + if (walletType.equalsIgnoreCase(row.getAppId())) { + return; + } + } + throw new BtcException(BtcEnums.INEXISTENCE_WALLETTYPE); + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcBlockNumberServiceImpl.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcBlockNumberServiceImpl.java new file mode 100644 index 0000000..db9c388 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcBlockNumberServiceImpl.java @@ -0,0 +1,68 @@ +package com.blockchain.server.btc.service.impl; + +import com.blockchain.server.btc.common.constants.BtcBlockNumberConstans; +import com.blockchain.server.btc.common.enums.BtcEnums; +import com.blockchain.server.btc.common.exception.BtcException; +import com.blockchain.server.btc.dto.BtcBlockNumberDTO; +import com.blockchain.server.btc.entity.BtcBlockNumber; +import com.blockchain.server.btc.mapper.BtcBlockNumberMapper; +import com.blockchain.server.btc.rpc.BtcUtils; +import com.blockchain.server.btc.service.BtcBlockNumberService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; + +@Service +public class BtcBlockNumberServiceImpl implements BtcBlockNumberService { + @Autowired + private BtcBlockNumberMapper btcBlockNumberMapper; + @Autowired + private BtcUtils btcUtils; + + @Override + public void insertBlockNumber(int blockNumber, String status) { + BtcBlockNumber btcBlockNumber = new BtcBlockNumber(); + btcBlockNumber.setBlockNumber(blockNumber); + btcBlockNumber.setStatus(status); + btcBlockNumber.setCreateTime(new Date()); + btcBlockNumber.setUpdateTime(btcBlockNumber.getCreateTime()); + int count = btcBlockNumberMapper.insertSelective(btcBlockNumber); + if (count != 1) { + throw new BtcException(BtcEnums.INSERT_BLOCKNUMBER_ERROR); + } + } + + @Override + public void updateStatus(int blockNumber, String status) { + BtcBlockNumber btcBlockNumber = new BtcBlockNumber(); + btcBlockNumber.setBlockNumber(blockNumber); + btcBlockNumber.setStatus(status); + btcBlockNumber.setUpdateTime(new Date()); + int countRow = btcBlockNumberMapper.updateByPrimaryKeySelective(btcBlockNumber); + if (countRow != 1) { + throw new BtcException(BtcEnums.UPDATE_BLOCK_NUMBER_STATUS_ERROR); + } + } + + @Override + public List listByStatus(String status) { + return btcBlockNumberMapper.listByStatus(status); + } + + @Override + public int selectBigest() { + Integer blockNumber = btcBlockNumberMapper.selectBigest(); + if (blockNumber == null) { + try { + blockNumber = btcUtils.getBlockCount(); + this.insertBlockNumber(blockNumber, BtcBlockNumberConstans.STATUS_Y); + } catch (Exception e) { + e.printStackTrace(); + } + } + return blockNumber; + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcTokenServiceImpl.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcTokenServiceImpl.java new file mode 100644 index 0000000..5c79cfa --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcTokenServiceImpl.java @@ -0,0 +1,69 @@ +package com.blockchain.server.btc.service.impl; + +import com.blockchain.server.btc.common.constants.BtcConstans; +import com.blockchain.server.btc.common.constants.UsdtConstans; +import com.blockchain.server.btc.common.enums.BtcEnums; +import com.blockchain.server.btc.common.exception.BtcException; +import com.blockchain.server.btc.dto.BtcTokenDTO; +import com.blockchain.server.btc.entity.BtcToken; +import com.blockchain.server.btc.mapper.BtcTokenMapper; +import com.blockchain.server.btc.service.BtcTokenService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class BtcTokenServiceImpl implements BtcTokenService { + @Autowired + private BtcTokenMapper btcTokenMapper; + + @Autowired + private BtcTokenService btcTokenService; + + @Override + public List listToken() { + return btcTokenMapper.listToken(); + } + + @Override + public BtcTokenDTO selectTokenById(Integer tokenId) { + //校验币种id + btcTokenService.getAndVerifyTokenNameById(tokenId); + return btcTokenMapper.selectTokenById(tokenId); + } + + @Override + public int getAndVerifyTokenIdByName(String tokenName) { + if (tokenName.equalsIgnoreCase(BtcConstans.BTC_SYMBOL)) { + return BtcConstans.BTC_PROPERTY_ID; + } else if (tokenName.equalsIgnoreCase(UsdtConstans.USDT_SYMBOL)) { + return UsdtConstans.USDT_PROPERTY_ID; + } else { + throw new BtcException(BtcEnums.INEXISTENCE_TOKENNAME); + } + } + + @Override + public String getAndVerifyTokenNameById(int tokenId) { + if (tokenId == BtcConstans.BTC_PROPERTY_ID) { + return BtcConstans.BTC_SYMBOL; + } else if (tokenId == UsdtConstans.USDT_PROPERTY_ID) { + return UsdtConstans.USDT_SYMBOL; + } else { + throw new BtcException(BtcEnums.INEXISTENCE_TOKENID); + } + } + + @Override + public BtcToken selectByTokenName(String coinName) { + BtcToken where = new BtcToken(); + where.setTokenSymbol(coinName); + BtcToken token = btcTokenMapper.selectOne(where); + if(token == null){ + throw new BtcException(BtcEnums.INEXISTENCE_TOKENNAME); + } + return token; + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcTransferAuditingServiceImpl.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcTransferAuditingServiceImpl.java new file mode 100644 index 0000000..399e717 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcTransferAuditingServiceImpl.java @@ -0,0 +1,24 @@ +package com.blockchain.server.btc.service.impl; + +import com.blockchain.server.btc.common.enums.BtcEnums; +import com.blockchain.server.btc.common.exception.BtcException; +import com.blockchain.server.btc.entity.BtcTransferAuditing; +import com.blockchain.server.btc.mapper.BtcTransferAuditingMapper; +import com.blockchain.server.btc.service.BtcTransferAuditingService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class BtcTransferAuditingServiceImpl implements BtcTransferAuditingService { + @Autowired + private BtcTransferAuditingMapper btcTransferAuditingMapper; + + @Override + public void insertAuditing(BtcTransferAuditing btcTransferAuditing) { + int countIa = btcTransferAuditingMapper.insertSelective(btcTransferAuditing); + if (countIa != 1) { + throw new BtcException(BtcEnums.INSERT_AUDITING_ERROR); + } + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletKeyServiceImpl.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletKeyServiceImpl.java new file mode 100644 index 0000000..1fe90df --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletKeyServiceImpl.java @@ -0,0 +1,33 @@ +package com.blockchain.server.btc.service.impl; + +import com.blockchain.server.btc.common.enums.BtcEnums; +import com.blockchain.server.btc.common.exception.BtcException; +import com.blockchain.server.btc.common.util.BtcRASUtils; +import com.blockchain.server.btc.entity.BtcWalletKey; +import com.blockchain.server.btc.mapper.BtcWalletKeyMapper; +import com.blockchain.server.btc.service.BtcWalletKeyService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class BtcWalletKeyServiceImpl implements BtcWalletKeyService { + @Autowired + private BtcWalletKeyMapper btcWalletKeyMapper; + + @Override + public void insertWalletKey(String address, String privateKey) { + BtcWalletKey btcWalletKey = new BtcWalletKey(); + btcWalletKey.setAddr(address); + try { + privateKey = BtcRASUtils.encrypt(privateKey); + } catch (Exception e) { + throw new BtcException(BtcEnums.CREATE_WALLET_ERROR); + } + btcWalletKey.setPrivateKey(privateKey); + int countIw = btcWalletKeyMapper.insertSelective(btcWalletKey); + if (countIw != 1) { + throw new BtcException(BtcEnums.CREATE_WALLET_ERROR); + } + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletOutServiceImpl.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletOutServiceImpl.java new file mode 100644 index 0000000..1e50ecf --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletOutServiceImpl.java @@ -0,0 +1,28 @@ +package com.blockchain.server.btc.service.impl; + +import com.blockchain.server.btc.common.enums.BtcEnums; +import com.blockchain.server.btc.common.exception.BtcException; +import com.blockchain.server.btc.entity.BtcWalletOut; +import com.blockchain.server.btc.mapper.BtcWalletOutMapper; +import com.blockchain.server.btc.service.BtcWalletOutService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.UUID; + +@Service +public class BtcWalletOutServiceImpl implements BtcWalletOutService { + + @Autowired + private BtcWalletOutMapper btcWalletOutMapper; + + @Override + public void InsertWalletOut(BtcWalletOut btcWalletOut) { + btcWalletOut.setId(UUID.randomUUID().toString()); + int countIwo = btcWalletOutMapper.insertSelective(btcWalletOut); + if (countIwo != 1) { + throw new BtcException(BtcEnums.INSERT_WALLET_OUT_ERROR); + } + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletServiceImpl.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletServiceImpl.java new file mode 100644 index 0000000..85ced5c --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletServiceImpl.java @@ -0,0 +1,194 @@ +package com.blockchain.server.btc.service.impl; + +import com.blockchain.server.btc.common.constants.BtcTransferConstans; +import com.blockchain.server.btc.common.enums.BtcEnums; +import com.blockchain.server.btc.common.exception.BtcException; +import com.blockchain.server.btc.common.util.BtcAddressSetRedisUtils; +import com.blockchain.server.btc.dto.BtcApplicationDTO; +import com.blockchain.server.btc.dto.BtcTokenDTO; +import com.blockchain.server.btc.dto.BtcWalletDTO; +import com.blockchain.server.btc.entity.BtcToken; +import com.blockchain.server.btc.entity.BtcWallet; +import com.blockchain.server.btc.entity.BtcWalletTransfer; +import com.blockchain.server.btc.mapper.BtcWalletMapper; +import com.blockchain.server.btc.rpc.BtcUtils; +import com.blockchain.server.btc.service.BtcApplicationService; +import com.blockchain.server.btc.service.BtcTokenService; +import com.blockchain.server.btc.service.BtcWalletService; +import com.blockchain.server.btc.service.BtcWalletTransferService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +@Service +public class BtcWalletServiceImpl implements BtcWalletService { + @Autowired + private BtcUtils btcUtils; + + @Autowired + private BtcWalletTransferService btcWalletTransferService; + + @Autowired + private BtcAddressSetRedisUtils btcAddressSetRedisUtils; + + @Autowired + private BtcWalletMapper btcWalletMapper; + + @Autowired + private BtcApplicationService btcApplicationService; + + @Autowired + private BtcTokenService btcTokenService; + + @Autowired + private BtcWalletKeyServiceImpl btcWalletKeyService; + + @Override + @Transactional + public Integer insertWallet(String userOpenId) { + List btcApplicationDTOList = btcApplicationService.listApplication(); + for (BtcApplicationDTO btcApplicationDTO : btcApplicationDTOList) { + String address = ""; + try { + address = btcUtils.getNewAddress(); + String privateKey = btcUtils.getPrivateKeyByAddress(address); + btcAddressSetRedisUtils.insert(address); + btcWalletKeyService.insertWalletKey(address, privateKey); + } catch (Exception e) { + throw new BtcException(BtcEnums.CREATE_WALLET_ERROR); + } + List btcTokenDTOList = btcTokenService.listToken(); + for (BtcTokenDTO btcTokenDTO : btcTokenDTOList) { + BtcWallet btcWallet = new BtcWallet(); + btcWallet.setAddr(address); + btcWallet.setTokenId(btcTokenDTO.getTokenId()); + btcWallet.setUserOpenId(userOpenId); + btcWallet.setTokenSymbol(btcTokenDTO.getTokenSymbol()); + btcWallet.setBalance(0.0); + btcWallet.setFreeBalance(0.0); + btcWallet.setFreezeBalance(0.0); + btcWallet.setWalletType(btcApplicationDTO.getAppId()); + btcWallet.setCreateTime(new Date()); + btcWallet.setUpdateTime(btcWallet.getCreateTime()); + + int count = btcWalletMapper.insertSelective(btcWallet); + if (count != 1) { + throw new BtcException(BtcEnums.CREATE_WALLET_ERROR); + } + } + } + + return 1; + } + + @Override + public List selectAllByUserOpenId(String userOpenId, String walletType) { + //校验钱包类型 + btcApplicationService.checkWalletType(walletType); + return btcWalletMapper.selectAllByUserOpenId(userOpenId, walletType); + } + + @Override + public BtcWalletDTO selectByUserOpenId(String userOpenId, Integer tokenId, String walletType) { + return btcWalletMapper.selectByUserOpenId(userOpenId, tokenId, walletType); + } + + @Override + public BtcWalletDTO selectByAddr(String addr, Integer tokenId, String walletType) { + return btcWalletMapper.selectByAddr(addr, tokenId, walletType); + } + + @Override + public Integer updateBalanceByAddrInRowLock(String address, Integer tokenId, Double freeAmount, Double freezeAmount, Double totalAmount, Date modifyTime) { + return btcWalletMapper.updateBalanceByAddrInRowLock(address, tokenId, freeAmount, freezeAmount, totalAmount, modifyTime); + } + + @Override + public Set getAllWalletAddr() { + return btcWalletMapper.getAllWalletAddr(); + } + @Override + @Transactional + public void updateTxOutError(BtcWalletTransfer btcWalletTransfer) { + BtcWalletDTO wallet = selectByAddr(btcWalletTransfer.getFromAddr(), btcWalletTransfer.getTokenId(), null); + Date date = new Date(); + // 业务操作(1)- 恢复提现余额,扣除手续费 + Double amount = btcWalletTransfer.getAmount(); + this.updateBalanceByAddrInRowLock(wallet.getAddr(), wallet.getTokenId(), amount, amount * -1, Double.valueOf(0),date); + // 业务操作(2)- 修改记录状态 + btcWalletTransferService.updateStatus(btcWalletTransfer.getId(), BtcTransferConstans.STATUS_FILE, date); + } + + @Override + @Transactional + public void updateTxOutSuccess(BtcWalletTransfer btcWalletTransfer) { + BtcWalletDTO wallet = selectByAddr(btcWalletTransfer.getFromAddr(), btcWalletTransfer.getTokenId(), null); + Date date = new Date(); + // 业务操作(1)- 恢复提现余额,扣除手续费 + Double amount = btcWalletTransfer.getAmount(); + this.updateBalanceByAddrInRowLock(wallet.getAddr(), wallet.getTokenId(), Double.valueOf(0), amount * -1, amount * -1,date); + // 业务操作(2)- 修改记录状态 + btcWalletTransferService.updateStatus(btcWalletTransfer.getId(), BtcTransferConstans.STATUS_SUCCESS, date); + } + + @Override + public BtcWallet findWallet(String userId,String walletType, String coinName) { + BtcWallet wallet = new BtcWallet(); + wallet.setWalletType(walletType); + wallet.setTokenSymbol(coinName); + wallet.setUserOpenId(userId); + BtcWallet btcWallet = btcWalletMapper.selectOne(wallet); + if(btcWallet == null) { + throw new BtcException(BtcEnums.INEXISTENCE_WALLET); + } + return btcWallet; + } + + @Override + @Transactional + public BtcWalletTransfer handleTransfer(String userId, String fromType, String toType, String coinName, Double amount) { + Date end = new Date(); + amount = new BigDecimal(amount.toString()).abs().doubleValue(); + //校验钱包类型 + btcApplicationService.checkWalletType(fromType); + btcApplicationService.checkWalletType(toType); + BtcToken btcToken = btcTokenService.selectByTokenName(coinName); + BtcWallet fromWallet = this.findWallet(userId,fromType,coinName); + BtcWallet toWallet = this.findWallet(userId,toType,coinName); + //该用户减去提现可用余额、总额 + int countUb = this.updateBalanceByAddrInRowLock(fromWallet.getAddr(), fromWallet.getTokenId(), -amount, 0.0, -amount, end); + if (countUb != 1) { + throw new BtcException(BtcEnums.WITHDRAW_ERROR); + } + //接收用户加上可用余额、总额 + int countUbR = this.updateBalanceByAddrInRowLock(toWallet.getAddr(), toWallet.getTokenId(), amount, 0.0, amount, end); + if (countUbR != 1) { + throw new BtcException(BtcEnums.WITHDRAW_ERROR); + } + + //并插入一条提现记录,站内快速转账 + BtcWalletTransfer btcWalletTransfer = new BtcWalletTransfer(); + btcWalletTransfer.setId(UUID.randomUUID().toString()); + btcWalletTransfer.setFromAddr(fromWallet.getAddr()); + btcWalletTransfer.setToAddr(toWallet.getAddr()); + btcWalletTransfer.setAmount(Math.abs(amount)); + btcWalletTransfer.setTokenId(btcToken.getTokenId()); + btcWalletTransfer.setTokenSymbol(btcToken.getTokenSymbol()); + btcWalletTransfer.setTransferType(BtcTransferConstans.TYPE_FAST); + btcWalletTransfer.setStatus(BtcTransferConstans.STATUS_SUCCESS); + btcWalletTransfer.setCreateTime(end); + btcWalletTransfer.setUpdateTime(end); + int countIt = btcWalletTransferService.insertTransfer(btcWalletTransfer); + if (countIt != 1) { + throw new BtcException(BtcEnums.WITHDRAW_ERROR); + } + return btcWalletTransfer; + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletTransferServiceImpl.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletTransferServiceImpl.java new file mode 100644 index 0000000..5624032 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/BtcWalletTransferServiceImpl.java @@ -0,0 +1,335 @@ +package com.blockchain.server.btc.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.dto.GasDTO; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import com.blockchain.common.base.exception.RPCException; +import com.blockchain.server.btc.common.constants.BtcTransferConstans; +import com.blockchain.server.btc.common.enums.BtcEnums; +import com.blockchain.server.btc.common.exception.BtcException; +import com.blockchain.server.btc.common.util.CheckEthFeginResult; +import com.blockchain.server.btc.dto.BtcWalletDTO; +import com.blockchain.server.btc.dto.BtcWalletTransferDTO; +import com.blockchain.server.btc.entity.BtcWalletTransfer; +import com.blockchain.server.btc.feign.EthServerFegin; +import com.blockchain.server.btc.feign.UserServerFegin; +import com.blockchain.server.btc.mapper.BtcWalletTransferMapper; +import com.blockchain.server.btc.rpc.BtcUtils; +import com.blockchain.server.btc.service.*; +import com.codingapi.tx.annotation.ITxTransaction; +import com.codingapi.tx.annotation.TxTransaction; +import com.github.pagehelper.PageHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +@Service +public class BtcWalletTransferServiceImpl implements BtcWalletTransferService, ITxTransaction { + @Autowired + private BtcWalletTransferMapper btcWalletTransferMapper; + + @Autowired + private BtcWalletService btcWalletService; + + @Autowired + private BtcApplicationService btcApplicationService; + + @Autowired + private BtcTokenService btcTokenService; + + @Autowired + private BtcUtils btcUtils; + + @Autowired + private ConfigWalletParamService walletParamService; + + @Autowired + private EthServerFegin ethServerFegin; + + @Autowired + private UserServerFegin userServerFegin; + + @Override + public Integer insertTransfer(BtcWalletTransfer btcWalletTransfer) { + return btcWalletTransferMapper.insertSelective(btcWalletTransfer); + } + + @Override + public List selectTransfer(String userOpenId, Integer tokenId, String walletType) { + //校验钱包类型 + btcApplicationService.checkWalletType(walletType); + //校验币种id + btcTokenService.getAndVerifyTokenNameById(tokenId); + BtcWalletDTO btcWalletDTO = btcWalletService.selectByUserOpenId(userOpenId, tokenId, walletType); + String addr = btcWalletDTO.getAddr(); + return btcWalletTransferMapper.selectTransfer(addr, tokenId); + } + + @Override + public Integer updateStatusById(String id, int status) { + BtcWalletTransfer btcWalletTransfer = new BtcWalletTransfer(); + btcWalletTransfer.setId(id); + btcWalletTransfer.setStatus(status); + return btcWalletTransferMapper.updateByPrimaryKeySelective(btcWalletTransfer); + } + + @Override + public Boolean isNotExistsTransferIn(String txId) { + String id = btcWalletTransferMapper.selectIdByTxIdAndType(txId, BtcTransferConstans.TYPE_IN); + return StringUtils.isEmpty(id); + } + + @Override + @Transactional + public void handleBlockRecharge(BtcWalletTransfer btcWalletTransfer) { + if (isNotExistsTransferIn(btcWalletTransfer.getHash())) { + //插入充值记录 + int countIt = insertTransfer(btcWalletTransfer); + if (countIt != 1) { + throw new BtcException(BtcEnums.PARSE_TRANSFER_IN_ERROR); + } + //增加钱包可用余额、总额 + int countUb = btcWalletService.updateBalanceByAddrInRowLock(btcWalletTransfer.getToAddr(), btcWalletTransfer.getTokenId(), + btcWalletTransfer.getAmount(), 0.0, btcWalletTransfer.getAmount(), btcWalletTransfer.getCreateTime()); + if (countUb != 1) { + throw new BtcException(BtcEnums.PARSE_TRANSFER_IN_ERROR); + } + } + } + + @Override + @Transactional + @TxTransaction + public BtcWalletDTO handleOrder(WalletOrderDTO walletOrderDTO) { + double freeBalance = walletOrderDTO.getFreeBalance().doubleValue(); + double freezeBalance = walletOrderDTO.getFreezeBalance().doubleValue(); + + int tokenId = btcTokenService.getAndVerifyTokenIdByName(walletOrderDTO.getTokenName()); + //查询用户钱包 + BtcWalletDTO btcWalletDTO = btcWalletService.selectByUserOpenId(walletOrderDTO.getUserId(), tokenId, walletOrderDTO.getWalletType()); + if (btcWalletDTO == null) { + throw new BtcException(BtcEnums.NO_WALLET_ERROR); + } + + if (btcWalletDTO.getFreeBalance() + freeBalance < 0 || btcWalletDTO.getFreezeBalance() + freezeBalance < 0) { + throw new BtcException(BtcEnums.FREEBALANCE_NOT_ENOUGH); + } + + //加减钱包可用余额、冻结余额,之间转换 + int countUb = btcWalletService.updateBalanceByAddrInRowLock(btcWalletDTO.getAddr(), btcWalletDTO.getTokenId(), freeBalance, freezeBalance, 0.0, new Date()); + if (countUb != 1) { + throw new BtcException(BtcEnums.TRANSFER_ERROR); + } + + //返回加减余额后的数据 + return btcWalletService.selectByUserOpenId(walletOrderDTO.getUserId(), tokenId, walletOrderDTO.getWalletType()); + } + + @Override + @Transactional + @TxTransaction + public Integer handleChange(WalletChangeDTO walletChangeDTO) { + double freeBalance = walletChangeDTO.getFreeBalance().doubleValue(); + double freezeBalance = walletChangeDTO.getFreezeBalance().doubleValue(); + double totalBalance = freeBalance + freezeBalance; + + Date now = new Date(); + + int tokenId = btcTokenService.getAndVerifyTokenIdByName(walletChangeDTO.getTokenName()); + //查询用户钱包 + BtcWalletDTO btcWalletDTO = btcWalletService.selectByUserOpenId(walletChangeDTO.getUserId(), tokenId, walletChangeDTO.getWalletType()); + if (btcWalletDTO == null) { + throw new BtcException(BtcEnums.NO_WALLET_ERROR); + } + + if (btcWalletDTO.getFreeBalance() + freeBalance < 0 || btcWalletDTO.getFreezeBalance() + freezeBalance < 0) { + throw new BtcException(BtcEnums.BALANCE_NOT_ENOUGH); + } + + if (freeBalance != 0 || freezeBalance != 0) { + //加减钱包可用余额、冻结余额、总额 + int countUb = btcWalletService.updateBalanceByAddrInRowLock(btcWalletDTO.getAddr(), btcWalletDTO.getTokenId(), freeBalance, freezeBalance, totalBalance, now); + if (countUb != 1) { + throw new BtcException(BtcEnums.TRANSFER_ERROR); + } + } + + //插入一条交易记录 + BtcWalletTransfer btcWalletTransfer = new BtcWalletTransfer(); + if (totalBalance < 0) { + btcWalletTransfer.setFromAddr(btcWalletDTO.getAddr()); + btcWalletTransfer.setToAddr(null); + } else { + btcWalletTransfer.setFromAddr(null); + btcWalletTransfer.setToAddr(btcWalletDTO.getAddr()); + } + btcWalletTransfer.setId(UUID.randomUUID().toString()); + btcWalletTransfer.setHash(walletChangeDTO.getRecordId()); + + btcWalletTransfer.setAmount(Math.abs(totalBalance)); + btcWalletTransfer.setTokenId(tokenId); + btcWalletTransfer.setTokenSymbol(walletChangeDTO.getTokenName()); + btcWalletTransfer.setGasPrice(walletChangeDTO.getGasBalance().doubleValue()); + btcWalletTransfer.setTransferType(walletChangeDTO.getWalletType()); + btcWalletTransfer.setStatus(BtcTransferConstans.STATUS_SUCCESS); + btcWalletTransfer.setCreateTime(now); + btcWalletTransfer.setUpdateTime(now); + int countIt = insertTransfer(btcWalletTransfer); + + if (countIt != 1) { + throw new BtcException(BtcEnums.TRANSFER_ERROR); + } + + return 1; + } + + @Override + @Transactional + public BtcWalletDTO handleWithdraw(String userOpenId, String password, String toAddress, Integer tokenId, double amount, String walletType) { + //校验钱包类型 + btcApplicationService.checkWalletType(walletType); + //校验币种id + String tokenSymbol = btcTokenService.getAndVerifyTokenNameById(tokenId); + + //获取提现手续费 + GasDTO gasDTO = walletParamService.getGasConfig(tokenSymbol); + //与最小提现数额做对比 + if (gasDTO.getMinWdAmount().compareTo(new BigDecimal(amount)) > 0) { + throw new BtcException(BtcEnums.LOW_WITHDRAW_AMOUNT_ERROR); + } + + //检验密码 + CheckEthFeginResult.checkIsPassword(ethServerFegin.isPassword(password)); + + //验证提现地址是否有效 + JSONObject vaObj = null; + try { + vaObj = btcUtils.validateAddress(toAddress); + } catch (Exception e) { + throw new BtcException(BtcEnums.ADDRESS_ERROR); + } + //验证提现地址是否有效 + if (!vaObj.getBoolean("isvalid")) { + throw new BtcException(BtcEnums.ADDRESS_ERROR); + } + + //获取钱包余额 + BtcWalletDTO btcWalletDTO = btcWalletService.selectByUserOpenId(userOpenId, tokenId, walletType); + if (btcWalletDTO.getFreeBalance() + amount < 0) { + throw new BtcException(BtcEnums.FREEBALANCE_NOT_ENOUGH); + } + //用户转出钱包地址 + String fromAddress = btcWalletDTO.getAddr(); + + Date now = new Date(); + + //判断是否属于节点钱包 + if (vaObj.getBoolean("ismine")) { + //*********** 是,快速转账,数据库划转 *********** + + //该用户减去提现可用余额、总额 + int countUb = btcWalletService.updateBalanceByAddrInRowLock(fromAddress, tokenId, -amount, 0.0, -amount, now); + if (countUb != 1) { + throw new BtcException(BtcEnums.WITHDRAW_ERROR); + } + //接收用户加上可用余额、总额 + int countUbR = btcWalletService.updateBalanceByAddrInRowLock(toAddress, tokenId, amount, 0.0, amount, now); + if (countUbR != 1) { + throw new BtcException(BtcEnums.WITHDRAW_ERROR); + } + + //并插入一条提现记录,站内快速转账 + BtcWalletTransfer btcWalletTransfer = new BtcWalletTransfer(); + btcWalletTransfer.setId(UUID.randomUUID().toString()); +// btcWalletTransfer.setHash(null); + btcWalletTransfer.setFromAddr(fromAddress); + btcWalletTransfer.setToAddr(toAddress); + btcWalletTransfer.setAmount(Math.abs(amount)); +// btcWalletTransfer.setGasPrice(null); + btcWalletTransfer.setTokenId(tokenId); + btcWalletTransfer.setTokenSymbol(tokenSymbol); + btcWalletTransfer.setTransferType(BtcTransferConstans.TYPE_FAST); + btcWalletTransfer.setStatus(BtcTransferConstans.STATUS_SUCCESS); + btcWalletTransfer.setCreateTime(now); + btcWalletTransfer.setUpdateTime(now); + int countIt = insertTransfer(btcWalletTransfer); + if (countIt != 1) { + throw new BtcException(BtcEnums.WITHDRAW_ERROR); + } + } else { + //*********** 否,区块链转账 *********** + // 查询是否存在提现黑名单中 + // 抛出错误表示用户禁止提现 + userServerFegin.verifyBanWithdraw(userOpenId); + // 判断用户是否存在提现白名单中 + ResultDTO resultDTO = userServerFegin.verifyFreeWithdraw(userOpenId); + if (resultDTO.getCode() != BaseConstant.REQUEST_SUCCESS) + throw new RPCException(resultDTO.getCode(), resultDTO.getMsg()); + // 用户存在提现白名单中,设置提现手续费为零 + if (resultDTO.getData()) gasDTO.setGasPrice(BigDecimal.ZERO); + // 余额加上扣除手续费 + //该用户减去提现可用余额、加上冻结余额 + int countUb = btcWalletService.updateBalanceByAddrInRowLock(fromAddress, tokenId, -amount, amount, 0.0, now); + if (countUb != 1) { + throw new BtcException(BtcEnums.WITHDRAW_ERROR); + } + //并插入一条提现记录 + BtcWalletTransfer btcWalletTransfer = new BtcWalletTransfer(); + btcWalletTransfer.setId(UUID.randomUUID().toString()); +// btcWalletTransfer.setHash(null); + btcWalletTransfer.setFromAddr(fromAddress); + btcWalletTransfer.setToAddr(toAddress); + btcWalletTransfer.setAmount(Math.abs(amount)); + btcWalletTransfer.setTokenId(tokenId); + btcWalletTransfer.setTokenSymbol(tokenSymbol); + //设置手续费 + //TODO 扣减不同币种手续费 + btcWalletTransfer.setGasPrice(gasDTO.getGasPrice().doubleValue()); + btcWalletTransfer.setGasTokenType(gasDTO.getGasTokenType()); + btcWalletTransfer.setGasTokenSymbol(gasDTO.getGasTokenSymbol()); + btcWalletTransfer.setGasTokenName(gasDTO.getGasTokenName()); + btcWalletTransfer.setTransferType(BtcTransferConstans.TYPE_OUT); + btcWalletTransfer.setStatus(BtcTransferConstans.STATUS_FIRST_TRIAL); + btcWalletTransfer.setCreateTime(now); + btcWalletTransfer.setUpdateTime(now); + int countIt = insertTransfer(btcWalletTransfer); + if (countIt != 1) { + throw new BtcException(BtcEnums.WITHDRAW_ERROR); + } + } + + //返回加减余额后的数据 + return btcWalletService.selectByAddr(fromAddress, tokenId, walletType); + } + + @Override + public List selectByTxTypeAndStatus(String transferType, int Status, int pageNum, int pageSize) { + PageHelper.startPage(pageNum, pageSize); // 分页处理 + BtcWalletTransfer where = new BtcWalletTransfer(); + where.setTransferType(transferType); + where.setStatus(Status); + return btcWalletTransferMapper.select(where); + } + + @Override + @Transactional + public void updateStatus(String id, int status, Date date) { + BtcWalletTransfer ethWalletTransfer = btcWalletTransferMapper.selectByPrimaryKey(id); + ethWalletTransfer.setStatus(status); + ethWalletTransfer.setUpdateTime(date); + // 失败抛出异常 + int row = btcWalletTransferMapper.updateByPrimaryKeySelective(ethWalletTransfer); + if (row == 0) { + throw new BtcException(BtcEnums.SERVER_IS_TOO_BUSY); + } + } +} diff --git a/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/ConfigWalletParamServiceImpl.java b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/ConfigWalletParamServiceImpl.java new file mode 100644 index 0000000..b4995d0 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/java/com/blockchain/server/btc/service/impl/ConfigWalletParamServiceImpl.java @@ -0,0 +1,35 @@ +package com.blockchain.server.btc.service.impl; + +import com.blockchain.common.base.dto.GasDTO; +import com.blockchain.common.base.util.JsonUtils; +import com.blockchain.server.btc.common.constants.BtcConfigConstants; +import com.blockchain.server.btc.entity.ConfigWalletParam; +import com.blockchain.server.btc.mapper.ConfigWalletParamMapper; +import com.blockchain.server.btc.service.ConfigWalletParamService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 配置表——业务接口 + * + * @date 2019年2月16日17:09:19 + */ +@Service +public class ConfigWalletParamServiceImpl implements ConfigWalletParamService { + @Autowired + private ConfigWalletParamMapper configMapper; + + @Override + public GasDTO getGasConfig(String tokenSymbol) { + ConfigWalletParam where = new ConfigWalletParam(); + where.setModuleType(BtcConfigConstants.MODULE_TYPE); + where.setStatus(BtcConfigConstants.STATUS_NORMAL); + String paramName = BtcConfigConstants.GAS_CONFIG + tokenSymbol; + where.setParamName(paramName.toLowerCase()); + ConfigWalletParam config = configMapper.selectOne(where); + if (config == null) return null; + String val = config.getParamValue(); + return JsonUtils.jsonToPojo(val, GasDTO.class); + } + +} diff --git a/blockchain-server/blockchain-server-btc/src/main/resources/application.yml b/blockchain-server/blockchain-server-btc/src/main/resources/application.yml new file mode 100644 index 0000000..1dac60c --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/resources/application.yml @@ -0,0 +1,3 @@ +#日志配置路径 +logging: + config: classpath:logback/logback-${spring.cloud.config.profile}.xml diff --git a/blockchain-server/blockchain-server-btc/src/main/resources/bootstrap.yml b/blockchain-server/blockchain-server-btc/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..9254db5 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/resources/bootstrap.yml @@ -0,0 +1,24 @@ +server: + port: 8301 +spring: + cloud: + #配置中心 + config: + profile: dev + username: admin + password: test123456 + name: springconf,springcloudconf,redisconf,dbconf,txconf,xssconf,btcconf,ipconf + discovery: + service-id: dapp-config-server + enabled: true + application: + name: dapp-btc-server +#注册中心 +eureka: + client: + service-url: + defaultZone: http://eureka:8001/eureka/ + instance: + prefer-ip-address: true + instance-id: ${spring.cloud.client.ip-address}:${server.port} + hostname: ${spring.cloud.client.ip-address} diff --git a/blockchain-server/blockchain-server-btc/src/main/resources/logback/logback-dev.xml b/blockchain-server/blockchain-server-btc/src/main/resources/logback/logback-dev.xml new file mode 100644 index 0000000..9a0e324 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/resources/logback/logback-dev.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/resources/logback/logback-pro.xml b/blockchain-server/blockchain-server-btc/src/main/resources/logback/logback-pro.xml new file mode 100644 index 0000000..5b55059 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/resources/logback/logback-pro.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcApplicationMapper.xml b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcApplicationMapper.xml new file mode 100644 index 0000000..432fb27 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcApplicationMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + + dapp_btc_application + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcBlockNumberMapper.xml b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcBlockNumberMapper.xml new file mode 100644 index 0000000..c42aa53 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcBlockNumberMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + dapp_btc_block_number + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcTokenMapper.xml b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcTokenMapper.xml new file mode 100644 index 0000000..2dbfc9b --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcTokenMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + dapp_btc_token + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcTransferAuditingMapper.xml b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcTransferAuditingMapper.xml new file mode 100644 index 0000000..4baf523 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcTransferAuditingMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletKeyMapper.xml b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletKeyMapper.xml new file mode 100644 index 0000000..ae66213 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletKeyMapper.xml @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletMapper.xml b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletMapper.xml new file mode 100644 index 0000000..1bb166e --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletMapper.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + dapp_btc_wallet + + + + + + + + + UPDATE + + SET + balance = balance + #{totalAmount}, + free_balance = free_balance + #{freeAmount}, + freeze_balance = freeze_balance + #{freezeAmount}, + update_time = #{modifyTime} + WHERE addr = #{address} + AND token_id = #{tokenId} + AND balance + #{totalAmount} >= 0 + AND free_balance + #{freeAmount} >= 0 + AND freeze_balance + #{freezeAmount} >= 0 + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletOutMapper.xml b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletOutMapper.xml new file mode 100644 index 0000000..9a4c2ae --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletOutMapper.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletTransferMapper.xml b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletTransferMapper.xml new file mode 100644 index 0000000..9fe6d20 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/BtcWalletTransferMapper.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + dapp_btc_wallet_transfer + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-btc/src/main/resources/mapper/ConfigWalletParamMapper.xml b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/ConfigWalletParamMapper.xml new file mode 100644 index 0000000..16446d2 --- /dev/null +++ b/blockchain-server/blockchain-server-btc/src/main/resources/mapper/ConfigWalletParamMapper.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/pom.xml b/blockchain-server/blockchain-server-cct/pom.xml new file mode 100644 index 0000000..a499c56 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/pom.xml @@ -0,0 +1,41 @@ + + + + blockchain-server + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + blockchain-server-cct + 1.0-SNAPSHOT + + + com.blockchain + blockchain-server-base + 1.0-SNAPSHOT + + + org.redisson + redisson + 3.10.2 + + + com.blockchain + blockchain-common-tx + 1.0-SNAPSHOT + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/CctApplication.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/CctApplication.java new file mode 100644 index 0000000..29cdff9 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/CctApplication.java @@ -0,0 +1,12 @@ +package com.blockchain.server.cct; + +import com.blockchain.server.base.BaseConf; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication(scanBasePackageClasses = {BaseConf.class, CctApplication.class}) +public class CctApplication { + public static void main(String[] args) { + SpringApplication.run(CctApplication.class); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/enums/CctDataEnums.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/enums/CctDataEnums.java new file mode 100644 index 0000000..d5aad67 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/enums/CctDataEnums.java @@ -0,0 +1,73 @@ +package com.blockchain.server.cct.common.enums; + +import lombok.Getter; + +public enum CctDataEnums { + + //发布订阅相关 + PUBLISH_ORDER("发布的频道名字", 0, "publishOrder"), + REDIS_LIST_KEY("自动撮合数据在list中的key", 0, "cct:list:order:num:"), + REDIS_LOCK_NUM_KEY("自动撮合分布式锁单价的key", 0, "cct:distributed:lock:num:"), + REDIS_LOCK_ORDERID_KEY("自动撮合分布式锁订单id的key", 0, "cct:distributed:lock:orderid:"), + REDIS_MATCH_COUNT_KEY("自动撮合订单撮合次数key", 0, "cct:match:count:orderid:"), + REDIS_SEND_INTERVAL("发送到前端的广播时间间隔keyvalue", 100, "cct:send:time"), + SUBSCRIPTION("webSocket发布订阅主题", 0, "/topic/subscription"), + USER_SUBSCRIPTION("用户一对一发布订阅主题", 0, "/topic/user"), + + //配置相关 + CONFIG_MAKER_CHARGE("挂单手续费配置名字", 0, "maker_charge"), + CONFIG_TAKER_CHARGE("吃单手续费配置名字", 0, "taker_charge"), + CONFIG_SERVICE_REST("休市配置名字", 0, "closed"), + CONFIG_SUSPENDED("撮合停盘配置名字", 0, "suspended_{0}-{1}"), + CONFIG_PUBLISH_LIMIT("下单限制配置名字", 0, "publish_{0}-{1}"), + CONFIG_MATCH_LIMIT("撮合限制配置名字", 0, "match_{0}-{1}"), + CONFIG_AUTO_REPEAL("自动撤单配置名字", 0, "auto_repeal"), + CONFIG_COMMISSION_CHARGE("佣金百分比配置名字", 0, "commission_charge"), + CONFIG_COMMISSION_COIN("佣金代币配置名字", 0, "commission_coin"), + CONFIG_COMMISSION_VALIDITY("佣金有效期配置名字", 0, "commission_validity"), + CONFIG_COMMISSION_ISSUE_TIME("佣金发放时间配置名字", 0, "commission_issue_time"), + + //公共状态 + COMMON_STATUS_YES("可用状态", 0, "Y"), + COMMON_STATUS_NO("禁用状态", 0, "N"), + + //订单状态 + ORDER_STATUS_NEW("新建", 0, "NEW"), + ORDER_STATUS_MATCH("已撮合", 0, "MATCH"), + ORDER_STATUS_FINISH("已完成", 0, "FINISH"), + ORDER_STATUS_CANCEL("撤单", 0, "CANCEL"), + + + //订单类型 + ORDER_TYPE_BUY("买单", 0, "BUY"), + ORDER_TYPE_SELL("卖单", 0, "SELL"), + + //发布类型 + PUBLISH_TYPE_MARKET("市价交易", 0, "MARKET"), + PUBLISH_TYPE_LIMIT("限价交易", 0, "LIMIT"), + + //成交记录订单类型 + DETAIL_TYPE_MAKER("成交记录挂单类型", 0, "MAKER"), + DETAIL_TYPE_TAKER("成交记录吃单类型", 0, "TAKER"), + + //公共 + DECIMAL_LENGTH("数据库小数位长度", 8, ""), + + //接口排序 + SORT_DESC("排序倒序", 0, "DESC"), + SORT_ASC("排序升序", 0, "ASC"), + ; + + @Getter + private String note;//注释 + @Getter + private int intValue;//值 int类型,默认0 + @Getter + private String strVlue;//值 string类型 + + CctDataEnums(String note, int intValue, String strVlue) { + this.note = note; + this.intValue = intValue; + this.strVlue = strVlue; + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/enums/CctEnums.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/enums/CctEnums.java new file mode 100644 index 0000000..0be1a56 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/enums/CctEnums.java @@ -0,0 +1,44 @@ +package com.blockchain.server.cct.common.enums; + +import lombok.Getter; + +public enum CctEnums { + ORDER_USERID_NULL(5001, "用户id获取失败!", "User id retrieval failed!", "用戶id獲取失敗!"), + ORDER_PASS_NULL(5003, "密码为空!", "Password is empty!", "密碼為空!"), + ORDER_COINNAME_NULL(5004, "交易币对为空!", "The trading pair is short!", "交易幣對為空!"), + ORDER_UNITNAME_NULL(5005, "交易币对为空!", "The trading pair is short!", "交易幣對為空!"), + ORDER_PRICE_NULL(5006, "委托单价为空!", "The entrustment unit price is empty!", "委托單價為空!"), + ORDER_NUM_NULL(5007, "委托数量为空!", "The number of delegates is empty!", "委托數量為空!"), + ORDER_TURNOVER_NULL(5008, "交易额为空!", "The trading volume is empty!", "交易額為空!"), + ORDER_TURNOVER_ERROR(5009, "交易额不合法!", "Illegal trading volume!", "交易額不合法!"), + ORDER_PRICE_ERROR(5010, "委托单价不合法!", "The entrustment unit price is illegal!", "委托單價不合法!"), + ORDER_NUM_ERROR(5011, "委托数量不合法!", "The number of delegates is not legal!", "委托數量不合法!"), + ORDER_ID_NULL(5012, "订单获取失败!", "Order acquisition failure!", "訂單獲取失敗!"), + ORDER_NULL(5013, "订单不存在!", "Order does not exist!", "訂單不存在!"), + ORDER_CANCEL_ERROR(5014, "当前订单状态无法撤销!", "The current order status cannot be revoked!", "當前訂單狀態無法撤銷!"), + ORDER_USERID_ERROR(5015, "您与该订单并无交易关系!", "You have no transaction relationship with this order!", "您與該訂單並無交易關系!"), + CLOSED(5016, "休市中!", "", ""), + PUBLISH_ORDER_WALLET_ERROR(5017, "操作失败,钱包异常!", "Operation failed, wallet abnormal!", "操作失敗,錢包異常!"), + TRADING_ON_NULL(5018, "当前平台不支持该代币交易!", "The current platform does not support this token transaction!", "當前平臺不支持該代幣交易!"), + TEMPORARILY_NOT_OPENED(5020, "暂停交易!", "暫停交易!", "Suspended trading"), + TRADING_ON_STOP_DEAL(5021, "暂停交易!", "暫停交易!", "Suspended trading"), + ORDER_OVER_MAX_LIMIT(5022, "下单失败,限价交易报价的涨跌停限制!", "下單失敗,限價交易報價的漲跌停限制!", "Place failure, limit trading quotation limit limit!"), + ORDER_OVER_MIN_LIMIT(5023, "下单失败,限价交易报价的涨跌停限制!", "下單失敗,限價交易報價的漲跌停限制!", "Place failure, limit trading quotation limit limit!"), + ; + + @Getter + private int code; + @Getter + private String cnmsg; + @Getter + private String enMsg; + @Getter + private String hkMsg; + + CctEnums(int code, String cnmsg, String enMsg, String hkMsg) { + this.code = code; + this.cnmsg = cnmsg; + this.enMsg = enMsg; + this.hkMsg = hkMsg; + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/exception/CctException.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/exception/CctException.java new file mode 100644 index 0000000..1545962 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/exception/CctException.java @@ -0,0 +1,38 @@ +package com.blockchain.server.cct.common.exception; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.cct.common.enums.CctEnums; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +public class CctException extends BaseException { + public CctException(CctEnums rs) { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + //可能是定时器调用,避免获取request空指针 + if (servletRequestAttributes == null) { + this.code = rs.getCode(); + this.msg = rs.getCnmsg(); + } else { + HttpServletRequest request = servletRequestAttributes.getRequest(); + String userLocale = HttpRequestUtil.getUserLocale(request); + String msg = ""; + switch (userLocale) { + case BaseConstant.USER_LOCALE_EN_US: + msg = rs.getEnMsg(); + break; + case BaseConstant.USER_LOCALE_ZH_HK: + msg = rs.getHkMsg(); + break; + default: + msg = rs.getCnmsg(); + break; + } + this.code = rs.getCode(); + this.msg = msg; + } + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/interceptor/InterceptorConf.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/interceptor/InterceptorConf.java new file mode 100644 index 0000000..5bf047f --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/interceptor/InterceptorConf.java @@ -0,0 +1,58 @@ +package com.blockchain.server.cct.common.interceptor; + +import com.blockchain.server.base.interceptor.LoginInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class InterceptorConf implements WebMvcConfigurer { + @Bean + public LoginInterceptor loginInterceptor() { + return new LoginInterceptor(); + } + + @Bean + public TradingInterceptor tradingInterceptor() { + return new TradingInterceptor(); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(loginInterceptor()) + .addPathPatterns("/**") + .excludePathPatterns("/coin/**") + .excludePathPatterns("/order/listBuyOrder") + .excludePathPatterns("/order/listSellOrder") + .excludePathPatterns("/record/listRecord") + .excludePathPatterns("/inner/**") + .excludePathPatterns("/webjars/**") + .excludePathPatterns("/swagger-ui.html") + .excludePathPatterns("/swagger-resources/**") + .excludePathPatterns("/v2/api-docs/**"); + +// registry.addInterceptor(tradingInterceptor()) +// .addPathPatterns("/order/limitBuy") +// .addPathPatterns("/order/limitSell") +// .addPathPatterns("/order/marketBuy") +// .addPathPatterns("/order/marketSell") +// .addPathPatterns("/order/cancel"); + } + + @Bean + public CorsFilter corsFilter() { + CorsConfiguration config = new CorsConfiguration(); + config.addAllowedOrigin("*"); + config.setAllowCredentials(true); + config.addAllowedMethod("*"); + config.addAllowedHeader("*"); + UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource(); + configSource.registerCorsConfiguration("/**", config); + return new CorsFilter(configSource); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/interceptor/TradingInterceptor.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/interceptor/TradingInterceptor.java new file mode 100644 index 0000000..7b60f73 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/interceptor/TradingInterceptor.java @@ -0,0 +1,16 @@ +package com.blockchain.server.cct.common.interceptor; + +import com.blockchain.server.cct.common.enums.CctEnums; +import com.blockchain.server.cct.common.exception.CctException; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class TradingInterceptor extends HandlerInterceptorAdapter { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + throw new CctException(CctEnums.TEMPORARILY_NOT_OPENED); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/publish/listener/ListenerBean.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/publish/listener/ListenerBean.java new file mode 100644 index 0000000..f715b12 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/publish/listener/ListenerBean.java @@ -0,0 +1,58 @@ +package com.blockchain.server.cct.common.publish.listener; + +import com.blockchain.server.cct.common.enums.CctDataEnums; +import com.blockchain.server.cct.common.publish.receiver.MessageReceiver; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.listener.PatternTopic; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; + +@Configuration//相当于xml中配置的beans +public class ListenerBean { + + + /** + * redis消息监听器容器 + * 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器 + * 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理 + * + * @param connectionFactory + * @param listenerAdapter + * @return + */ + @Bean + RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) { + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + //订阅一个的通道 + container.addMessageListener(listenerAdapter, new PatternTopic(CctDataEnums.PUBLISH_ORDER.getStrVlue())); + //这个container可以添加多个messageListener + return container; + } + + /** + * 消息监听器适配器,绑定消息处理器,利用反射技术调用消息处理器的业务方法 + * + * @param receiver + * @return + */ + @Bean + MessageListenerAdapter listenerAdapter(MessageReceiver receiver) { + //这个地方是给messageListenerAdapter 传入一个消息接受处理器,利用反射的方法调用"autoMatch"、 + return new MessageListenerAdapter(receiver, "autoMatch"); + } + + /*** + * 使用默认的工厂初始化redis操作模板 + * @param connectionFactory + * @return + */ + @Bean + StringRedisTemplate template(RedisConnectionFactory connectionFactory) { + return new StringRedisTemplate(connectionFactory); + } + +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/publish/receiver/MessageReceiver.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/publish/receiver/MessageReceiver.java new file mode 100644 index 0000000..b06f7ba --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/publish/receiver/MessageReceiver.java @@ -0,0 +1,282 @@ +package com.blockchain.server.cct.common.publish.receiver; + +import com.blockchain.common.base.exception.RPCException; +import com.blockchain.server.cct.common.enums.CctDataEnums; +import com.blockchain.server.cct.common.enums.CctEnums; +import com.blockchain.server.cct.common.exception.CctException; +import com.blockchain.server.cct.common.redisson.RedissonTool; +import com.blockchain.server.cct.common.redistool.RedisTool; +import com.blockchain.server.cct.entity.PublishOrder; +import com.blockchain.server.cct.service.MatchService; +import com.blockchain.server.cct.service.PublishOrderService; +import org.redisson.api.RedissonClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Component +public class MessageReceiver { + @Autowired + private PublishOrderService publishOrderService; + @Autowired + private MatchService matchService; + @Autowired + private StringRedisTemplate stringRedisTemplate; + @Autowired + private RedissonClient redissonClient; + + //撮合次数 + private static final int MATCH_COUNT = 5; + //日志 + private static final Logger LOG = LoggerFactory.getLogger(MessageReceiver.class); + + /** + * 自动撮合发布订阅的订阅者 + * 接收一个key进行获取分布式锁 + * + * @param orderKey + */ + public void autoMatch(String orderKey) { + //锁key名(锁单价) + String lockKey = CctDataEnums.REDIS_LOCK_NUM_KEY.getStrVlue() + orderKey; + //redis集合中存在的单价key名 + String redisListKey = CctDataEnums.REDIS_LIST_KEY.getStrVlue() + orderKey; + + //判断单价的key的集合长度是否为0 + long size = stringRedisTemplate.opsForList().size(redisListKey); + if (size == 0) { + return; + } + + //获得锁(公平锁) + boolean isgetLock = RedissonTool.tryFairLock(redissonClient, lockKey, 0, 5, TimeUnit.SECONDS); + try { + //获取成功,进行撮合 + if (isgetLock) { + //自旋,将集合最右的对象放到最左 + String orderId = stringRedisTemplate.opsForList().rightPopAndLeftPush(redisListKey, redisListKey); + //撮合 + match(orderId, redisListKey); + //释放锁(公平锁) + RedissonTool.unFairLock(redissonClient, lockKey); + //另启一条线程进行递归 + new Thread(() -> { + //递归当前撮合方法 + autoMatch(orderKey); + }).start(); + } + } catch (Exception e) { + LOG.error("撮合入口方法异常:orderId:{}", orderKey); + } + } + + /*** + * 撮合逻辑 + * + * @param orderId + * @param redisListKey + */ + private void match(String orderId, String redisListKey) { + PublishOrder order = publishOrderService.selectById(orderId); + //撮合第一步将撮合订单更新为已撮合状态 + //防止无法进入撮合方法内部,导致该订单一直为新建状态 + boolean isUpdate = updateOrderStatusToMatch(order, redisListKey); + if (!isUpdate) { + return; + } + + //缓存撮合次数,并判断撮合次数是否过大 +// boolean isOversize = checkMatchCount(orderId, redisListKey, order.getPublishType()); +// if (!isOversize) { +// return; +// } + + //根据id查询匹配的撮合订单 + List matchs = matchService.listBeMatchOrder(orderId); + + //判断可撮合订单列表是否为空· + boolean isNullBeMatch = checkBeMatchListSize(matchs.size(), orderId, redisListKey, order.getPublishType()); + if (!isNullBeMatch) { + return; + } + + //迭代撮合 + for (PublishOrder bymatch : matchs) { + //锁key名(锁订单id) + String lockKey = CctDataEnums.REDIS_LOCK_ORDERID_KEY.getStrVlue() + bymatch.getId(); + //获得被撮合对象订单id的锁(公平锁) + boolean isgetLock = RedissonTool.tryFairLock(redissonClient, lockKey, 0, 5, TimeUnit.SECONDS); + //获取成功 + if (isgetLock) { + try { + //开始撮合 + boolean flag = matchService.handleMatch(orderId, bymatch.getId()); + //判断是否可以继续迭代撮合 + if (!flag) { + break; + } + } catch (RPCException | CctException e) { + //捕获远程调用异常 + e.printStackTrace(); + //如果是市价订单,进行撤单 + cancelMarketOrder(order.getPublishType(), orderId); + //删除key并退出方法 + stringRedisTemplate.opsForList().remove(redisListKey, 1, orderId); + //结束方法 + return; + } finally { + //释放被撮合对象订单id的锁(公平锁) + RedissonTool.unFairLock(redissonClient, lockKey); + } + } else { + //跳过当次循环 + continue; + } + } + + //检查订单是否撮合完毕可以结束 + isMatchEnd(orderId, redisListKey); + } + + /*** + * 将订单更新为已撮合状态 + * 撮合时第一步将订单改为已撮合状态 + * + * @param order + * @param redisListKey + * @return + */ + private boolean updateOrderStatusToMatch(PublishOrder order, String redisListKey) { + //防空 + if (order == null) { + //删除key并退出方法 + stringRedisTemplate.opsForList().remove(redisListKey, 1, order.getId()); + return false; + } + //已撮合,不操作返回更新成功标识 + if (order.getOrderStatus().equals(CctDataEnums.ORDER_STATUS_MATCH.getStrVlue())) { + return true; + } + + //使用乐观锁更新 + publishOrderService.updateStatusInVersionLock(order.getId(), + CctDataEnums.ORDER_STATUS_NEW.getStrVlue(), CctDataEnums.ORDER_STATUS_MATCH.getStrVlue()); + + return true; + } + + /*** + * 校验订单剩余交易额是否为0 + * 如果不是,继续撮合 + * 是,结束撮合删除redis中的对象并发布信息至前端 + * + * @param orderId + * @param redisListKey + */ + private void isMatchEnd(String orderId, String redisListKey) { + //排他锁查询 + PublishOrder order = publishOrderService.selectByIdForUpdate(orderId); + + //市价卖单时判断剩余数量,其余判断剩余交易额 + //如果有剩余数量就继续撮合,否则删除redislist对象,并广播信息到前端 + if (order.getPublishType().equals(CctDataEnums.PUBLISH_TYPE_MARKET.getStrVlue()) && + order.getOrderType().equals(CctDataEnums.ORDER_TYPE_SELL.getStrVlue())) { + + if (order.getLastNum().compareTo(BigDecimal.ZERO) > 0) { + //递归方法,继续撮合 + match(orderId, redisListKey); + } else { + //删除该集合中的第一个对象 + stringRedisTemplate.opsForList().remove(redisListKey, 1, orderId); + } + } else { + if (order.getLastTurnover().compareTo(BigDecimal.ZERO) > 0) { + //递归方法,继续撮合 + match(orderId, redisListKey); + } else { + //删除该集合中的第一个对象 + stringRedisTemplate.opsForList().remove(redisListKey, 1, orderId); + } + } + } + + /*** + * 判断撮合次数是否超过限制 + * + * @param orderId + * @param redisListKey + * @param publishType + * @return + */ + private boolean checkMatchCount(String orderId, String redisListKey, String publishType) { +// //订单撮合次数的Key +// String redisMatchCountKey = CctDataEnums.REDIS_MATCH_COUNT_KEY.getStrVlue() + orderId; +// //判断key是否存在 +// Boolean hasKey = stringRedisTemplate.hasKey(redisMatchCountKey); +// +// //如果存在对应的key +// if (hasKey) { +// //获取撮合次数 +// Integer count = Integer.valueOf(stringRedisTemplate.opsForValue().get(redisMatchCountKey)); +// //如果撮合次数大于X次 +// if (count > MATCH_COUNT) { +// //如果是市价订单,进行撤单 +// cancelMarketOrder(publishType, orderId); +// //删除订单key并退出方法 +// stringRedisTemplate.opsForList().remove(redisListKey, 1, orderId); +// //删除撮合次数key +// stringRedisTemplate.delete(redisMatchCountKey); +// return false; +// } else { +// //撮合次数自增一 +// stringRedisTemplate.opsForValue().increment(redisMatchCountKey, 1); +// } +// } else { +// //不存在对应的key,新增一个并赋予初始值 +// stringRedisTemplate.opsForValue().increment(redisMatchCountKey, 1); +// } +// + return true; + } + + /*** + * 判断被撮合订单列表是否存在 + * + * @param size + * @param orderId + * @param redisListKey + * @param publishType + * @return + */ + private boolean checkBeMatchListSize(int size, String orderId, String redisListKey, String publishType) { + //可撮合列表长度为0 + if (size == 0) { + //如果市价撮合没有订单没有匹配的订单,进行撤单 + cancelMarketOrder(publishType, orderId); + //删除key并退出方法 + stringRedisTemplate.opsForList().remove(redisListKey, 1, orderId); + return false; + } + return true; + } + + /*** + * 撤销市价订单 + * + * @param publishType + * @param orderId + */ + private void cancelMarketOrder(String publishType, String orderId) { + if (publishType.equals(CctDataEnums.PUBLISH_TYPE_MARKET.getStrVlue())) { + publishOrderService.handleCancelOrder(orderId); + } + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/redisson/RedissonConfig.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/redisson/RedissonConfig.java new file mode 100644 index 0000000..46d492e --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/redisson/RedissonConfig.java @@ -0,0 +1,41 @@ +package com.blockchain.server.cct.common.redisson; + + +import org.apache.commons.lang3.StringUtils; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.SingleServerConfig; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RedissonConfig { + + @Value("${spring.redis.host}") + private String host; + + @Value("${spring.redis.port}") + private String port; + + @Value("${spring.redis.password}") + private String password; + + @Value("${spring.redis.database}") + private int database; + + @Bean + public RedissonClient getRedisson() { + //redisson配置类 + Config config = new Config(); + //使用redisson单机模式,并设置相关配置 + SingleServerConfig singleServerConfig = config.useSingleServer(); + singleServerConfig.setAddress("redis://" + host + ":" + port).setDatabase(database); + if (StringUtils.isNotBlank(password)) { + singleServerConfig.setPassword(password); + } + //创建redisson客户端 + return Redisson.create(config); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/redisson/RedissonTool.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/redisson/RedissonTool.java new file mode 100644 index 0000000..4f64103 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/redisson/RedissonTool.java @@ -0,0 +1,48 @@ +package com.blockchain.server.cct.common.redisson; + +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; + +import java.util.concurrent.TimeUnit; + +public class RedissonTool { + + /*** + * 获得公平锁 + * @param redisson + * @param lockKey 锁key名 + * @return + */ + public static RLock fairLock(RedissonClient redisson, String lockKey) { + RLock fairLock = redisson.getFairLock(lockKey); + fairLock.lock(); + return fairLock; + } + + /*** + * 尝试获得锁(公平锁) + * @param redisson 客户端 + * @param lockKey 锁名 + * @param waitTime 获取锁的等待时间 + * @param leaseTime 锁超时时间 + * @param unit 参数的时间单位 + * @return + */ + public static boolean tryFairLock(RedissonClient redisson, String lockKey, int waitTime, int leaseTime, TimeUnit unit) { + RLock fairLock = redisson.getFairLock(lockKey); + try { + return fairLock.tryLock(waitTime, leaseTime, unit); + } catch (InterruptedException e) { + return false; + } + } + + /*** + * 释放锁(公平锁) + * @param redisson + * @param lockKey + */ + public static void unFairLock(RedissonClient redisson, String lockKey) { + redisson.getFairLock(lockKey).unlock(); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/redistool/RedisTool.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/redistool/RedisTool.java new file mode 100644 index 0000000..6dc2c43 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/redistool/RedisTool.java @@ -0,0 +1,128 @@ +package com.blockchain.server.cct.common.redistool; + +import com.blockchain.server.cct.common.enums.CctDataEnums; +import com.blockchain.server.cct.entity.PublishOrder; +import com.blockchain.server.cct.service.PublishOrderService; +import com.blockchain.server.cct.service.TradingRecordService; +import com.github.pagehelper.PageHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/*** + * redis工具类 + */ +@Component +public class RedisTool { + + @Autowired + private PublishOrderService orderService; + @Autowired + private TradingRecordService recordService; + @Autowired + private RedisTemplate redisTemplate; + @Autowired + private SimpMessagingTemplate simpMessagingTemplate; + + private static final String BUY_ORDERS_KSY = "BUY_ORDERS"; + private static final String SELL_ORDERS_KSY = "SELL_ORDERS"; + private static final String USER_ORDERS_KSY = "CCT_USER_ORDERS"; + + /*** + * 发送广播逻辑 + * + * @param coinName + * @param unitName + */ + public void send(String coinName, String unitName) { +// //间隔时间 +// Object intervalTime = redisTemplate.opsForValue().get(CctDataEnums.REDIS_SEND_INTERVAL.getStrVlue()); +// //如果不存在间隔时间 +// if (intervalTime == null) { +// //设置时间 +// redisTemplate.opsForValue().set(CctDataEnums.REDIS_SEND_INTERVAL.getStrVlue(), new Date().getTime() + ""); +// //发送消息 +// template.convertAndSend(CctDataEnums.SUBSCRIPTION.getStrVlue(), payload); +// return; +// } +// //如果发送时间间隔太短 +// if (new Date().getTime() - Long.valueOf(intervalTime.toString()) < CctDataEnums.REDIS_SEND_INTERVAL.getIntValue()) { +// //返回 +// return; +// } + + //盘口买单 + List buyOrders = listBuyOrder(coinName, unitName); + //盘口卖单 + List sellOrders = listSellOrder(coinName, unitName); + + //封装返回前端参数 + Map sendObj = new HashMap<>(); + sendObj.put(BUY_ORDERS_KSY, buyOrders); + sendObj.put(SELL_ORDERS_KSY, sellOrders); + + //发送消息,根据币种推送对应的通道 + simpMessagingTemplate.convertAndSend(CctDataEnums.SUBSCRIPTION.getStrVlue() + "/" + coinName + "-" + unitName, sendObj); +// //重新设置间隔时间 +// redisTemplate.opsForValue().set(CctDataEnums.REDIS_SEND_INTERVAL.getStrVlue(), new Date().getTime() + ""); + } + + /*** + * 成交时推送用户订单信息 + * @param userId + * @param coinName + * @param unitName + * @param type + */ + public void sendToUserOrder(String userId, String coinName, String unitName, String type) { + String[] status = {CctDataEnums.ORDER_STATUS_NEW.getStrVlue(), CctDataEnums.ORDER_STATUS_MATCH.getStrVlue()}; + PageHelper.startPage(0, 3); + List userOrders = orderService.listUserOrder( + userId, coinName, unitName, type, "", status); + + Map sendObj = new HashMap<>(); + sendObj.put(USER_ORDERS_KSY, userOrders); + + simpMessagingTemplate.convertAndSend(CctDataEnums.USER_SUBSCRIPTION.getStrVlue() + "/" + userId + "/" + coinName + "-" + unitName, sendObj); + } + + /*** + * 查询盘口买单 + * @param coinName + * @param unitName + * @return + */ + public List listBuyOrder(String coinName, String unitName) { + return orderService.listOrder(coinName, unitName, CctDataEnums.ORDER_TYPE_BUY.getStrVlue(), + CctDataEnums.ORDER_STATUS_MATCH.getStrVlue(), CctDataEnums.PUBLISH_TYPE_LIMIT.getStrVlue(), CctDataEnums.SORT_DESC.getStrVlue()); + } + + /*** + * 查询盘口卖单 + * @param coinName + * @param unitName + * @return + */ + public List listSellOrder(String coinName, String unitName) { + return orderService.listOrder( + coinName, unitName, CctDataEnums.ORDER_TYPE_SELL.getStrVlue(), + CctDataEnums.ORDER_STATUS_MATCH.getStrVlue(), + CctDataEnums.PUBLISH_TYPE_LIMIT.getStrVlue(), CctDataEnums.SORT_ASC.getStrVlue()); + } + + /*** + * 查询成交记录 + * @param coinName + * @param unitName + * @return + */ + public List listRecord(String coinName, String unitName, Integer pageNum, Integer pageSize) { + PageHelper.startPage(pageNum, pageSize); + return recordService.listRecordByCoinAndUnit(coinName, unitName); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/util/CCTExceptionPreconditionUtils.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/util/CCTExceptionPreconditionUtils.java new file mode 100644 index 0000000..e889571 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/util/CCTExceptionPreconditionUtils.java @@ -0,0 +1,46 @@ +package com.blockchain.server.cct.common.util; + +import com.blockchain.server.cct.common.enums.CctEnums; +import com.blockchain.server.cct.common.exception.CctException; +import org.apache.commons.lang3.StringUtils; + +import java.math.BigDecimal; + +public class CCTExceptionPreconditionUtils { + + /** + * 判断字符串不为空或空串 + * + * @param checkString 需要判断的字符串 + * @param enums 业务异常提示枚举 + */ + public static void checkStringNotBlank(String checkString, CctEnums enums) { + if (StringUtils.isBlank(checkString)) { + throw new CctException(enums); + } + } + + /** + * 判断一个对象不可以为空 + * + * @param obj 需要判断的对象 + * @param enums 业务异常提示枚举 + */ + public static void checkNotNull(Object obj, CctEnums enums) { + if (obj == null) { + throw new CctException(enums); + } + } + + /*** + * 判断BigDecimal对象是否小于0 + * + * @param checkBigDecimal + * @param enums + */ + public static void checkBigDecimalLessThanOrEqualZero(BigDecimal checkBigDecimal, CctEnums enums) { + if (checkBigDecimal.compareTo(BigDecimal.ZERO) <= 0) { + throw new CctException(enums); + } + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/util/CheckConfigUtil.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/util/CheckConfigUtil.java new file mode 100644 index 0000000..e67a902 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/util/CheckConfigUtil.java @@ -0,0 +1,100 @@ +package com.blockchain.server.cct.common.util; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.cct.common.enums.CctDataEnums; +import com.blockchain.server.cct.common.enums.CctEnums; +import com.blockchain.server.cct.common.exception.CctException; +import com.blockchain.server.cct.entity.Config; +import com.blockchain.server.cct.feign.UserFeign; +import com.blockchain.server.cct.service.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +/*** + * 配置信息表相关的配置查询、校验 + */ +@Component +public class CheckConfigUtil { + + @Autowired + private ConfigService configService; + @Autowired + private UserFeign userFeign; + + /*** + * 判断是否休市中 + */ + public void checkIsClosed() { + //查询休市配置信息 + Config config = configService.selectByKey( + CctDataEnums.CONFIG_SERVICE_REST.getStrVlue(), CctDataEnums.COMMON_STATUS_YES.getStrVlue()); + + //如果配置不为空,代表休市中,抛出异常 + if (config != null) { + throw new CctException(CctEnums.CLOSED); + } + } + + /*** + * 查询是否有挂单手续费配置 + * @return Bigdecimal + */ + public BigDecimal checkIsTakerCharge(String userId) { + //查询挂单手续费配置信息 + Config config = configService.selectByKey( + CctDataEnums.CONFIG_TAKER_CHARGE.getStrVlue(), CctDataEnums.COMMON_STATUS_YES.getStrVlue()); + + //查询用户是否免交易手续费 + Boolean isServiceCharge = verifyFreeTransaction(userId); + //如果为true,代表免交易手续费 + if (isServiceCharge) { + return BigDecimal.ZERO; + } + + //如果配置为空,返回0 + if (config == null) { + return BigDecimal.ZERO; + } + + //返回手续费 + return new BigDecimal(config.getDataValue()); + } + + /*** + * 查询是否有吃单手续费配置 + * @return Bigdecimal + */ + public BigDecimal checkIsMakerCharge(String userId) { + //查询吃单手续费配置信息 + Config config = configService.selectByKey( + CctDataEnums.CONFIG_MAKER_CHARGE.getStrVlue(), CctDataEnums.COMMON_STATUS_YES.getStrVlue()); + + //查询用户是否免交易手续费 + Boolean isServiceCharge = verifyFreeTransaction(userId); + //如果为true,代表免交易手续费 + if (isServiceCharge) { + return BigDecimal.ZERO; + } + + //如果配置为空,返回0 + if (config == null) { + return BigDecimal.ZERO; + } + + //返回手续费 + return new BigDecimal(config.getDataValue()); + } + + /*** + * 检查用户是否免交易手续费 + * @param userId + * @return + */ + private Boolean verifyFreeTransaction(String userId) { + ResultDTO resultDTO = userFeign.verifyFreeTransaction(userId); + return resultDTO.getData(); + } + +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/websocket/WebSocketConfig.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/websocket/WebSocketConfig.java new file mode 100644 index 0000000..79f119b --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/common/websocket/WebSocketConfig.java @@ -0,0 +1,19 @@ +package com.blockchain.server.cct.common.websocket; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +@Configuration +@EnableWebSocketMessageBroker +public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + //注册STMOP端点,前端通过 域名:端口/websocket + //setAllowedOrigins的字符串如果不是"*",必须是"HTTP:"或者是"HTTPS:"开头,目前尝试如果不加的话会出现跨域问题 + //withSockJS用于兼容SockJS + registry.addEndpoint("/cct-websocket").setAllowedOrigins("*").withSockJS(); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/CoinController.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/CoinController.java new file mode 100644 index 0000000..c94d4af --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/CoinController.java @@ -0,0 +1,44 @@ +package com.blockchain.server.cct.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.cct.common.enums.CctDataEnums; +import com.blockchain.server.cct.controller.api.CoinApi; +import com.blockchain.server.cct.dto.coin.TradingOnDTO; +import com.blockchain.server.cct.entity.Coin; +import com.blockchain.server.cct.service.CoinService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Api(CoinApi.COIN_API) +@RestController +@RequestMapping("/coin") +public class CoinController { + + @Autowired + private CoinService coinService; + + @ApiOperation(value = CoinApi.listTradingOn.METHOD_TITLE_NAME, + notes = CoinApi.listTradingOn.METHOD_TITLE_NOTE) + @GetMapping("/listTradingOn") + public ResultDTO listTradingOn( + @ApiParam(CoinApi.listTradingOn.METHOD_API_UNITNAME) @RequestParam("unitName") String unitName) { + List tradingOns = coinService.listCoinByUnit(unitName, CctDataEnums.COMMON_STATUS_YES.getStrVlue()); + return ResultDTO.requstSuccess(tradingOns); + } + + @ApiOperation(value = CoinApi.listCoin.METHOD_TITLE_NAME, + notes = CoinApi.listCoin.METHOD_TITLE_NOTE) + @GetMapping("/listCoin") + public ResultDTO listCoin() { + List coins = coinService.listCoinGroupByUnit(); + return ResultDTO.requstSuccess(coins); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/ConfigController.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/ConfigController.java new file mode 100644 index 0000000..05d48f3 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/ConfigController.java @@ -0,0 +1,31 @@ +package com.blockchain.server.cct.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.cct.controller.api.ConfigApi; +import com.blockchain.server.cct.dto.config.ConfigDTO; +import com.blockchain.server.cct.service.ConfigService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Api(ConfigApi.CONFIG_API) +@RestController +@RequestMapping("/config") +public class ConfigController { + + @Autowired + private ConfigService configService; + + @ApiOperation(value = ConfigApi.listServiceChargeConfig.METHOD_TITLE_NAME, + notes = ConfigApi.listServiceChargeConfig.METHOD_TITLE_NOTE) + @GetMapping("/listServiceCharge") + public ResultDTO listServiceChargeConfig() { + List configs = configService.listServiceCharge(); + return ResultDTO.requstSuccess(configs); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/PublishOrderController.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/PublishOrderController.java new file mode 100644 index 0000000..934f3e6 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/PublishOrderController.java @@ -0,0 +1,213 @@ +package com.blockchain.server.cct.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.base.controller.BaseController; +import com.blockchain.server.cct.common.enums.CctDataEnums; +import com.blockchain.server.cct.common.redistool.RedisTool; +import com.blockchain.server.cct.controller.api.PublishOrderApi; +import com.blockchain.server.cct.dto.order.PublishOrderParamDTO; +import com.blockchain.server.cct.entity.PublishOrder; +import com.blockchain.server.cct.service.PublishOrderService; +import com.github.pagehelper.PageHelper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Api(PublishOrderApi.ORDER_API) +@RestController +@RequestMapping("/order") +public class PublishOrderController extends BaseController { + + @Autowired + private PublishOrderService publishOrderService; + @Autowired + private StringRedisTemplate stringRedisTemplate; + @Autowired + private RedisTemplate redisTemplate; + @Autowired + private RedisTool redisTool; + + @ApiOperation(value = PublishOrderApi.handleLimitBuyOrder.METHOD_TITLE_NAME, + notes = PublishOrderApi.handleLimitBuyOrder.METHOD_TITLE_NOTE) + @PostMapping("/limitBuy") + public ResultDTO handleLimitBuyOrder(@ApiParam(PublishOrderApi.handleLimitBuyOrder.METHOD_API_PARAM_DTO) PublishOrderParamDTO paramDTO, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + paramDTO.setUserId(userId); + //返回发布订单的id + String orderId = publishOrderService.handleLimitBuy(paramDTO); + //通知订阅者进行撮合,通知前端刷新行情 + convertAndSend(orderId, paramDTO.getCoinName(), paramDTO.getUnitName(), paramDTO.getUnitPrice().toString()); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = PublishOrderApi.handleLimitSellOrder.METHOD_TITLE_NAME, + notes = PublishOrderApi.handleLimitSellOrder.METHOD_TITLE_NOTE) + @PostMapping("/limitSell") + public ResultDTO handleLimitSellOrder(@ApiParam(PublishOrderApi.handleLimitSellOrder.METHOD_API_PARAM_DTO) PublishOrderParamDTO paramDTO, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + paramDTO.setUserId(userId); + //返回发布订单的id + String orderId = publishOrderService.handleLimitSell(paramDTO); + //通知订阅者进行撮合,通知前端刷新行情 + convertAndSend(orderId, paramDTO.getCoinName(), paramDTO.getUnitName(), paramDTO.getUnitPrice().toString()); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = PublishOrderApi.handleMarketBuyOrder.METHOD_TITLE_NAME, + notes = PublishOrderApi.handleMarketBuyOrder.METHOD_TITLE_NOTE) + @PostMapping("/marketBuy") + public ResultDTO handleMarketBuyOrder(@ApiParam(PublishOrderApi.handleMarketBuyOrder.METHOD_API_PARAM_DTO) PublishOrderParamDTO paramDTO, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + paramDTO.setUserId(userId); + //返回发布订单的id + String orderId = publishOrderService.handleMarketBuy(paramDTO); + //通知订阅者进行撮合,通知前端刷新行情 + convertAndSend(orderId, paramDTO.getCoinName(), paramDTO.getUnitName(), paramDTO.getTurnover().toString()); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = PublishOrderApi.handleMarketSellOrder.METHOD_TITLE_NAME, + notes = PublishOrderApi.handleMarketSellOrder.METHOD_TITLE_NOTE) + @PostMapping("/marketSell") + public ResultDTO handleMarketSellOrder(@ApiParam(PublishOrderApi.handleMarketSellOrder.METHOD_API_PARAM_DTO) PublishOrderParamDTO paramDTO, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + paramDTO.setUserId(userId); + //返回发布订单的id + String orderId = publishOrderService.handleMarketSell(paramDTO); + //通知订阅者进行撮合,通知前端刷新行情 + convertAndSend(orderId, paramDTO.getCoinName(), paramDTO.getUnitName(), paramDTO.getTotalNum().toString()); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = PublishOrderApi.cancel.METHOD_TITLE_NAME, + notes = PublishOrderApi.cancel.METHOD_TITLE_NOTE) + @PostMapping("/cancel") + public ResultDTO handleCancelOrder(@ApiParam(PublishOrderApi.cancel.METHOD_API_ORDER_ID) String orderId, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + //撤销订单 + publishOrderService.handleCancelOrder(orderId, userId); + PublishOrder order = publishOrderService.selectById(orderId); + //用户撤销订单,发送广播至前端刷新行情 + redisTool.send(order.getCoinName(), order.getUnitName()); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = PublishOrderApi.listUserOrder.METHOD_TITLE_NAME, + notes = PublishOrderApi.listUserOrder.METHOD_TITLE_NOTE) + @GetMapping("/listUserOrder") + public ResultDTO listUserOrder( + @ApiParam(PublishOrderApi.listUserOrder.METHOD_API_COINNAME) @RequestParam(value = "coinName", required = false) String coinName, + @ApiParam(PublishOrderApi.listUserOrder.METHOD_API_UNITNAME) @RequestParam(value = "unitName", required = false) String unitName, + @ApiParam(PublishOrderApi.listUserOrder.METHOD_API_TYPE) @RequestParam(value = "type", required = false) String type, + @ApiParam(PublishOrderApi.listUserOrder.METHOD_API_PAGENUM) @RequestParam(value = "pageNum", required = false, defaultValue = "0") Integer pageNum, + @ApiParam(PublishOrderApi.listUserOrder.METHOD_API_PAGESIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + String[] status = {CctDataEnums.ORDER_STATUS_NEW.getStrVlue(), CctDataEnums.ORDER_STATUS_MATCH.getStrVlue()}; + PageHelper.startPage(pageNum, pageSize); + List publishOrders = publishOrderService.listUserOrder( + userId, coinName, unitName, type, "", status); + return ResultDTO.requstSuccess(publishOrders); + } + + @ApiOperation(value = PublishOrderApi.listBuyOrder.METHOD_TITLE_NAME, + notes = PublishOrderApi.listBuyOrder.METHOD_TITLE_NOTE) + @GetMapping("/listBuyOrder") + public ResultDTO listBuyOrder( + @ApiParam(PublishOrderApi.listBuyOrder.METHOD_API_COINNAME) @RequestParam("coinName") String coinName, + @ApiParam(PublishOrderApi.listBuyOrder.METHOD_API_UNITNAME) @RequestParam("unitName") String unitName, + @ApiParam(PublishOrderApi.listBuyOrder.METHOD_API_PAGENUM) @RequestParam(value = "pageNum", required = false, defaultValue = "0") Integer pageNum, + @ApiParam(PublishOrderApi.listBuyOrder.METHOD_API_PAGESIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "5") Integer pageSize) { + //查询买单盘口 + List publishOrders = redisTool.listBuyOrder(coinName, unitName); + return ResultDTO.requstSuccess(publishOrders); + } + + @ApiOperation(value = PublishOrderApi.listSellOrder.METHOD_TITLE_NAME, + notes = PublishOrderApi.listSellOrder.METHOD_TITLE_NOTE) + @GetMapping("/listSellOrder") + public ResultDTO listSellOrder( + @ApiParam(PublishOrderApi.listSellOrder.METHOD_API_COINNAME) @RequestParam("coinName") String coinName, + @ApiParam(PublishOrderApi.listSellOrder.METHOD_API_UNITNAME) @RequestParam("unitName") String unitName, + @ApiParam(PublishOrderApi.listSellOrder.METHOD_API_PAGENUM) @RequestParam(value = "pageNum", required = false, defaultValue = "0") Integer pageNum, + @ApiParam(PublishOrderApi.listSellOrder.METHOD_API_PAGESIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "5") Integer pageSize) { + //查询卖单盘口 + List publishOrders = redisTool.listSellOrder(coinName, unitName); + return ResultDTO.requstSuccess(publishOrders); + } + + @ApiOperation(value = PublishOrderApi.listOrderByCoinAndUnit.METHOD_TITLE_NAME, + notes = PublishOrderApi.listOrderByCoinAndUnit.METHOD_TITLE_NOTE) + @GetMapping("/listOrderByCoinAndUnit") + public ResultDTO listOrderByCoinAndUnit(@ApiParam(PublishOrderApi.listOrderByCoinAndUnit.METHOD_API_COINNAME) @RequestParam("coinName") String coinName, + @ApiParam(PublishOrderApi.listOrderByCoinAndUnit.METHOD_API_UNITNAME) @RequestParam("unitName") String unitName) { + //盘口买单 + List buyOrders = redisTool.listBuyOrder(coinName, unitName); + //盘口卖单 + List sellOrders = redisTool.listSellOrder(coinName, unitName); + //封装返回前端参数 + Map resultMap = new HashMap<>(); + resultMap.put("BUY_ORDERS", buyOrders); + resultMap.put("SELL_ORDERS", sellOrders); + return ResultDTO.requstSuccess(resultMap); + } + + @ApiOperation(value = PublishOrderApi.pcListUserOrder.METHOD_TITLE_NAME, + notes = PublishOrderApi.pcListUserOrder.METHOD_TITLE_NOTE) + @GetMapping("/pcListUserOrder") + public ResultDTO pcListUserOrder( + @ApiParam(PublishOrderApi.pcListUserOrder.METHOD_API_COINNAME) @RequestParam(value = "coinName", required = false) String coinName, + @ApiParam(PublishOrderApi.pcListUserOrder.METHOD_API_UNITNAME) @RequestParam(value = "unitName", required = false) String unitName, + @ApiParam(PublishOrderApi.pcListUserOrder.METHOD_API_TYPE) @RequestParam(value = "type", required = false) String type, + @ApiParam(PublishOrderApi.pcListUserOrder.METHOD_API_PAGENUM) @RequestParam(value = "pageNum", required = false, defaultValue = "0") Integer pageNum, + @ApiParam(PublishOrderApi.pcListUserOrder.METHOD_API_PAGESIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + String[] status = {CctDataEnums.ORDER_STATUS_NEW.getStrVlue(), CctDataEnums.ORDER_STATUS_MATCH.getStrVlue()}; + PageHelper.startPage(pageNum, pageSize); + List publishOrders = publishOrderService.listUserOrder( + userId, coinName, unitName, type, "", status); + return generatePage(publishOrders); + } + + + /*** + * 订单生成后 + * 通知订阅者程序进行撮合 + * 通知前端订阅者刷新页面 + * + * @param orderId 订单id + * @param coinName 基本货币 + * @param unitName 二级货币 + * @param publishNum 发布参数(单价或交易额或数量) + */ + private void convertAndSend(String orderId, String coinName, String unitName, String publishNum) { + //key字符串 + String trandingOn = coinName + "-" + unitName; + String key = publishNum + ":" + trandingOn; + //将撮合订单id和单价放进集合中 + //cct:list:order:num:1.00:BTC-USDT + String listKey = CctDataEnums.REDIS_LIST_KEY.getStrVlue() + key; + stringRedisTemplate.opsForList().leftPush(listKey, orderId); + //通过StringRedisTemplate对象向redis消息队列频道发布消息,进行撮合订单 + stringRedisTemplate.convertAndSend(CctDataEnums.PUBLISH_ORDER.getStrVlue(), key); + //用户发布订单,发送广播至前端刷新行情 + redisTool.send(coinName, unitName); + } +} + diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/TradingDetailController.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/TradingDetailController.java new file mode 100644 index 0000000..70f8563 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/TradingDetailController.java @@ -0,0 +1,98 @@ +package com.blockchain.server.cct.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.base.controller.BaseController; +import com.blockchain.server.cct.controller.api.TradingDetailApi; +import com.blockchain.server.cct.dto.trading.DetailByOrderIdDTO; +import com.blockchain.server.cct.dto.trading.ListUserDetailDTO; +import com.blockchain.server.cct.service.TradingDetailService; +import com.github.pagehelper.PageHelper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +@Api(TradingDetailApi.DETAIL_API) +@RestController +@RequestMapping("/detail") +public class TradingDetailController extends BaseController { + + @Autowired + private TradingDetailService detailService; + @Autowired + private RedisTemplate redisTemplate; + + @ApiOperation(value = TradingDetailApi.listUserHistory.METHOD_TITLE_NAME, + notes = TradingDetailApi.listUserHistory.METHOD_TITLE_NOTE) + @GetMapping("/listUserDetail") + public ResultDTO listUserHistory( + @ApiParam(TradingDetailApi.listUserHistory.METHOD_API_COINNAME) @RequestParam(value = "coinName", required = false) String coinName, + @ApiParam(TradingDetailApi.listUserHistory.METHOD_API_UNITNAME) @RequestParam(value = "unitName", required = false) String unitName, + @ApiParam(TradingDetailApi.listUserHistory.METHOD_API_BEGINTIME) @RequestParam(value = "beginTime", required = false) String beginTime, + @ApiParam(TradingDetailApi.listUserHistory.METHOD_API_LASTTIME) @RequestParam(value = "lastTime", required = false) String lastTime, + @ApiParam(TradingDetailApi.listUserHistory.METHOD_API_STATUS) @RequestParam(value = "status", required = false) String status, + @ApiParam(TradingDetailApi.listUserHistory.METHOD_API_PAGENUM) @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, + @ApiParam(TradingDetailApi.listUserHistory.METHOD_API_PAGESIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + PageHelper.startPage(pageNum, pageSize); + List userDetail = detailService.listUserDetail(userId, coinName, unitName, beginTime, lastTime, status); + return ResultDTO.requstSuccess(userDetail); + } + + @ApiOperation(value = TradingDetailApi.listDetailByOrderId.METHOD_TITLE_NAME, + notes = TradingDetailApi.listDetailByOrderId.METHOD_TITLE_NOTE) + @GetMapping("/listDetailByOrderId") + public ResultDTO listDetailByOrderId( + @ApiParam(TradingDetailApi.listDetailByOrderId.METHOD_API_ORDERID) @RequestParam("orderId") String orderId, + @ApiParam(TradingDetailApi.listDetailByOrderId.METHOD_API_PAGENUM) @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, + @ApiParam(TradingDetailApi.listDetailByOrderId.METHOD_API_PAGESIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, + HttpServletRequest request) { + //校验下token,实际不需要用到user + SSOHelper.getUser(redisTemplate, request); + PageHelper.startPage(pageNum, pageSize); + List details = detailService.listDetailByOrderId(orderId); + return ResultDTO.requstSuccess(details); + } + + @ApiOperation(value = TradingDetailApi.pcListUserHistory.METHOD_TITLE_NAME, + notes = TradingDetailApi.pcListUserHistory.METHOD_TITLE_NOTE) + @GetMapping("/pcListUserDetail") + public ResultDTO pcListUserHistory( + @ApiParam(TradingDetailApi.pcListUserHistory.METHOD_API_COINNAME) @RequestParam(value = "coinName", required = false) String coinName, + @ApiParam(TradingDetailApi.pcListUserHistory.METHOD_API_UNITNAME) @RequestParam(value = "unitName", required = false) String unitName, + @ApiParam(TradingDetailApi.pcListUserHistory.METHOD_API_BEGINTIME) @RequestParam(value = "beginTime", required = false) String beginTime, + @ApiParam(TradingDetailApi.pcListUserHistory.METHOD_API_LASTTIME) @RequestParam(value = "lastTime", required = false) String lastTime, + @ApiParam(TradingDetailApi.pcListUserHistory.METHOD_API_STATUS) @RequestParam(value = "status", required = false) String status, + @ApiParam(TradingDetailApi.pcListUserHistory.METHOD_API_PAGENUM) @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, + @ApiParam(TradingDetailApi.pcListUserHistory.METHOD_API_PAGESIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + PageHelper.startPage(pageNum, pageSize); + List userDetail = detailService.listUserDetail(userId, coinName, unitName, beginTime, lastTime, status); + return generatePage(userDetail); + } + + @ApiOperation(value = TradingDetailApi.pcListDetailByOrderId.METHOD_TITLE_NAME, + notes = TradingDetailApi.pcListDetailByOrderId.METHOD_TITLE_NOTE) + @GetMapping("/pcListDetailByOrderId") + public ResultDTO pcListDetailByOrderId( + @ApiParam(TradingDetailApi.pcListDetailByOrderId.METHOD_API_ORDERID) @RequestParam("orderId") String orderId, + @ApiParam(TradingDetailApi.pcListDetailByOrderId.METHOD_API_PAGENUM) @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, + @ApiParam(TradingDetailApi.pcListDetailByOrderId.METHOD_API_PAGESIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, + HttpServletRequest request) { + //校验下token,实际不需要用到user + SSOHelper.getUser(redisTemplate, request); + PageHelper.startPage(pageNum, pageSize); + List details = detailService.listDetailByOrderId(orderId); + return generatePage(details); + } + + +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/TradingRecordController.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/TradingRecordController.java new file mode 100644 index 0000000..74faaaa --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/TradingRecordController.java @@ -0,0 +1,37 @@ +package com.blockchain.server.cct.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.cct.common.redistool.RedisTool; +import com.blockchain.server.cct.controller.api.TradingRecordApi; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Api(TradingRecordApi.RECORD_API) +@RestController +@RequestMapping("/record") +public class TradingRecordController { + + @Autowired + private RedisTool redisTool; + + @ApiOperation(value = TradingRecordApi.listRecordByCoinAndUnit.METHOD_TITLE_NAME, + notes = TradingRecordApi.listRecordByCoinAndUnit.METHOD_TITLE_NOTE) + @GetMapping("/listRecord") + public ResultDTO listRecordByCoinAndUnit( + @ApiParam(TradingRecordApi.listRecordByCoinAndUnit.METHOD_API_COINNAME) @RequestParam("coinName") String coinName, + @ApiParam(TradingRecordApi.listRecordByCoinAndUnit.METHOD_API_UNITNAME) @RequestParam("unitName") String unitName, + @ApiParam(TradingRecordApi.listRecordByCoinAndUnit.METHOD_API_PAGENUM) @RequestParam(value = "pageNum", required = false, defaultValue = "0") Integer pageNum, + @ApiParam(TradingRecordApi.listRecordByCoinAndUnit.METHOD_API_PAGESIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "1") Integer pageSize) { + //查询最新成交历史 + List tradingRecords = redisTool.listRecord(coinName, unitName, pageNum, pageSize); + return ResultDTO.requstSuccess(tradingRecords); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/CoinApi.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/CoinApi.java new file mode 100644 index 0000000..99ede04 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/CoinApi.java @@ -0,0 +1,17 @@ +package com.blockchain.server.cct.controller.api; + +public class CoinApi { + public static final String COIN_API = "币种信息控制器"; + + public static class listTradingOn { + public static final String METHOD_TITLE_NAME = "查询可用交易对列表"; + public static final String METHOD_TITLE_NOTE = "查询可用交易对列表"; + public static final String METHOD_API_UNITNAME = "二级货币"; + } + + public static class listCoin { + public static final String METHOD_TITLE_NAME = "查询主币列表"; + public static final String METHOD_TITLE_NOTE = "查询主币列表"; + } + +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/ConfigApi.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/ConfigApi.java new file mode 100644 index 0000000..94ca18c --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/ConfigApi.java @@ -0,0 +1,10 @@ +package com.blockchain.server.cct.controller.api; + +public class ConfigApi { + public static final String CONFIG_API = "配置信息控制器"; + + public static class listServiceChargeConfig { + public static final String METHOD_TITLE_NAME = "查询手续费配置"; + public static final String METHOD_TITLE_NOTE = "查询手续费配置"; + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/PublishOrderApi.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/PublishOrderApi.java new file mode 100644 index 0000000..653bc7f --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/PublishOrderApi.java @@ -0,0 +1,80 @@ +package com.blockchain.server.cct.controller.api; + +public class PublishOrderApi { + public static final String ORDER_API = "发布订单控制器"; + + public static class handleLimitBuyOrder { + public static final String METHOD_TITLE_NAME = "发布限价买单"; + public static final String METHOD_TITLE_NOTE = "发布限价买单"; + public static final String METHOD_API_PARAM_DTO = "发布参数"; + } + + public static class handleLimitSellOrder { + public static final String METHOD_TITLE_NAME = "发布限价卖单"; + public static final String METHOD_TITLE_NOTE = "发布限价卖单"; + public static final String METHOD_API_PARAM_DTO = "发布参数"; + } + + public static class handleMarketBuyOrder { + public static final String METHOD_TITLE_NAME = "发布市价买单"; + public static final String METHOD_TITLE_NOTE = "发布市价买单"; + public static final String METHOD_API_PARAM_DTO = "发布参数"; + } + + public static class handleMarketSellOrder { + public static final String METHOD_TITLE_NAME = "发布市价卖单"; + public static final String METHOD_TITLE_NOTE = "发布市价卖单"; + public static final String METHOD_API_PARAM_DTO = "发布参数"; + } + + public static class cancel { + public static final String METHOD_TITLE_NAME = "撤销订单"; + public static final String METHOD_TITLE_NOTE = "撤销订单"; + public static final String METHOD_API_ORDER_ID = "订单id"; + } + + public static class listUserOrder { + public static final String METHOD_TITLE_NAME = "app端查询用户订单"; + public static final String METHOD_TITLE_NOTE = "app端查询用户订单"; + public static final String METHOD_API_COINNAME = "基本货币"; + public static final String METHOD_API_UNITNAME = "二级货币"; + public static final String METHOD_API_TYPE = "订单类型"; + public static final String METHOD_API_PAGENUM = "页码"; + public static final String METHOD_API_PAGESIZE = "分页条数"; + } + + public static class pcListUserOrder { + public static final String METHOD_TITLE_NAME = "pc端查询用户订单"; + public static final String METHOD_TITLE_NOTE = "pc端查询用户订单"; + public static final String METHOD_API_COINNAME = "基本货币"; + public static final String METHOD_API_UNITNAME = "二级货币"; + public static final String METHOD_API_TYPE = "订单类型"; + public static final String METHOD_API_PAGENUM = "页码"; + public static final String METHOD_API_PAGESIZE = "分页条数"; + } + + public static class listBuyOrder { + public static final String METHOD_TITLE_NAME = "查询盘口买单"; + public static final String METHOD_TITLE_NOTE = "查询盘口买单"; + public static final String METHOD_API_COINNAME = "基本货币"; + public static final String METHOD_API_UNITNAME = "二级货币"; + public static final String METHOD_API_PAGENUM = "页码"; + public static final String METHOD_API_PAGESIZE = "分页条数"; + } + + public static class listSellOrder { + public static final String METHOD_TITLE_NAME = "查询盘口卖单"; + public static final String METHOD_TITLE_NOTE = "查询盘口卖单"; + public static final String METHOD_API_COINNAME = "基本货币"; + public static final String METHOD_API_UNITNAME = "二级货币"; + public static final String METHOD_API_PAGENUM = "页码"; + public static final String METHOD_API_PAGESIZE = "分页条数"; + } + + public static class listOrderByCoinAndUnit { + public static final String METHOD_TITLE_NAME = "查询成交历史"; + public static final String METHOD_TITLE_NOTE = "查询成交历史-用于行情深度图"; + public static final String METHOD_API_COINNAME = "基本货币"; + public static final String METHOD_API_UNITNAME = "二级货币"; + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/TradingDetailApi.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/TradingDetailApi.java new file mode 100644 index 0000000..e2cba81 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/TradingDetailApi.java @@ -0,0 +1,45 @@ +package com.blockchain.server.cct.controller.api; + +public class TradingDetailApi { + public static final String DETAIL_API = "成交详情控制器"; + + public static class listUserHistory { + public static final String METHOD_TITLE_NAME = "app端查询用户成交记录"; + public static final String METHOD_TITLE_NOTE = "app端根据条件查询用户成交记录"; + public static final String METHOD_API_COINNAME = "基本货币"; + public static final String METHOD_API_UNITNAME = "二级货币"; + public static final String METHOD_API_BEGINTIME = "开始时间"; + public static final String METHOD_API_LASTTIME = "结束时间"; + public static final String METHOD_API_STATUS = "订单状态"; + public static final String METHOD_API_PAGENUM = "页码"; + public static final String METHOD_API_PAGESIZE = "每页条数"; + } + + public static class listDetailByOrderId { + public static final String METHOD_TITLE_NAME = "查询用户成交记录"; + public static final String METHOD_TITLE_NOTE = "根据条件查询用户成交记录"; + public static final String METHOD_API_ORDERID = "发布订单id"; + public static final String METHOD_API_PAGENUM = "页码"; + public static final String METHOD_API_PAGESIZE = "每页条数"; + } + + public static class pcListUserHistory { + public static final String METHOD_TITLE_NAME = "pc端查询用户成交记录"; + public static final String METHOD_TITLE_NOTE = "pc端根据条件查询用户成交记录"; + public static final String METHOD_API_COINNAME = "基本货币"; + public static final String METHOD_API_UNITNAME = "二级货币"; + public static final String METHOD_API_BEGINTIME = "开始时间"; + public static final String METHOD_API_LASTTIME = "结束时间"; + public static final String METHOD_API_STATUS = "订单状态"; + public static final String METHOD_API_PAGENUM = "页码"; + public static final String METHOD_API_PAGESIZE = "每页条数"; + } + + public static class pcListDetailByOrderId { + public static final String METHOD_TITLE_NAME = "pc端查询用户成交记录"; + public static final String METHOD_TITLE_NOTE = "pc端根据条件查询用户成交记录"; + public static final String METHOD_API_ORDERID = "发布订单id"; + public static final String METHOD_API_PAGENUM = "页码"; + public static final String METHOD_API_PAGESIZE = "每页条数"; + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/TradingRecordApi.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/TradingRecordApi.java new file mode 100644 index 0000000..d5abb07 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/controller/api/TradingRecordApi.java @@ -0,0 +1,26 @@ +package com.blockchain.server.cct.controller.api; + +public class TradingRecordApi { + public static final String RECORD_API = "成交记录控制器"; + + public static class listUserHistory { + public static final String METHOD_TITLE_NAME = "查询用户成交记录"; + public static final String METHOD_TITLE_NOTE = "根据条件查询用户成交记录"; + public static final String METHOD_API_COINNAME = "基本货币"; + public static final String METHOD_API_UNITNAME = "二级货币"; + public static final String METHOD_API_BEGINTIME = "开始时间"; + public static final String METHOD_API_LASTTIME = "结束时间"; + public static final String METHOD_API_STATUS = "订单状态"; + public static final String METHOD_API_PAGENUM = "页码"; + public static final String METHOD_API_PAGESIZE = "每页条数"; + } + + public static class listRecordByCoinAndUnit { + public static final String METHOD_TITLE_NAME = "根据交易币对查询成交记录"; + public static final String METHOD_TITLE_NOTE = "根据交易币对查询成交记录"; + public static final String METHOD_API_COINNAME = "基本货币"; + public static final String METHOD_API_UNITNAME = "二级货币"; + public static final String METHOD_API_PAGENUM = "页码"; + public static final String METHOD_API_PAGESIZE = "每页显示条数"; + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/coin/TradingOnDTO.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/coin/TradingOnDTO.java new file mode 100644 index 0000000..b889293 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/coin/TradingOnDTO.java @@ -0,0 +1,14 @@ +package com.blockchain.server.cct.dto.coin; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class TradingOnDTO { + private String coinName; + private String unitName; + private Integer coinDecimals; + private Integer unitDecimals; + private BigDecimal newestPrice; +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/config/ConfigDTO.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/config/ConfigDTO.java new file mode 100644 index 0000000..67b32c3 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/config/ConfigDTO.java @@ -0,0 +1,9 @@ +package com.blockchain.server.cct.dto.config; + +import lombok.Data; + +@Data +public class ConfigDTO { + private String dataTag; + private String dataValue; +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/matchconfig/MatchConfigDTO.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/matchconfig/MatchConfigDTO.java new file mode 100644 index 0000000..657ca6f --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/matchconfig/MatchConfigDTO.java @@ -0,0 +1,18 @@ +package com.blockchain.server.cct.dto.matchconfig; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class MatchConfigDTO { + private String id; + private String coinName; + private String unitName; + private BigDecimal minPercent; + private BigDecimal maxPercent; + private BigDecimal minPrice; + private BigDecimal maxPrice; + private java.util.Date createTime; + private java.util.Date modifyTime; +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/order/PublishOrderDTO.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/order/PublishOrderDTO.java new file mode 100644 index 0000000..68558ab --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/order/PublishOrderDTO.java @@ -0,0 +1,25 @@ +package com.blockchain.server.cct.dto.order; + +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +public class PublishOrderDTO implements Serializable { + private String id; + private String userId; + private BigDecimal unitPrice; + private BigDecimal totalNum; + private BigDecimal lastNum; + private BigDecimal totalTurnover; + private BigDecimal lastTurnover; + private String coinName; + private String unitName; + private String orderStatus; + private String orderType; + private String publishType; + private int version; + private java.util.Date createTime; + private java.util.Date modifyTime; +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/order/PublishOrderParamDTO.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/order/PublishOrderParamDTO.java new file mode 100644 index 0000000..abaa5d4 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/order/PublishOrderParamDTO.java @@ -0,0 +1,29 @@ +package com.blockchain.server.cct.dto.order; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * PublishOrderService 发布订单参数传输类 + * + * @version 1.0 + * @date 2018-11-06 11:02:51 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PublishOrderParamDTO implements Serializable { + private String userId;//用户id + private String pass;//密码 + private String unitName;//单位 + private String coinName;//币种 + private BigDecimal totalNum;//数量 + private BigDecimal unitPrice;//单价 + private BigDecimal turnover;//交易额 +} + + diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/rpc/currency/CurrencyMarketDTO.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/rpc/currency/CurrencyMarketDTO.java new file mode 100644 index 0000000..5d14bc3 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/rpc/currency/CurrencyMarketDTO.java @@ -0,0 +1,32 @@ +package com.blockchain.server.cct.dto.rpc.currency; + +import com.blockchain.common.base.dto.BaseDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CurrencyMarketDTO extends BaseDTO implements Comparable { + + private String currencyPair; + private BigDecimal amount; + private Long timestamp; + private float percent; + private double usdAmount; + private double cnyAmount; + private double hkdAmount; + private double eurAmount; + private BigDecimal lowest;//最低 + private BigDecimal highest;//最高 + private BigDecimal open;//开盘 + private BigDecimal total;//成交量 + + @Override + public int compareTo(CurrencyMarketDTO o) { + return (int) (10000 * o.percent - 10000 * this.percent); + } +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/trading/DetailByOrderIdDTO.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/trading/DetailByOrderIdDTO.java new file mode 100644 index 0000000..a99f254 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/trading/DetailByOrderIdDTO.java @@ -0,0 +1,23 @@ +package com.blockchain.server.cct.dto.trading; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/*** + * 根据发布id查询成交记录列表 + */ + +@Data +public class DetailByOrderIdDTO { + private String id; + private String recordId; + private BigDecimal unitPrice; + private BigDecimal tradingNum; + private BigDecimal serviceCharge; + private String tradingType; + @JsonFormat(pattern = "HH:mm MM/dd") + private Date createTime; +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/trading/ListUserDetailDTO.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/trading/ListUserDetailDTO.java new file mode 100644 index 0000000..da5d7df --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/trading/ListUserDetailDTO.java @@ -0,0 +1,31 @@ +package com.blockchain.server.cct.dto.trading; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/*** + * 用户成交记录DTO + */ + +@Data +public class ListUserDetailDTO { + private String recordId; //成交记录id + private String orderId; //订单id + private BigDecimal dealPrice; //成交均价 + private BigDecimal dealNum; //成交数量 + private BigDecimal dealTurnover; //成交总额 + private BigDecimal dealCharge; //成交总手续费 + private BigDecimal entrustPrice; //委托单价 + private BigDecimal entrustNum; //委托数量 + private BigDecimal entrustTurnover; //委托交易额 + private String publishType; //发布类型 + private String orderType; //订单类型 + private String coinName; //基本货币 + private String unitName; //二级货币 + private String orderStatus; //订单状态 + @JsonFormat(pattern = "HH:mm MM/dd") + private Date createTime; +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/trading/ListUserHistoryDTO.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/trading/ListUserHistoryDTO.java new file mode 100644 index 0000000..3958415 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/dto/trading/ListUserHistoryDTO.java @@ -0,0 +1,29 @@ +package com.blockchain.server.cct.dto.trading; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/*** + * 查询用户历史成交DTO + */ +@Data +public class ListUserHistoryDTO { + private String recordId; + private String orderId; + private String makerId; + private String takerId; + private String userId; + private String unitName; + private String coinName; + private BigDecimal unitPrice; + private BigDecimal tradingNum; + private BigDecimal totalAmount; + private BigDecimal realAmount; + private BigDecimal chargeRatio; + private BigDecimal serviceCharge; + private String tradingType; + private String orderType; + private Date createTime; +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Bill.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Bill.java new file mode 100644 index 0000000..def4c57 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Bill.java @@ -0,0 +1,41 @@ +package com.blockchain.server.cct.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * AppCctBill 数据传输类 + * + * @version 1.0 + * @date 2019-02-15 17:38:07 + */ +@Table(name = "app_cct_bill") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Bill extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "user_id") + private String userId; + @Column(name = "coin_name") + private String coinName; + @Column(name = "plan_money") + private String planMoney; + @Column(name = "real_money") + private String realMoney; + @Column(name = "service_charge") + private String serviceCharge; + @Column(name = "tag") + private String tag; + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Coin.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Coin.java new file mode 100644 index 0000000..8e72977 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Coin.java @@ -0,0 +1,49 @@ +package com.blockchain.server.cct.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * Coin 数据传输类 + * + * @version 1.0 + * @date 2019-02-15 17:38:07 + */ +@Table(name = "app_cct_coin") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Coin extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "coin_name") + private String coinName; + @Column(name = "unit_name") + private String unitName; + @Column(name = "coin_decimals") + private Integer coinDecimals; + @Column(name = "unit_decimals") + private Integer unitDecimals; + @Column(name = "coin_net") + private String coinNet; + @Column(name = "unit_net") + private String unitNet; + @Column(name = "rank") + private Integer rank; + @Column(name = "status") + private String status; + @Column(name = "tag") + private String tag; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/CoinLog.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/CoinLog.java new file mode 100644 index 0000000..6f21ff2 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/CoinLog.java @@ -0,0 +1,45 @@ +package com.blockchain.server.cct.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * CoinLog 数据传输类 + * + * @version 1.0 + * @date 2019-02-15 17:38:07 + */ +@Table(name = "pc_cct_coin_log") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CoinLog extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "sys_user_id") + private String sysUserId; + @Column(name = "ip_address") + private String ipAddress; + @Column(name = "coin_name") + private String coinName; + @Column(name = "unit_name") + private String unitName; + @Column(name = "coin_decimals") + private String coinDecimals; + @Column(name = "unit_decimals") + private String unitDecimals; + @Column(name = "type") + private String type; + @Column(name = "tag") + private String tag; + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Commission.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Commission.java new file mode 100644 index 0000000..718a5cc --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Commission.java @@ -0,0 +1,44 @@ +package com.blockchain.server.cct.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * Commission 数据传输类 + * + * @version 1.0 + * @date 2019-07-18 14:28:26 + */ +@Table(name = "app_cct_commission") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Commission extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "user_id") + private String userId; + @Column(name = "pid") + private String pid; + @Column(name = "record_id") + private String recordId; + @Column(name = "amount") + private BigDecimal amount; + @Column(name = "coin_name") + private String coinName; + @Column(name = "status") + private String status; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Config.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Config.java new file mode 100644 index 0000000..6d0c45c --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/Config.java @@ -0,0 +1,39 @@ +package com.blockchain.server.cct.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * Config 数据传输类 + * + * @version 1.0 + * @date 2019-02-15 17:38:07 + */ +@Table(name = "app_cct_config") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Config extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "data_tag") + private String dataTag; + @Column(name = "data_key") + private String dataKey; + @Column(name = "data_value") + private String dataValue; + @Column(name = "data_status") + private String dataStatus; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/ConfigLog.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/ConfigLog.java new file mode 100644 index 0000000..c37beaf --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/ConfigLog.java @@ -0,0 +1,47 @@ +package com.blockchain.server.cct.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * ConfigLog 数据传输类 + * + * @version 1.0 + * @date 2019-02-15 17:38:07 + */ +@Table(name = "pc_cct_config_log") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ConfigLog extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "sys_user_id") + private String sysUserId; + @Column(name = "ip_address") + private String ipAddress; + @Column(name = "data_tag") + private String dataTag; + @Column(name = "data_key") + private String dataKey; + @Column(name = "data_value_before") + private String dataValueBefore; + @Column(name = "data_value") + private String dataValue; + @Column(name = "data_status_before") + private String dataStatusBefore; + @Column(name = "data_status") + private String dataStatus; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/MatchConfig.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/MatchConfig.java new file mode 100644 index 0000000..8eec5be --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/MatchConfig.java @@ -0,0 +1,40 @@ +package com.blockchain.server.cct.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * MatchConfig 数据传输类 + * + * @version 1.0 + * @date 2019-07-03 09:44:25 + */ +@Table(name = "app_cct_match_config") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MatchConfig extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "coin_name") + private String coinName; + @Column(name = "unit_name") + private String unitName; + @Column(name = "min_percent") + private BigDecimal minPercent; + @Column(name = "max_percent") + private BigDecimal maxPercent; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/MatchConfigHandleLog.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/MatchConfigHandleLog.java new file mode 100644 index 0000000..c0590c2 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/MatchConfigHandleLog.java @@ -0,0 +1,51 @@ +package com.blockchain.server.cct.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * MatchConfigHandleLog 数据传输类 + * + * @version 1.0 + * @date 2019-07-03 09:44:25 + */ +@Table(name = "app_cct_match_config_handle_log") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MatchConfigHandleLog extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "sys_user_id") + private String sysUserId; + @Column(name = "ip_address") + private String ipAddress; + @Column(name = "handle_type") + private String handleType; + @Column(name = "before_coin_name") + private String beforeCoinName; + @Column(name = "after_coin_name") + private String afterCoinName; + @Column(name = "before_unit_name") + private String beforeUnitName; + @Column(name = "after_unit_name") + private String afterUnitName; + @Column(name = "before_min_percent") + private String beforeMinPercent; + @Column(name = "after_min_percent") + private String afterMinPercent; + @Column(name = "before_max_percent") + private String beforeMaxPercent; + @Column(name = "after_max_percent") + private String afterMaxPercent; + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/PublishOrder.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/PublishOrder.java new file mode 100644 index 0000000..d6ae472 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/PublishOrder.java @@ -0,0 +1,56 @@ +package com.blockchain.server.cct.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * PublishOrder 数据传输类 + * + * @version 1.0 + * @date 2019-02-15 17:38:07 + */ +@Table(name = "app_cct_publish_order") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PublishOrder extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "user_id") + private String userId; + @Column(name = "unit_price") + private BigDecimal unitPrice; + @Column(name = "total_num") + private BigDecimal totalNum; + @Column(name = "last_num") + private BigDecimal lastNum; + @Column(name = "total_turnover") + private BigDecimal totalTurnover; + @Column(name = "last_turnover") + private BigDecimal lastTurnover; + @Column(name = "coin_name") + private String coinName; + @Column(name = "unit_name") + private String unitName; + @Column(name = "order_status") + private String orderStatus; + @Column(name = "order_type") + private String orderType; + @Column(name = "publish_type") + private String publishType; + @Column(name = "version") + private int version; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/TradingDetail.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/TradingDetail.java new file mode 100644 index 0000000..c25c82e --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/TradingDetail.java @@ -0,0 +1,50 @@ +package com.blockchain.server.cct.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * TradingDetail 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:15 + */ +@Table(name = "app_cct_trading_detail") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TradingDetail extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "record_id") + private String recordId; + @Column(name = "publish_id") + private String publishId; + @Column(name = "total_amount") + private BigDecimal totalAmount; + @Column(name = "real_amount") + private BigDecimal realAmount; + @Column(name = "unit_price") + private BigDecimal unitPrice; + @Column(name = "trading_num") + private BigDecimal tradingNum; + @Column(name = "charge_ratio") + private BigDecimal chargeRatio; + @Column(name = "service_charge") + private BigDecimal serviceCharge; + @Column(name = "trading_type") + private String tradingType; + @Column(name = "coin_name") + private String coinName; + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/TradingRecord.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/TradingRecord.java new file mode 100644 index 0000000..f81371f --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/TradingRecord.java @@ -0,0 +1,46 @@ +package com.blockchain.server.cct.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * TradingRecord 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:08:15 + */ +@Table(name = "app_cct_trading_record") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TradingRecord extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "maker_id") + private String makerId; + @Column(name = "taker_id") + private String takerId; + @Column(name = "maker_price") + private BigDecimal makerPrice; + @Column(name = "taker_price") + private BigDecimal takerPrice; + @Column(name = "trading_num") + private BigDecimal tradingNum; + @Column(name = "trading_type") + private String tradingType; + @Column(name = "coin_name") + private String coinName; + @Column(name = "unit_name") + private String unitName; + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/rpc/user/UserRelation.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/rpc/user/UserRelation.java new file mode 100644 index 0000000..0170249 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/entity/rpc/user/UserRelation.java @@ -0,0 +1,50 @@ +package com.blockchain.server.cct.entity.rpc.user; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * UserRelation 用户关系, 数据传输类 + * + * @version 1.0 + * @date 2019-02-21 13:37:18 + */ +@Table(name = "dapp_u_user_relation") +@Data +public class UserRelation extends BaseModel { + @Id + @Column(name = "id") + private String id; + /** + * 当前用户id + */ + @Column(name = "user_id") + private String userId; + /** + * 父id + */ + @Column(name = "pid") + private String pid; + /** + * 关系链信息 + */ + @Column(name = "relation_chain") + private String relationChain; + + /** + * 关系深度 + */ + @Column(name = "tree_depth") + private Integer treeDepth; + + /** + * 创建时间 + */ + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/BTCFeign.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/BTCFeign.java new file mode 100644 index 0000000..5a0600b --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/BTCFeign.java @@ -0,0 +1,30 @@ +package com.blockchain.server.cct.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@FeignClient("dapp-btc-server") +public interface BTCFeign { + //请求路径 + String CONTENT_PATH = "/inner/walletTx"; + + /*** + * 冻结余额 + * @param orderDTO + * @return + */ + @PostMapping(CONTENT_PATH + "/order") + ResultDTO order(@RequestBody WalletOrderDTO orderDTO); + + /*** + * 扣减、增加总余额 + * @param changeDTO + * @return + */ + @PostMapping(CONTENT_PATH + "/change") + ResultDTO change(@RequestBody WalletChangeDTO changeDTO); +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/CurrencyFeign.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/CurrencyFeign.java new file mode 100644 index 0000000..e5667fa --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/CurrencyFeign.java @@ -0,0 +1,50 @@ +package com.blockchain.server.cct.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.cct.dto.rpc.currency.CurrencyMarketDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.math.BigDecimal; +import java.util.Date; + +@FeignClient("dapp-currency-server") +public interface CurrencyFeign { + //请求路径 + String CONTENT_PATH = "/inner/market"; + + /*** + * 保存最新行情信息 + * @param coinName + * @param unitName + * @param amount + * @param total + * @param timestamp + * @return + */ + @PostMapping(CONTENT_PATH + "/save") + ResultDTO save(@RequestParam("coinName") String coinName, + @RequestParam("unitName") String unitName, + @RequestParam("amount") BigDecimal amount, + @RequestParam("total") BigDecimal total, + @RequestParam("timestamp") Long timestamp, + @RequestParam("tradingType") String tradingType); + + /*** + * 查询最新行情 + * @param currencyPair + * @return + */ + @GetMapping("/market/get") + ResultDTO get(@RequestParam("currencyPair") String currencyPair); + + /*** + * 查询今天之前最新的行情 + * @param currencyPair + * @return + */ + @GetMapping("/market/getLast") + ResultDTO getLast(@RequestParam("currencyPair") String currencyPair); +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/EOSFeign.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/EOSFeign.java new file mode 100644 index 0000000..73eaa8d --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/EOSFeign.java @@ -0,0 +1,30 @@ +package com.blockchain.server.cct.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@FeignClient("dapp-eos-server") +public interface EOSFeign { + //请求路径 + String CONTENT_PATH = "/inner/walletTx"; + + /*** + * 冻结余额 + * @param orderDTO + * @return + */ + @PostMapping(CONTENT_PATH + "/order") + ResultDTO order(@RequestBody WalletOrderDTO orderDTO); + + /*** + * 扣减、增加总余额 + * @param changeDTO + * @return + */ + @PostMapping(CONTENT_PATH + "/change") + ResultDTO change(@RequestBody WalletChangeDTO changeDTO); +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/ETHFeign.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/ETHFeign.java new file mode 100644 index 0000000..d587648 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/ETHFeign.java @@ -0,0 +1,44 @@ +package com.blockchain.server.cct.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient("dapp-eth-server") +public interface ETHFeign { + //钱包公共接口请求路径 + String WALLET_PATH = "/inner/wallet"; + //钱包交易费用接口请求路径 + String TRANS_PATH = "/inner/walletTx"; + + /*** + * 冻结余额 + * @param orderDTO + * @return + */ + @PostMapping(TRANS_PATH + "/order") + ResultDTO order(@RequestBody WalletOrderDTO orderDTO); + + /*** + * 扣减、增加总余额 + * @param changeDTO + * @return + */ + @PostMapping(TRANS_PATH + "/change") + ResultDTO change(@RequestBody WalletChangeDTO changeDTO); + + /*** + * 校验密码 + * @param pass + * @return + */ + @GetMapping(WALLET_PATH + "/isPassword") + ResultDTO isPassword(@RequestParam(name = "password") String pass); + + +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/PushFeign.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/PushFeign.java new file mode 100644 index 0000000..b72f308 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/PushFeign.java @@ -0,0 +1,26 @@ +package com.blockchain.server.cct.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.enums.PushEnums; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.Map; + +@FeignClient(name = "dapp-user-server", path = "/inner") +public interface PushFeign { + + /*** + * 对单个用户推送消息 + * @param userId 用户id + * @param pushType 推送通知信息类型 + * @param payload 透传数据(通知附带数据) + * @return + */ + @PostMapping("/push/pushToSingle") + ResultDTO pushToSingle(@RequestParam("/userId") String userId, + @RequestParam("/pushType") String pushType, + @RequestBody Map payload); +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/UserFeign.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/UserFeign.java new file mode 100644 index 0000000..497f4db --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/feign/UserFeign.java @@ -0,0 +1,35 @@ +package com.blockchain.server.cct.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.cct.entity.rpc.user.UserRelation; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient(name = "dapp-user-server", path = "/inner") +public interface UserFeign { + + /*** + * 检查用户初级认证和黑名单 + * @return + */ + @GetMapping("/hasLowAuthAndUserList") + ResultDTO hasLowAuthAndUserList(@RequestParam("userId") String userId); + + /*** + * 免交易手续费 + * @param userId + * @return + */ + @PostMapping("/verifyFreeTransaction") + ResultDTO verifyFreeTransaction(@RequestParam("userId") String userId); + + /*** + * 根据用户id查询推荐关系 + * @param userId + * @return + */ + @GetMapping("/selectRelationByUserId") + ResultDTO selectRelationByUserId(@RequestParam("userId") String userId); +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/inner/CctInner.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/inner/CctInner.java new file mode 100644 index 0000000..8fc6523 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/inner/CctInner.java @@ -0,0 +1,100 @@ +package com.blockchain.server.cct.inner; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.cct.dto.order.PublishOrderDTO; +import com.blockchain.server.cct.service.MatchService; +import com.blockchain.server.cct.service.PublishOrderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.util.List; + +/** + * @author huangxl + * @create 2019-04-25 19:02 + */ +@RestController +@RequestMapping("/inner") +public class CctInner { + @Autowired + private MatchService matchService; + @Autowired + private PublishOrderService publishOrderService; + + + @GetMapping("/send") + public ResultDTO send(@RequestParam("coinName") String coinName, + @RequestParam("unitName") String unitName) { + matchService.convertAndSend(coinName, unitName); + return ResultDTO.requstSuccess(); + } + + /*** + * 查询单价范围内的挂单 + * @param coinName + * @param unitName + * @param minPrice + * @param maxPrice + * @return + */ + @PostMapping("/listBeMatchOrderToBot") + public ResultDTO listBeMatchOrderToBot(@RequestParam("coinName") String coinName, + @RequestParam("unitName") String unitName, + @RequestParam("minPrice") BigDecimal minPrice, + @RequestParam("maxPrice") BigDecimal maxPrice) { + List orderDTOS = matchService.listBeMatchOrderToBot(coinName, unitName, minPrice, maxPrice); + return ResultDTO.requstSuccess(orderDTOS); + } + + /**** + * 发布限价买单 - 用于撮合机器人 + * @param userId + * @param coinName + * @param unitName + * @param totalNum + * @param price + * @return + */ + @PostMapping("/handleLimitBuyToBot") + public ResultDTO handleLimitBuyToBot(@RequestParam("userId") String userId, + @RequestParam("coinName") String coinName, + @RequestParam("unitName") String unitName, + @RequestParam("totalNum") BigDecimal totalNum, + @RequestParam("price") BigDecimal price) { + String pubilshId = publishOrderService.handleLimitBuyToBot(userId, coinName, unitName, totalNum, price); + return ResultDTO.requstSuccess(pubilshId); + } + + /*** + * 发布限价卖单 - 用于撮合机器人 + * @param userId + * @param coinName + * @param unitName + * @param totalNum + * @param price + * @return + */ + @PostMapping("/handleLimitSellToBot") + public ResultDTO handleLimitSellToBot(@RequestParam("userId") String userId, + @RequestParam("coinName") String coinName, + @RequestParam("unitName") String unitName, + @RequestParam("totalNum") BigDecimal totalNum, + @RequestParam("price") BigDecimal price) { + String pubilshId = publishOrderService.handleLimitSellToBot(userId, coinName, unitName, totalNum, price); + return ResultDTO.requstSuccess(pubilshId); + } + + /*** + * 撮合 - 用于撮合机器人 + * @param matchId + * @param bymatchId + * @return + */ + @PostMapping("/handleMatchToBot") + public ResultDTO handleMatchToBot(@RequestParam("matchId") String matchId, + @RequestParam("bymatchId") String bymatchId) { + Boolean flag = matchService.handleMatch(matchId, bymatchId); + return ResultDTO.requstSuccess(flag); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/CoinMapper.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/CoinMapper.java new file mode 100644 index 0000000..6b22728 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/CoinMapper.java @@ -0,0 +1,48 @@ +package com.blockchain.server.cct.mapper; + +import com.blockchain.server.cct.entity.Coin; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * AppCctCoinMapper 数据访问类 + * + * @version 1.0 + * @date 2019-02-15 17:38:07 + */ +@Repository +public interface CoinMapper extends Mapper { + + /*** + * 根据币对、状态查询币对信息 + * @param coinName + * @param unitName + * @param status + * @return + */ + Coin selectByCoinUnitAndStatus( + @Param("coinName") String coinName, @Param("unitName") String unitName, @Param("status") String status); + + /*** + * 根据状态查询币种 + * @param status + * @return + */ + List listCoin(@Param("stauts") String status); + + /*** + * 根据单位查询交易币对 + * @param unitName + * @return + */ + List listCoinByUnit(@Param("unitName") String unitName, @Param("status") String status); + + /*** + * 查询主币列表 + * @return + */ + List listCoinGroupByUnit(); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/CommissionMapper.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/CommissionMapper.java new file mode 100644 index 0000000..f3f097e --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/CommissionMapper.java @@ -0,0 +1,52 @@ +package com.blockchain.server.cct.mapper; + +import com.blockchain.server.cct.entity.Commission; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * CommissionMapper 数据访问类 + * + * @version 1.0 + * @date 2019-07-18 14:28:26 + */ +@Repository +public interface CommissionMapper extends Mapper { + + /*** + * 行锁更新数据 + * @param userId + * @param coinName + * @param status + * @param amount + * @param modifyTime + * @return + */ + int updateByRowLock(@Param("userId") String userId, + @Param("coinName") String coinName, + @Param("status") String status, + @Param("amount") BigDecimal amount, + @Param("modifyTime") Date modifyTime); + + /*** + * 根据用户、状态、币种查询 + * @param userId + * @param status + * @param coinName + * @return + */ + Commission selectByUserAndStatusAndCoin(@Param("userId") String userId, + @Param("status") String status, + @Param("coinName") String coinName); + + /*** + * 根据id查询,排他锁 + * @param id + * @return + */ + Commission selectForUpdate(@Param("id") String id); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/ConfigMapper.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/ConfigMapper.java new file mode 100644 index 0000000..1061db6 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/ConfigMapper.java @@ -0,0 +1,31 @@ +package com.blockchain.server.cct.mapper; + +import com.blockchain.server.cct.entity.Config; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * AppCctConfigMapper 数据访问类 + * + * @version 1.0 + * @date 2019-02-15 17:38:07 + */ +@Repository +public interface ConfigMapper extends Mapper { + + /*** + * 根据key名和状态查询 + * @param key + * @param status + * @return + */ + Config selectByKeyAndStatus(@Param("key") String key, @Param("status") String status); + + /*** + * 根据key名查询配置信息 + * @param key + * @return + */ + Config selectByKey(@Param("key") String key); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/MatchConfigHandleLogMapper.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/MatchConfigHandleLogMapper.java new file mode 100644 index 0000000..77adece --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/MatchConfigHandleLogMapper.java @@ -0,0 +1,15 @@ +package com.blockchain.server.cct.mapper; + +import com.blockchain.server.cct.entity.MatchConfigHandleLog; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * MatchConfigHandleLogMapper 数据访问类 + * + * @version 1.0 + * @date 2019-07-03 09:44:25 + */ +@Repository +public interface MatchConfigHandleLogMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/MatchConfigMapper.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/MatchConfigMapper.java new file mode 100644 index 0000000..37ea56c --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/MatchConfigMapper.java @@ -0,0 +1,24 @@ +package com.blockchain.server.cct.mapper; + +import com.blockchain.server.cct.entity.MatchConfig; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * MatchConfigMapper 数据访问类 + * + * @version 1.0 + * @date 2019-07-03 09:44:25 + */ +@Repository +public interface MatchConfigMapper extends Mapper { + + /*** + * 根据基本货币二级货币查询今天之前最新的一条撮合配置 + * @param coinName + * @param unitName + * @return + */ + MatchConfig selectByCoinAndUnitBeforeToday(@Param("coinName") String coinName, @Param("unitName") String unitName); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/PublishOrderMapper.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/PublishOrderMapper.java new file mode 100644 index 0000000..08d539a --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/PublishOrderMapper.java @@ -0,0 +1,127 @@ +package com.blockchain.server.cct.mapper; + +import com.blockchain.common.base.dto.MarketDTO; +import com.blockchain.server.cct.entity.PublishOrder; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * AppCctPublishOrderMapper 数据访问类 + * + * @version 1.0 + * @date 2019-02-15 17:38:07 + */ +@Repository +public interface PublishOrderMapper extends Mapper { + + /*** + * 根据id查询订单 + * @param orderId + * @return + */ + PublishOrder selectById(@Param("orderId") String orderId); + + /*** + * 根据id查询订单,使用锁 + * @param orderId + * @return + */ + PublishOrder selectByIdForUpdate(@Param("orderId") String orderId); + + /*** + * 根据条件查询用户订单 + * @param userId + * @param coinName + * @param unitName + * @param orderType + * @param publishType + * @param orderStatus + * @return + */ + List listUserOrder(@Param("userId") String userId, @Param("coinName") String coinName, + @Param("unitName") String unitName, @Param("orderType") String orderType, + @Param("publishType") String publishType, @Param("status") String[] orderStatus); + + /*** + * 根据条件查询订单 + * @param coinName + * @param unitName + * @param orderType + * @param orderStatus + * @return + */ + List listOrder(@Param("coinName") String coinName, @Param("unitName") String unitName, + @Param("type") String orderType, @Param("status") String orderStatus, + @Param("publishType") String publishtType, @Param("sort") String sort); + + /*** + * 查询可撮合买单 + * @param unitName 基本货币 + * @param coinName 二级货币 + * @param status 订单状态 + * @param price 单价 + * @param publishType 发布类型 + * @param pageSize 查询条数 + * @return + */ + List listMatchBuyOrder(@Param("unitName") String unitName, @Param("coinName") String coinName, + @Param("status") String status, @Param("price") BigDecimal price, + @Param("publishType") String publishType, @Param("pageSize") int pageSize); + + /*** + * 查询可撮合卖单 + * @param unitName + * @param coinName + * @param status + * @param price + * @param publishType + * @param pageSize + * @return + */ + List listMatchSellOrder(@Param("unitName") String unitName, @Param("coinName") String coinName, + @Param("status") String status, @Param("price") BigDecimal price, + @Param("publishType") String publishType, @Param("pageSize") int pageSize); + + /*** + * 查询未撮合订单 - 用于自动撮合机器人 + * @param unitName + * @param coinName + * @param minPrice + * @param maxPrice + * @param orderStatus + * @param publishType + * @return + */ + List listMatchOrderToBot(@Param("unitName") String unitName, @Param("coinName") String coinName, + @Param("minPrice") BigDecimal minPrice, @Param("maxPrice") BigDecimal maxPrice, + @Param("orderStatus") String orderStatus, @Param("publishType") String publishType, + @Param("pageSize") int pageSize); + + /*** + * 更新订单状态,使用乐观锁 + * @param orderId + * @param beforeStatus + * @param laterStatus + * @return + */ + int updateStatusInVersionLock(@Param("orderId") String orderId, + @Param("beforeStatus") String beforeStatus, + @Param("laterStatus") String laterStatus, + @Param("time") Date time); + + /*** + * 根据币对、状态查询订单 + * @param coinName + * @param unitName + * @param orderStatus + * @return + */ + List listOrderByCoinAndUnitAndStatus(@Param("coinName") String coinName, @Param("unitName") String unitName, + @Param("status") String orderStatus, @Param("type") String orderType, + @Param("publishType") String publishType, @Param("sort") String sort); +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/TradingDetailMapper.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/TradingDetailMapper.java new file mode 100644 index 0000000..02a7d6c --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/TradingDetailMapper.java @@ -0,0 +1,41 @@ +package com.blockchain.server.cct.mapper; + +import com.blockchain.server.cct.dto.trading.DetailByOrderIdDTO; +import com.blockchain.server.cct.dto.trading.ListUserDetailDTO; +import com.blockchain.server.cct.entity.TradingDetail; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * CctTradingDetailMapper 数据访问类 + * + * @version 1.0 + * @date 2019-02-16 15:08:15 + */ +@Repository +public interface TradingDetailMapper extends Mapper { + + /*** + * 查询用户成交记录 + * @param userId + * @param coinName 基本货币 + * @param unitName 二级货币 + * @param beginTime 开始时间 + * @param lastTime 结束时间 + * @param status 发布订单状态 + * @return + */ + List listUserDetail(@Param("userId") String userId, @Param("coinName") String coinName, + @Param("unitName") String unitName, @Param("beginTime") String beginTime, + @Param("lastTime") String lastTime, @Param("status") String status); + + /*** + * 根据发布id查询成交记录列表 + * @param orderId + * @return + */ + List listDetailByOrderId(@Param("orderId") String orderId); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/TradingRecordMapper.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/TradingRecordMapper.java new file mode 100644 index 0000000..f45897f --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/mapper/TradingRecordMapper.java @@ -0,0 +1,33 @@ +package com.blockchain.server.cct.mapper; + +import com.blockchain.server.cct.entity.TradingRecord; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * CctTradingRecordMapper 数据访问类 + * + * @version 1.0 + * @date 2019-02-16 15:08:15 + */ +@Repository +public interface TradingRecordMapper extends Mapper { + /*** + * 根据交易对查询成交记录 + * @param coinName + * @param unitName + * @return + */ + List listRecordByCoinAndUnit(@Param("coinName") String coinName, @Param("unitName") String unitName); + + /*** + * 根据交易币对查询最新一条成交记录 + * @param coinName + * @param unitName + * @return + */ + TradingRecord selectByCoinAndUnitLimitOne(@Param("coinName") String coinName, @Param("unitName") String unitName); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/redis/PublishOrderCache.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/redis/PublishOrderCache.java new file mode 100644 index 0000000..d1a77c0 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/redis/PublishOrderCache.java @@ -0,0 +1,144 @@ +package com.blockchain.server.cct.redis; + +import com.blockchain.common.base.dto.MarketDTO; +import com.blockchain.server.cct.common.redisson.RedissonTool; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.text.MessageFormat; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +@Component +public class PublishOrderCache { + + @Autowired + private RedisTemplate redisTemplate; + + private static final int LOCK_WAIT_TIME = 0; //获取锁等待时间 + private static final int LOCK_LEASE_TIME = 5; //释放时间 + private static final String ORDER_LIST_CACHE = "cct:list:order:{0}:{1}-{2}";//本地系统盘口数据key + private static final String ORDER_LOCK_LIST_CACHE = "cct:distributed:lock:order:{0}:{1}-{2}";//本地系统盘口数据Key的分布式锁 + + /*** + * 获取订单列表key + * @param coinName + * @param unitName + * @param orderType + * @return + */ + public static String getOrderListKey(String coinName, String unitName, String orderType) { + return MessageFormat.format(ORDER_LIST_CACHE, orderType, coinName, unitName); + } + + /*** + * 获取订单列表锁key + * @param coinName + * @param unitName + * @param orderType + * @return + */ + public static String getOrderLockListKey(String coinName, String unitName, String orderType) { + return MessageFormat.format(ORDER_LOCK_LIST_CACHE, orderType, coinName, unitName); + } + + /*** + * 根据分数获取sortSet + * @param coinName + * @param unitName + * @param orderType + * @param score + * @return + */ + public Set getZSetByScore(String coinName, String unitName, String orderType, double score) { + return redisTemplate.opsForZSet().rangeByScore(getOrderListKey(coinName, unitName, orderType), score, score); + } + + /*** + * 升序获取sortSet列表 + * @param coinName + * @param unitName + * @param orderType + * @param start + * @param end + * @return + */ + public Set getZSetASC(String coinName, String unitName, String orderType, int start, int end) { + return redisTemplate.opsForZSet().range(getOrderListKey(coinName, unitName, orderType), start, end); + } + + /*** + * 降序获取sorcSet列表 + * @param coinName + * @param unitName + * @param orderType + * @param start + * @param end + * @return + */ + public Set getZSetDESC(String coinName, String unitName, String orderType, int start, int end) { + return redisTemplate.opsForZSet().reverseRange(getOrderListKey(coinName, unitName, orderType), start, end); + } + + /*** + * 设值进sortSet中 + * @param coinName + * @param unitName + * @param orderType + * @param order + * @param score + * @return + */ + public boolean setZSetValue(String coinName, String unitName, String orderType, MarketDTO order, double score) { + return redisTemplate.opsForZSet().add(getOrderListKey(coinName, unitName, orderType), order, score); + } + + /*** + * 根据分数删除sortSet中的对象 + * @param coinName + * @param unitName + * @param orderType + * @param score + * @return + */ + public long removeZSetByScore(String coinName, String unitName, String orderType, double score) { + return redisTemplate.opsForZSet().removeRangeByScore(getOrderListKey(coinName, unitName, orderType), score, score); + } + + /*** + * 是否存在Key + * @param coinName + * @param unitName + * @param orderType + * @return + */ + public boolean hasKey(String coinName, String unitName, String orderType) { + return redisTemplate.hasKey(getOrderListKey(coinName, unitName, orderType)); + } + + /*** + * 获取分布式锁 + * @param redissonClient + * @param coinName + * @param unitName + * @param orderType + * @return + */ + public boolean tryFairLock(RedissonClient redissonClient, String coinName, String unitName, String orderType) { + return RedissonTool.tryFairLock(redissonClient, getOrderLockListKey(coinName, unitName, orderType), LOCK_WAIT_TIME, LOCK_LEASE_TIME, TimeUnit.SECONDS); + } + + /*** + * 释放锁 + * @param redissonClient + * @param coinName + * @param unitName + * @param orderType + */ + public void unFairLock(RedissonClient redissonClient, String coinName, String unitName, String orderType) { + RedissonTool.unFairLock(redissonClient, getOrderLockListKey(coinName, unitName, orderType)); + } + +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/CoinService.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/CoinService.java new file mode 100644 index 0000000..e592c79 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/CoinService.java @@ -0,0 +1,39 @@ +package com.blockchain.server.cct.service; + +import com.blockchain.server.cct.dto.coin.TradingOnDTO; +import com.blockchain.server.cct.entity.Coin; + +import java.util.List; + +public interface CoinService { + + /*** + * 根据币对、状态查询币种信息 + * @param coinName + * @param unitName + * @param status + * @return + */ + Coin selectCoin(String coinName, String unitName, String status); + + /*** + * 校验币对是否存在、是否可用 + * @param coinName + * @param unitName + * @return + */ + Coin checkCoinIsYes(String coinName, String unitName); + + /*** + * 查询可用币种列表 + * @return + */ + List listCoinByUnit(String unitName, String stauts); + + /*** + * 查询主币列表 + * @return + */ + List listCoinGroupByUnit(); + +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/CommissionService.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/CommissionService.java new file mode 100644 index 0000000..c41de74 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/CommissionService.java @@ -0,0 +1,19 @@ +package com.blockchain.server.cct.service; + +import com.blockchain.server.cct.entity.Commission; + +import java.math.BigDecimal; +import java.util.Date; + +public interface CommissionService { + + /*** + * 生成佣金 + * @param userId + * @param recordId + * @param serviceCharge + * @param coinName + */ + void generateCommission(String userId, String recordId, BigDecimal serviceCharge, String coinName); + +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/ConfigService.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/ConfigService.java new file mode 100644 index 0000000..a828c2b --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/ConfigService.java @@ -0,0 +1,42 @@ +package com.blockchain.server.cct.service; + +import com.blockchain.server.cct.dto.config.ConfigDTO; +import com.blockchain.server.cct.entity.Config; + +import java.math.BigDecimal; +import java.util.List; + +public interface ConfigService { + + /*** + * 查询可用的配置信息 + * @param key + * @param status + * @return + */ + Config selectByKey(String key, String status); + + /*** + * 查询所有手续费配置 + * @return + */ + List listServiceCharge(); + + /*** + * 查询佣金百分比 + * @return + */ + BigDecimal selectCommissionCharge(); + + /*** + * 查询佣金代币 + * @return + */ + String selectCommissionCoin(); + + /*** + * 查询佣金有效期 + * @return + */ + Integer selectCommissionValidity(); +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/MatchConfigService.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/MatchConfigService.java new file mode 100644 index 0000000..d7d371d --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/MatchConfigService.java @@ -0,0 +1,17 @@ +package com.blockchain.server.cct.service; + +import com.blockchain.server.cct.dto.matchconfig.MatchConfigDTO; +import com.blockchain.server.cct.entity.MatchConfig; + +import java.math.BigDecimal; + +public interface MatchConfigService { + + /*** + * 查询今天之前最新的撮合配置信息 + * @param coinName + * @param unitName + * @return + */ + MatchConfigDTO getByCoinAndUnitBeforeToday(String coinName, String unitName); +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/MatchService.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/MatchService.java new file mode 100644 index 0000000..afa1d8c --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/MatchService.java @@ -0,0 +1,38 @@ +package com.blockchain.server.cct.service; + +import com.blockchain.server.cct.dto.order.PublishOrderDTO; +import com.blockchain.server.cct.entity.PublishOrder; + +import java.math.BigDecimal; +import java.util.List; + +public interface MatchService { + + /*** + * 循环条件的匹配的订单集合,进行撮合操作 + * @param matchId + * @param bymatchId + */ + boolean handleMatch(String matchId, String bymatchId); + + /*** + * 查询可撮合的订单 + * @param orderId + * @return + */ + List listBeMatchOrder(String orderId); + + /*** + * 查询单价范围内的挂单 + * @param coinName + * @param unitName + * @return + */ + List listBeMatchOrderToBot(String coinName, String unitName, BigDecimal minPrice, BigDecimal maxPrice); + /*** + * 发送推送通知,推送买卖盘口数据 + * @param coinName + * @param unitName + */ + void convertAndSend(String coinName, String unitName); +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/PublishOrderService.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/PublishOrderService.java new file mode 100644 index 0000000..5a9ac5c --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/PublishOrderService.java @@ -0,0 +1,157 @@ +package com.blockchain.server.cct.service; + +import com.blockchain.common.base.dto.MarketDTO; +import com.blockchain.server.cct.dto.order.PublishOrderParamDTO; +import com.blockchain.server.cct.entity.PublishOrder; + +import java.math.BigDecimal; +import java.util.List; + +public interface PublishOrderService { + + /*** + * 发布限价买单 + * @param paramDTO + * @return orderId + */ + String handleLimitBuy(PublishOrderParamDTO paramDTO); + + /*** + * 发布限价卖单 + * @param paramDTO + * @return orderId + */ + String handleLimitSell(PublishOrderParamDTO paramDTO); + + /*** + * 发布限价买单 - 用于撮合机器人 + * @param userId + * @param coinName + * @param unitName + * @param totalNum + * @param price + * @return + */ + String handleLimitBuyToBot(String userId, String coinName, String unitName, + BigDecimal totalNum, BigDecimal price); + + /*** + * 发布限价买单 - 用于撮合机器人 + * @param userId + * @param coinName + * @param unitName + * @param totalNum + * @param price + * @return + */ + String handleLimitSellToBot(String userId, String coinName, String unitName, + BigDecimal totalNum, BigDecimal price); + /*** + * 发布市价买单 + * @param paramDTO + * @return + */ + String handleMarketBuy(PublishOrderParamDTO paramDTO); + + /*** + * 发布市价卖单 + * @param paramDTO + * @return + */ + String handleMarketSell(PublishOrderParamDTO paramDTO); + + /*** + * 撤销订单 + * 用于外部接口 + * + * @param orderId + */ + int handleCancelOrder(String orderId, String userId); + + /*** + * 无法撮合时 + * 撤销市价订单 + * 用于程序内部 + * + * @param orderId + * @return + */ + int handleCancelOrder(String orderId); + + /*** + * 撤销订单 - 用于撮合机器人 + * @param orderId + * @param userId + * @return + */ + int handleCancelOrderToBot(String orderId, String userId); + + /*** + * 更新订单 + * @param order + * @return + */ + int updateByPrimaryKeySelective(PublishOrder order); + + /*** + * 更新订单状态,使用乐观锁 + * @param orderId + * @param beforeStatus + * @param laterStatus + * @return + */ + int updateStatusInVersionLock(String orderId, String beforeStatus, String laterStatus); + + /*** + * 根据id查询订单 + * @param orderId + * @return + */ + PublishOrder selectById(String orderId); + + /*** + * 使用锁查询订单 + * @param orderId + * @return + */ + PublishOrder selectByIdForUpdate(String orderId); + + /*** + * 根据条件查询首页订单 + * @param coinName + * @param unitName + * @param orderType + * @param orderStatus + * @param publishType + * @param sort + * @return + */ + List listOrder(String coinName, String unitName, String orderType, String orderStatus, + String publishType, String sort); + + /*** + * 根据币种、状态查询订单 + * 用于行情深度图 + * @param coinName + * @param unitName + * @param orderStatus + * @param orderType + * @param sort + * @return + */ + List listOrderByCoinAndUnitAndStatus(String coinName, String unitName, String orderStatus, + String orderType, String sort); + + /*** + * 根据指定条件查询用户订单 + * @param userId + * @param coinName + * @param unitName + * @param orderType + * @param publishType + * @param orderStatus + * @return + */ + List listUserOrder(String userId, String coinName, String unitName, String orderType, + String publishType, String[] orderStatus); +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/TradingDetailService.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/TradingDetailService.java new file mode 100644 index 0000000..fe5d782 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/TradingDetailService.java @@ -0,0 +1,37 @@ +package com.blockchain.server.cct.service; + +import com.blockchain.server.cct.dto.trading.DetailByOrderIdDTO; +import com.blockchain.server.cct.dto.trading.ListUserDetailDTO; +import com.blockchain.server.cct.entity.TradingDetail; + +import java.util.List; + +public interface TradingDetailService { + + /*** + * 插入成交订单扣费信息 + * @param detail + * @return + */ + int insertTradingDetail(TradingDetail detail); + + /*** + * 查询用户成交记录 + * @param userId + * @param coinName 基本货币 + * @param unitName 二级货币 + * @param beginTime 开始时间 + * @param lastTime 结束时间 + * @param status 发布订单状态 + * @return + */ + List listUserDetail(String userId, String coinName, String unitName, String beginTime, + String lastTime, String status); + + /*** + * 根据发布id查询用户成交记录列表 + * @param orderId + * @return + */ + List listDetailByOrderId(String orderId); +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/TradingRecordService.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/TradingRecordService.java new file mode 100644 index 0000000..d4d3341 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/TradingRecordService.java @@ -0,0 +1,31 @@ +package com.blockchain.server.cct.service; + +import com.blockchain.server.cct.entity.TradingRecord; + +import java.util.List; + +public interface TradingRecordService { + + /*** + * 插入成交双方订单记录 + * @param record + * @return + */ + int insertTradingRecord(TradingRecord record); + + /*** + * 根据交易币对查询成交记录 + * @param coinName + * @param unitName + * @return + */ + List listRecordByCoinAndUnit(String coinName, String unitName); + + /*** + * 根据交易币对查询最新一条成交记录 + * @param coinName + * @param unitName + * @return + */ + TradingRecord getRecordByCoinAndUnitLimitOne(String coinName, String unitName); +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/WalletService.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/WalletService.java new file mode 100644 index 0000000..3abab7d --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/WalletService.java @@ -0,0 +1,37 @@ +package com.blockchain.server.cct.service; + +import java.math.BigDecimal; + +public interface WalletService { + + /*** + * 冻结、解冻余额 + * @param userId + * @param publishId 订单id + * @param tokenName 处理货币 + * @param coinNet 主网标识 + * @param freeBalance 可用余额 + * @param freezeBalance 冻结余额 + */ + void handleBalance(String userId, String publishId, String tokenName, + String coinNet, BigDecimal freeBalance, BigDecimal freezeBalance); + + /*** + * 扣除、增加真实余额 + * @param userId + * @param detailId 成交记录id + * @param tokenName 处理货币 + * @param coinNet 主网标识 + * @param freeBalance 可用余额 + * @param freezeBalance 冻结余额 + * @param gasBalance 手续费 + */ + void handleRealBalance(String userId, String detailId, String tokenName, + String coinNet, BigDecimal freeBalance, BigDecimal freezeBalance, BigDecimal gasBalance); + + /*** + * 验证密码 + * @param pass 公钥加密后的密码 + */ + void isPassword(String pass); +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/CoinServiceImpl.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/CoinServiceImpl.java new file mode 100644 index 0000000..3bb3766 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/CoinServiceImpl.java @@ -0,0 +1,63 @@ +package com.blockchain.server.cct.service.impl; + +import com.blockchain.server.cct.common.enums.CctDataEnums; +import com.blockchain.server.cct.common.enums.CctEnums; +import com.blockchain.server.cct.common.exception.CctException; +import com.blockchain.server.cct.dto.coin.TradingOnDTO; +import com.blockchain.server.cct.entity.Coin; +import com.blockchain.server.cct.entity.TradingRecord; +import com.blockchain.server.cct.mapper.CoinMapper; +import com.blockchain.server.cct.service.CoinService; +import com.blockchain.server.cct.service.TradingRecordService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +@Service +public class CoinServiceImpl implements CoinService { + + @Autowired + private CoinMapper coinMapper; + + @Override + public Coin selectCoin(String coinName, String unitName, String status) { + return coinMapper.selectByCoinUnitAndStatus(coinName, unitName, status); + } + + @Override + public Coin checkCoinIsYes(String coinName, String unitName) { + //TODO 暂时性功能,币种不等于PC/USDT,抛出异常 +// if (!coinName.equals("PC") && unitName.equals("USDT")) { +// throw new CctException(CctEnums.TRADING_ON_STOP_DEAL); +// } + Coin coin = selectCoin(coinName, unitName, CctDataEnums.COMMON_STATUS_YES.getStrVlue()); + if (coin == null) { + throw new CctException(CctEnums.TRADING_ON_NULL); + } + return coin; + } + + @Override + public List listCoinByUnit(String unitName, String stauts) { + List coins = coinMapper.listCoinByUnit(unitName, stauts); + List tradingOns = new ArrayList<>(); + for (Coin coin : coins) { + TradingOnDTO tradingOn = new TradingOnDTO(); + tradingOn.setCoinName(coin.getCoinName()); + tradingOn.setUnitName(coin.getUnitName()); + tradingOn.setUnitDecimals(coin.getUnitDecimals()); + tradingOn.setCoinDecimals(coin.getCoinDecimals()); + tradingOn.setNewestPrice(BigDecimal.ZERO); + tradingOns.add(tradingOn); + } + return tradingOns; + } + + @Override + public List listCoinGroupByUnit() { + return coinMapper.listCoinGroupByUnit(); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/CommissionServiceImpl.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/CommissionServiceImpl.java new file mode 100644 index 0000000..98bd3a6 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/CommissionServiceImpl.java @@ -0,0 +1,209 @@ +package com.blockchain.server.cct.service.impl; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.cct.common.enums.CctDataEnums; +import com.blockchain.server.cct.common.enums.CctEnums; +import com.blockchain.server.cct.common.exception.CctException; +import com.blockchain.server.cct.dto.rpc.currency.CurrencyMarketDTO; +import com.blockchain.server.cct.entity.Commission; +import com.blockchain.server.cct.entity.rpc.user.UserRelation; +import com.blockchain.server.cct.feign.CurrencyFeign; +import com.blockchain.server.cct.feign.UserFeign; +import com.blockchain.server.cct.mapper.CommissionMapper; +import com.blockchain.server.cct.service.CommissionService; +import com.blockchain.server.cct.service.ConfigService; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.text.MessageFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.UUID; + +@Service +public class CommissionServiceImpl implements CommissionService { + + @Autowired + private CommissionMapper commissionMapper; + @Autowired + private ConfigService configService; + @Autowired + private UserFeign userFeign; + @Autowired + private CurrencyFeign currencyFeign; + + @Override + @Transactional + public void generateCommission(String userId, String recordId, BigDecimal serviceCharge, String coinName) { + //判断用户id是否为空 + checkUserIdNull(userId); + //判断代币是否为空 + checkCoinNameNull(coinName); + //判断手续费是否无法生成佣金 + if (!checkServiceCharge(serviceCharge)) { + return; + } + + //用户关系链 + UserRelation userRelation = selectRelationByUserId(userId); + //关系链不存在或者父级id不存在 + if (userRelation == null || StringUtils.isBlank(userRelation.getPid())) { + return; + } + + //查询用户注册时间是否超过佣金有效期 + boolean validity = checkCommissionValidity(userRelation.getCreateTime()); + if (!validity) { + return; + } + + //查询佣金代币 + String commissionCoin = configService.selectCommissionCoin(); + if (StringUtils.isBlank(commissionCoin)) { + return; + } + //查询佣金百分比 + BigDecimal commissionCharge = configService.selectCommissionCharge(); + if (commissionCharge == null) { + return; + } + + //计算佣金 + BigDecimal commissionAmount = computeCommissionAmount(serviceCharge, coinName, commissionCharge, commissionCoin); + + //判断是否存在 + Commission commission = new Commission(); + Date now = new Date(); + commission.setId(UUID.randomUUID().toString()); + commission.setUserId(userId); + commission.setPid(userRelation.getPid()); + commission.setRecordId(recordId); + commission.setAmount(commissionAmount); + commission.setCoinName(commissionCoin); + commission.setCreateTime(now); + commission.setModifyTime(now); + commission.setStatus(CctDataEnums.COMMON_STATUS_NO.getStrVlue()); + commissionMapper.insertSelective(commission); + } + + /*** + * 判断用户id是否为空 + * @param userId + */ + private void checkUserIdNull(String userId) { + if (StringUtils.isBlank(userId)) { + throw new CctException(CctEnums.ORDER_USERID_NULL); + } + } + + /*** + * 判断币种是否为空 + * @param coinName + */ + private void checkCoinNameNull(String coinName) { + if (StringUtils.isBlank(coinName)) { + throw new CctException(CctEnums.ORDER_COINNAME_NULL); + } + } + + /*** + * //判断手续费为空或者小于0 + * 返回false终止生成佣金方法 + * @param serviceCharge + * @return + */ + private boolean checkServiceCharge(BigDecimal serviceCharge) { + if (serviceCharge == null || serviceCharge.compareTo(BigDecimal.ZERO) <= 0) { + return false; + } + return true; + } + + /*** + * 计算佣金 + * @param serviceCharge + * @param coinName + * @param commissionCharge + * @param commissionCoin + * @return + */ + private BigDecimal computeCommissionAmount(BigDecimal serviceCharge, String coinName, BigDecimal commissionCharge, String commissionCoin) { + //如果代币跟数据库设置的代币不一致,查询最新行情获取汇率 + if (!coinName.equals(commissionCoin)) { + CurrencyMarketDTO currency = getCurrency(coinName, commissionCoin); + //获取 成交代币/佣金代币 最新行情 + BigDecimal amount = currency.getAmount(); + //将成交手续费兑换成佣金代币 : 成交手续费 * 最新行情 + serviceCharge = serviceCharge.multiply(amount); + } + + //佣金 = 成交手续费 * 佣金百分比 + return serviceCharge.multiply(commissionCharge); + } + + /*** + * 计算生成佣金用户注册时间是否超过有效期 + * @param registerTime + * @return 返回false 不生成佣金 + */ + private boolean checkCommissionValidity(Date registerTime) { + //查询佣金有效期 + Integer validity = configService.selectCommissionValidity(); + try { + //计算用户注册至今的天数 + Integer difference = checkUserRegisterTimeDifference(registerTime); + //如果用户注册至今的天数超过佣金生成有效期 + if (difference > validity) { + return false; + } + return true; + } catch (ParseException e) { + return false; + } + } + + /*** + * 计算用户注册至今的时间天数 + * @param registerTime + * @return + * @throws ParseException + */ + private Integer checkUserRegisterTimeDifference(Date registerTime) throws ParseException { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + //通过格式化去除时分秒 + String registerTimeFormat = dateFormat.format(registerTime); + String nowTimeFormat = dateFormat.format(new Date()); + //再转回日期 + registerTime = dateFormat.parse(registerTimeFormat); + Date nowTime = dateFormat.parse(nowTimeFormat); + //差再除以1天的毫秒数 + return (int) ((nowTime.getTime() - registerTime.getTime()) / (1000 * 3600 * 24)); + } + + /*** + * 查询最新行情 + * @param coinName + * @param unitName + * @return + */ + private CurrencyMarketDTO getCurrency(String coinName, String unitName) { + String currencyPair = "{0}-{1}"; + ResultDTO resultDTO = currencyFeign.get(MessageFormat.format(currencyPair, coinName, unitName)); + return resultDTO.getData(); + } + + /*** + * 根据用户查询推荐关系 + * @param userId + * @return + */ + private UserRelation selectRelationByUserId(String userId) { + ResultDTO resultDTO = userFeign.selectRelationByUserId(userId); + return resultDTO.getData(); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/ConfigServiceImpl.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/ConfigServiceImpl.java new file mode 100644 index 0000000..ba1dbfd --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/ConfigServiceImpl.java @@ -0,0 +1,79 @@ +package com.blockchain.server.cct.service.impl; + +import com.blockchain.server.cct.common.enums.CctDataEnums; +import com.blockchain.server.cct.dto.config.ConfigDTO; +import com.blockchain.server.cct.entity.Config; +import com.blockchain.server.cct.mapper.ConfigMapper; +import com.blockchain.server.cct.service.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +@Service +public class ConfigServiceImpl implements ConfigService { + + @Autowired + private ConfigMapper configMapper; + + @Override + public Config selectByKey(String key, String status) { + return configMapper.selectByKeyAndStatus(key, status); + } + + @Override + public List listServiceCharge() { + List configs = new ArrayList<>(); + //挂单数据 + Config makerCharge = configMapper.selectByKey(CctDataEnums.CONFIG_MAKER_CHARGE.getStrVlue()); + ConfigDTO makerChargeDTO = new ConfigDTO(); + makerChargeDTO.setDataTag(makerCharge.getDataTag()); + makerChargeDTO.setDataValue(makerCharge.getDataValue()); + + //吃单数据 + Config takerCharge = configMapper.selectByKey(CctDataEnums.CONFIG_TAKER_CHARGE.getStrVlue()); + ConfigDTO takerChargeDTO = new ConfigDTO(); + takerChargeDTO.setDataTag(takerCharge.getDataTag()); + takerChargeDTO.setDataValue(takerCharge.getDataValue()); + + configs.add(makerChargeDTO); + configs.add(takerChargeDTO); + return configs; + } + + @Override + public BigDecimal selectCommissionCharge() { + Config config = configMapper.selectByKeyAndStatus(CctDataEnums.CONFIG_COMMISSION_CHARGE.getStrVlue(), + CctDataEnums.COMMON_STATUS_YES.getStrVlue()); + if (config != null) { + return new BigDecimal(config.getDataValue()); + } + return null; + } + + @Override + public String selectCommissionCoin() { + Config config = configMapper.selectByKeyAndStatus(CctDataEnums.CONFIG_COMMISSION_COIN.getStrVlue(), + CctDataEnums.COMMON_STATUS_YES.getStrVlue()); + if (config != null) { + return config.getDataValue(); + } + return null; + } + + @Override + public Integer selectCommissionValidity() { + Config config = configMapper.selectByKeyAndStatus(CctDataEnums.CONFIG_COMMISSION_VALIDITY.getStrVlue(), + CctDataEnums.COMMON_STATUS_YES.getStrVlue()); + if (config != null) { + Integer validity = Integer.valueOf(config.getDataValue()); + //大于等于0的时候返回配置值,其他情况返回空 + if (validity > 0) { + return validity; + } + } + return null; + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/MatchConfigServiceImpl.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/MatchConfigServiceImpl.java new file mode 100644 index 0000000..7d6dd5c --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/MatchConfigServiceImpl.java @@ -0,0 +1,62 @@ +package com.blockchain.server.cct.service.impl; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.cct.dto.matchconfig.MatchConfigDTO; +import com.blockchain.server.cct.dto.rpc.currency.CurrencyMarketDTO; +import com.blockchain.server.cct.entity.MatchConfig; +import com.blockchain.server.cct.entity.TradingRecord; +import com.blockchain.server.cct.feign.CurrencyFeign; +import com.blockchain.server.cct.mapper.MatchConfigMapper; +import com.blockchain.server.cct.service.MatchConfigService; +import com.blockchain.server.cct.service.TradingRecordService; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; + +@Service +public class MatchConfigServiceImpl implements MatchConfigService { + + @Autowired + private MatchConfigMapper matchConfigMapper; + @Autowired + private CurrencyFeign currencyFeign; + + + @Override + public MatchConfigDTO getByCoinAndUnitBeforeToday(String coinName, String unitName) { + MatchConfig matchConfig = matchConfigMapper.selectByCoinAndUnitBeforeToday(coinName, unitName); + CurrencyMarketDTO currency = getCurrency(coinName, unitName); + //判空 + if (matchConfig != null) { + MatchConfigDTO matchConfigDTO = new MatchConfigDTO(); + BeanUtils.copyProperties(matchConfig, matchConfigDTO); + + //最新行情判空 + if (currency != null) { + //挂单金额作为最新成交价 + BigDecimal price = currency.getAmount(); + //最大价格 = 最新成交价 + (最新成交价 * 最大涨幅) + matchConfigDTO.setMaxPrice(price.add(price.multiply(matchConfigDTO.getMaxPercent()))); + //最小价格 = 最新成交价 - (最新成交价 * 最小跌幅) + matchConfigDTO.setMinPrice(price.subtract(price.multiply(matchConfigDTO.getMinPercent()))); + } + return matchConfigDTO; + } + + return null; + } + + /*** + * 获取最新行情 + * @param coinName + * @param unitName + * @return + */ + private CurrencyMarketDTO getCurrency(String coinName, String unitName) { + String currencyPari = coinName + "-" + unitName; + ResultDTO resultDTO = currencyFeign.getLast(currencyPari); + return resultDTO.getData(); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/MatchServiceImpl.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/MatchServiceImpl.java new file mode 100644 index 0000000..ba48cb1 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/MatchServiceImpl.java @@ -0,0 +1,698 @@ +package com.blockchain.server.cct.service.impl; + +import com.blockchain.common.base.constant.PushConstants; +import com.blockchain.common.base.dto.MarketDTO; +import com.blockchain.common.base.enums.PushEnums; +import com.blockchain.server.cct.common.enums.CctDataEnums; +import com.blockchain.server.cct.common.redistool.RedisTool; +import com.blockchain.server.cct.common.util.CheckConfigUtil; +import com.blockchain.server.cct.dto.matchconfig.MatchConfigDTO; +import com.blockchain.server.cct.dto.order.PublishOrderDTO; +import com.blockchain.server.cct.entity.*; +import com.blockchain.server.cct.feign.CurrencyFeign; +import com.blockchain.server.cct.feign.PushFeign; +import com.blockchain.server.cct.mapper.PublishOrderMapper; +import com.blockchain.server.cct.redis.PublishOrderCache; +import com.blockchain.server.cct.service.*; +import com.codingapi.tx.annotation.ITxTransaction; +import com.codingapi.tx.annotation.TxTransaction; +import org.redisson.api.RedissonClient; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronizationAdapter; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +import java.math.BigDecimal; +import java.text.MessageFormat; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Service +public class MatchServiceImpl implements MatchService, ITxTransaction { + @Autowired + private PublishOrderService orderService; + @Autowired + private TradingRecordService recordService; + @Autowired + private TradingDetailService detailService; + @Autowired + private WalletService walletService; + @Autowired + private CoinService coinService; + @Autowired + private ConfigService configService; + @Autowired + private MatchConfigService matchConfigService; + @Autowired + private CommissionService commissionService; + @Autowired + private PublishOrderMapper orderMapper; + @Autowired + private CheckConfigUtil configUtil; + @Autowired + private CurrencyFeign currencyFeign; + @Autowired + private PushFeign pushFeign; + @Autowired + private RedisTool redisTool; + @Autowired + private RedissonClient redissonClient; + @Autowired + private PublishOrderCache orderCache; + + //撮合计算参数常量 + private static final String TRANS_PRICE = "price"; + private static final String TRANS_NUM = "transNum"; + private static final String TRANS_TURNOVER = "turnover"; + + //异步线程池 + private static ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); + + private static final BigDecimal MINUS_ONE = new BigDecimal("-1");//负一 + private static final BigDecimal ZERO = BigDecimal.ZERO;//零 + private static final int DEFAULT_PAGESIZE = 10; //查询撮合订单默认分页 + private static final int MATCH_BOT_ORDER_PAGESIZE = 50; //查询单价范围内的挂单默认分页 + + @Override + public List listBeMatchOrder(String orderId) { + //查询订单 + PublishOrder order = orderService.selectById(orderId); + String publishType = order.getPublishType(); //发布类型 + String orderType = order.getOrderType(); //订单类型 + String coinName = order.getCoinName(); //基本货币 + String unitName = order.getUnitName(); //二级货币 + BigDecimal price = order.getUnitPrice(); //单价 + String status = CctDataEnums.ORDER_STATUS_MATCH.getStrVlue(); //查询的订单状态 + + //返回集合 + List matchs = new ArrayList<>(); + + //查询限价可撮合订单 + if (publishType.equals(CctDataEnums.PUBLISH_TYPE_LIMIT.getStrVlue())) { + if (orderType.equals(CctDataEnums.ORDER_TYPE_BUY.getStrVlue())) { + matchs = orderMapper.listMatchSellOrder(unitName, coinName, status, price, "", DEFAULT_PAGESIZE); + } else { + matchs = orderMapper.listMatchBuyOrder(unitName, coinName, status, price, "", DEFAULT_PAGESIZE); + } + } + + //查询市价可撮合订单 + if (publishType.equals(CctDataEnums.PUBLISH_TYPE_MARKET.getStrVlue())) { + if (orderType.equals(CctDataEnums.ORDER_TYPE_BUY.getStrVlue())) { + matchs = orderMapper.listMatchSellOrder( + unitName, coinName, status, price, CctDataEnums.PUBLISH_TYPE_LIMIT.getStrVlue(), DEFAULT_PAGESIZE); + } else { + matchs = orderMapper.listMatchBuyOrder( + unitName, coinName, status, price, CctDataEnums.PUBLISH_TYPE_LIMIT.getStrVlue(), DEFAULT_PAGESIZE); + } + } + + //返回查询集合,可以为空 + return matchs; + } + + @Override + public List listBeMatchOrderToBot(String coinName, String unitName, BigDecimal minPrice, BigDecimal maxPrice) { + String orderStatus = CctDataEnums.ORDER_STATUS_MATCH.getStrVlue(); + String publishType = CctDataEnums.PUBLISH_TYPE_LIMIT.getStrVlue(); + List orders = orderMapper.listMatchOrderToBot(unitName, coinName, minPrice, maxPrice, orderStatus, publishType, + MATCH_BOT_ORDER_PAGESIZE); + + List orderDTOS = new ArrayList<>(); + for (PublishOrder order : orders) { + PublishOrderDTO orderDTO = new PublishOrderDTO(); + BeanUtils.copyProperties(order, orderDTO); + orderDTOS.add(orderDTO); + } + + return orderDTOS; + } + + @Override + public void convertAndSend(String coinName, String unitName) { + //使用异步线程推送前端 + cachedThreadPool.execute(new Runnable() { + @Override + public void run() { + //发送广播到前端,更新前端行情 + redisTool.send(coinName.toUpperCase(), unitName.toUpperCase()); + } + }); + } + + @Override + @Transactional + @TxTransaction(isStart = true) + public boolean handleMatch(String matchId, String bymatchId) { + + //查询撮合订单和被撮合订单,使用排他锁 + PublishOrder match = orderService.selectByIdForUpdate(matchId); + PublishOrder bymatch = orderService.selectByIdForUpdate(bymatchId); + + String coinName = match.getCoinName(); + String unitName = match.getUnitName(); + + //判断当前是否停盘 + boolean suspendedFlag = checkIsSuspended(coinName, unitName); + //停盘时,终止外部程序继续撮合 + if (suspendedFlag) { + return false; + } + + //如果被撮合订单状态不是已撮合,返回true,继续撮合下一条数据 + if (!bymatch.getOrderStatus().equals(CctDataEnums.ORDER_STATUS_MATCH.getStrVlue())) { + return true; + } + //如果撮合订单状态不是已撮合,返回false,终止外部撮合程序 + if (!match.getOrderStatus().equals(CctDataEnums.ORDER_STATUS_MATCH.getStrVlue())) { + return false; + } + + //判断吃单单价是否可以撮合 + boolean matchPriceFlag = checkMatchPrice(match.getUnitPrice(), coinName, unitName); + //如果不可以撮合返回false终止外部程序继续调用撮合 + if (!matchPriceFlag) { + return false; + } + //判断挂单单价是否可以撮合 + boolean bymatchPriceFlag = checkMatchPrice(bymatch.getUnitPrice(), coinName, unitName); + //如果不可以撮合,返回true,继续下一次撮合 + if (!bymatchPriceFlag) { + return true; + } + + //变量保存撮合订单数据,用于被撮合订单的数据计算 + BigDecimal mprice = match.getUnitPrice(); + BigDecimal mlastNum = match.getLastNum(); + BigDecimal mturnover = match.getLastTurnover(); + + //计算撮合数据完毕,返回撮合数据 + Map matchMap = checkPublishType(match, bymatch.getUnitPrice(), bymatch.getLastNum(), bymatch.getLastTurnover()); + Map bymatchMap = checkPublishType(bymatch, mprice, mlastNum, mturnover); + + //插入成交双方订单信息 + String recordId = insertTradingRecord(match.getId(), bymatch.getId(), matchMap.get(TRANS_PRICE), bymatchMap.get(TRANS_PRICE), + matchMap.get(TRANS_NUM), coinName, unitName, bymatch.getOrderType()); + + //挂单手续费 + BigDecimal makerCharge = configUtil.checkIsMakerCharge(bymatch.getUserId()); + //吃单手续费 + BigDecimal takerCharge = configUtil.checkIsTakerCharge(match.getUserId()); + + //更新撮合双方的钱包以及插入变更信息 + //主动撮合方是吃单、被动撮合是挂单 + updateMatchWallet(matchMap, match, takerCharge, CctDataEnums.DETAIL_TYPE_TAKER.getStrVlue(), recordId); + updateMatchWallet(bymatchMap, bymatch, makerCharge, CctDataEnums.DETAIL_TYPE_MAKER.getStrVlue(), recordId); + + //被撮合订单的单价、成交数量、订单类型 + BigDecimal bymatchPrice = bymatchMap.get(TRANS_PRICE); + BigDecimal bymatchTransNum = bymatchMap.get(TRANS_NUM); + String bymatchOrderType = bymatch.getOrderType(); + //撮合订单的单价、成交数量、订单类型 + BigDecimal matchPrice = matchMap.get(TRANS_PRICE); + BigDecimal matchTransNum = matchMap.get(TRANS_NUM); + String matchOrderType = match.getOrderType(); + + //判断订单剩余是否可继续撮合 + boolean isMatch = checkMatchLast(match); + + //更新缓存中的盘口数据 + lockRedisOrderList(coinName, unitName, matchOrderType, Double.parseDouble(matchPrice.toString()), matchTransNum); + lockRedisOrderList(coinName, unitName, bymatchOrderType, Double.parseDouble(bymatchPrice.toString()), bymatchTransNum); + + //撮合完毕,异步执行行情数据更新、广播最新行情 + asyncSend(match, bymatch, bymatchPrice, bymatchTransNum); + + return isMatch; + } + + /*** + * 检查发布类型 + * @param order + * @param price + * @param lastNum + * @param lastTurnover + */ + private Map checkPublishType(PublishOrder order, BigDecimal price, BigDecimal lastNum, BigDecimal lastTurnover) { + //订单发布类型 + String publishType = order.getPublishType(); + //撮合数据 + Map trans = new HashMap<>(); + + //限价订单 + if (publishType.equals(CctDataEnums.PUBLISH_TYPE_LIMIT.getStrVlue())) { + //计算后返回撮合数据 + trans = toCalculateLimitOrder(order, lastNum, lastTurnover); + trans.put(TRANS_PRICE, order.getUnitPrice()); + } + //市价订单 + if (publishType.equals(CctDataEnums.PUBLISH_TYPE_MARKET.getStrVlue())) { + //计算后返回撮合数据 + trans = toCalculateMarketOrder(order, price, lastNum, lastTurnover); + trans.put(TRANS_PRICE, price); + } + + return trans; + } + + /*** + * 计算限价订单 + * @param order + * @param lastNum + */ + private Map toCalculateLimitOrder(PublishOrder order, BigDecimal lastNum, BigDecimal lastTurnover) { + //限价订单扣除情况一致 + //撮合数量 + BigDecimal transNum = toCalculateLimitTransNum(order.getUnitPrice(), order.getLastNum(), lastNum, lastTurnover); + //撮合交易额 + BigDecimal turnover = toCalculateLimitTransTurnover(order.getUnitPrice(), order.getLastTurnover(), transNum, lastTurnover); + //订单扣除 剩余数量 剩余交易额 + order.setLastNum(order.getLastNum().subtract(transNum)); + order.setLastTurnover(order.getLastTurnover().subtract(turnover)); + //如果剩余数量不足,设置为已完成 + if (order.getLastNum().compareTo(ZERO) <= 0) { + order.setOrderStatus(CctDataEnums.ORDER_STATUS_FINISH.getStrVlue()); + } + //如果剩余交易额不足,设置为已完成 + if (order.getLastTurnover().compareTo(ZERO) <= 0) { + order.setOrderStatus(CctDataEnums.ORDER_STATUS_FINISH.getStrVlue()); + } + //更新版本、时间 + order.setVersion(order.getVersion() + 1); + order.setModifyTime(new Date()); + orderService.updateByPrimaryKeySelective(order); + + //返回撮合成功数据 + Map trans = new HashMap<>(); + trans.put(TRANS_NUM, transNum); + trans.put(TRANS_TURNOVER, turnover); + return trans; + } + + /*** + * 计算限价交易撮合数量 + * + * @param price + * @param orderLastNum + * @param lastNum + * @param lastTurnover + * @return + */ + private BigDecimal toCalculateLimitTransNum(BigDecimal price, BigDecimal orderLastNum, BigDecimal lastNum, BigDecimal lastTurnover) { + //如果没有剩余数量,代表对方订单是市价买单 + if (lastNum == null || lastNum.compareTo(ZERO) == 0) { + //计算市价买单的剩余数量 + lastNum = lastTurnover.divide(price, CctDataEnums.DECIMAL_LENGTH.getIntValue(), BigDecimal.ROUND_DOWN); + } + //计算撮合数量 + return orderLastNum.min(lastNum); + } + + /*** + * 计算限价交易撮合交易额 + * + * @param price + * @param orderLastTurnover + * @param transNum + * @param lastTurnover + * @return + */ + private BigDecimal toCalculateLimitTransTurnover(BigDecimal price, BigDecimal orderLastTurnover, BigDecimal transNum, BigDecimal lastTurnover) { + //lastTurnover为空,代表对方订单是市价卖单 +// if (lastTurnover == null || lastTurnover.compareTo(ZERO) == 0) { +// return price.multiply(transNum); +// } else { +// //其他情况返回 +// return orderLastTurnover.min(lastTurnover); +// } + + return price.multiply(transNum); + } + + /*** + * 计算市价订单类型(买卖) + * @param order + * @param price + * @param lastNum + * @param lastTurnover + */ + private Map toCalculateMarketOrder(PublishOrder order, BigDecimal price, BigDecimal lastNum, BigDecimal lastTurnover) { + //撮合数量 + BigDecimal transNum = ZERO; + //交易额 + BigDecimal turnover = ZERO; + //市价买 + if (order.getOrderType().equals(CctDataEnums.ORDER_TYPE_BUY.getStrVlue())) { + //撮合交易额 + turnover = order.getLastTurnover().min(lastTurnover); + //订单扣除 剩余交易额 + order.setLastTurnover(order.getLastTurnover().subtract(turnover)); + //如果剩余交易额不足,设置为已完成 + if (order.getLastTurnover().compareTo(ZERO) <= 0) { + order.setOrderStatus(CctDataEnums.ORDER_STATUS_FINISH.getStrVlue()); + } + //撮合数量 + transNum = turnover.divide(price, CctDataEnums.DECIMAL_LENGTH.getIntValue(), BigDecimal.ROUND_DOWN); + } + + //市价卖 + if (order.getOrderType().equals(CctDataEnums.ORDER_TYPE_SELL.getStrVlue())) { + //撮合数量 + transNum = order.getLastNum().min(lastNum); + //交易额 + turnover = price.multiply(transNum); + //订单扣除 剩余数量 + order.setLastNum(order.getLastNum().subtract(transNum)); + //如果剩余数量不足,设置为已完成 + if (order.getLastNum().compareTo(ZERO) <= 0) { + order.setOrderStatus(CctDataEnums.ORDER_STATUS_FINISH.getStrVlue()); + } + } + + //更新版本、时间 + order.setVersion(order.getVersion() + 1); + order.setModifyTime(new Date()); + orderService.updateByPrimaryKeySelective(order); + + //返回撮合成功数据 + Map trans = new HashMap<>(); + trans.put(TRANS_NUM, transNum); + trans.put(TRANS_TURNOVER, turnover); + return trans; + } + + /**** + * 撮合后更新钱包数据,并插入变更记录 + * @param trans 撮合数据 + * @param order 订单对象 + * @param serviceChargeRatio 手续费比例 + * @param tradingType 交易类型 挂单 | 吃单 + * @param recordId + */ + private void updateMatchWallet(Map trans, PublishOrder order, BigDecimal serviceChargeRatio, String + tradingType, String recordId) { + //基本货币、二级货币 + String coinName = order.getCoinName(); + String unitName = order.getUnitName(); + //查询币种详细信息 + Coin coin = coinService.selectCoin(coinName, unitName, CctDataEnums.COMMON_STATUS_YES.getStrVlue()); + /*** + * 处理买方钱包: + * coinWallet :totalNum +(transNum - serviceCharge) + * freeNum +(transNum - serviceCharge) + * unitWallet :totalNum -(transNum * price) + * blockNum -(transNum * price) + * + * 处理卖方钱包: + * coinWallet :totalNum - transNum + * blockNum - transNum + * unitWallet :totalNum +(transNum * price - serviceCharge) + * freeNum +(transNum * price - serviceCharge) + */ + + //撮合数据 + BigDecimal transNum = trans.get(TRANS_NUM); + BigDecimal transPrice = trans.get(TRANS_PRICE); + BigDecimal turnover = trans.get(TRANS_TURNOVER); + + //扣除的手续费 + BigDecimal serviceCharge = BigDecimal.ZERO; + //增加的代币 + String getCoin = null; + + //买方 + if (order.getOrderType().equals(CctDataEnums.ORDER_TYPE_BUY.getStrVlue())) { + //买方手续费 + serviceCharge = transNum.multiply(serviceChargeRatio); + //coin钱包实际增加的数量 + BigDecimal realAmount = transNum.subtract(serviceCharge); + //unit钱包扣除的数量 + BigDecimal deduct = turnover.multiply(MINUS_ONE); + //插入成交订单详情记录 + String detailId = insertTradingDetail(order.getId(), recordId, transNum, transPrice, realAmount, tradingType, + serviceChargeRatio, serviceCharge, coinName); + //扣除总余额、冻结余额,单位:二级货币 + walletService.handleRealBalance(order.getUserId(), detailId, unitName, coin.getUnitNet(), ZERO, deduct, ZERO); + //增加余额的代币 + getCoin = coinName; + //增加总余额、可用余额,单位:基本货币 + walletService.handleRealBalance(order.getUserId(), detailId, getCoin, coin.getCoinNet(), realAmount, ZERO, serviceCharge); + } + + //卖方 + if (order.getOrderType().equals(CctDataEnums.ORDER_TYPE_SELL.getStrVlue())) { + //卖方手续费 + serviceCharge = turnover.multiply(serviceChargeRatio); + //unit钱包实际增加的数量 + BigDecimal realAmount = turnover.subtract(serviceCharge); + //coin钱包扣除的数量 + BigDecimal deduct = transNum.multiply(MINUS_ONE); + //插入成交订单详情记录 + String detailId = insertTradingDetail(order.getId(), recordId, transNum, transPrice, realAmount, tradingType, + serviceChargeRatio, serviceCharge, order.getUnitName()); + //扣除总余额、冻结余额,单位:基本货币 + walletService.handleRealBalance(order.getUserId(), detailId, coinName, coin.getCoinNet(), ZERO, deduct, ZERO); + //增加余额的代币 + getCoin = unitName; + //增加总余额、可用余额,单位:二级货币 + walletService.handleRealBalance(order.getUserId(), detailId, getCoin, coin.getUnitNet(), realAmount, ZERO, serviceCharge); + } + + //生成佣金 + commissionService.generateCommission(order.getUserId(), recordId, serviceCharge, getCoin); + } + + /*** + * 判断订单是否可以继续撮合 + * + * @param order + * @return + */ + private boolean checkMatchLast(PublishOrder order) { + //如果是市价卖单,则判断剩余数量 + //其余的类型,则判断剩余交易额 + if (order.getPublishType().equals(CctDataEnums.PUBLISH_TYPE_MARKET.getStrVlue()) && + order.getOrderType().equals(CctDataEnums.ORDER_TYPE_SELL.getStrVlue())) { + if (order.getLastNum().compareTo(BigDecimal.ZERO) <= 0) { + return false; + } + } else { + if (order.getLastTurnover().compareTo(BigDecimal.ZERO) <= 0) { + return false; + } + } + return true; + } + + /*** + * 检查限价订单是否符合单价范围 + * @param price + * @param coinName + * @param unitName + */ + private boolean checkMatchPrice(BigDecimal price, String coinName, String unitName) { + //判断币种是否打开撮合限制 + String publishLimitKey = CctDataEnums.CONFIG_MATCH_LIMIT.getStrVlue(); + String coinPublishLimitKey = MessageFormat.format(publishLimitKey, coinName, unitName); + Config config = configService.selectByKey(coinPublishLimitKey, CctDataEnums.COMMON_STATUS_YES.getStrVlue()); + //没打开限制,返回 + if (config == null) { + return true; + } + + //查询单价范围限制 + MatchConfigDTO matchConfigDTO = matchConfigService.getByCoinAndUnitBeforeToday(coinName, unitName); + //判空 + if (matchConfigDTO != null) { + BigDecimal minPrice = matchConfigDTO.getMinPrice(); + BigDecimal maxPrice = matchConfigDTO.getMaxPrice(); + //最小单价判空 + if (minPrice != null) { + //下单单价未达到最低单价 + if (price.compareTo(minPrice) < 0) { + return false; + } + } + //最大单价判空 + if (maxPrice != null) { + //下单单价超出最大单价 + if (price.compareTo(maxPrice) > 0) { + return false; + } + } + } + return true; + } + + /*** + * 判断当前是否停盘 + * 停盘返回true + * @return + */ + private boolean checkIsSuspended(String coinName, String unitName) { + //根据币种查询停盘配置 + String suspendedKey = CctDataEnums.CONFIG_SUSPENDED.getStrVlue(); + String coinSuspendedKey = MessageFormat.format(suspendedKey, coinName, unitName); + //查询开启的停盘配置 + Config config = configService.selectByKey(coinSuspendedKey, CctDataEnums.COMMON_STATUS_YES.getStrVlue()); + if (config != null) { + return true; + } + return false; + } + + /*** + * 插入成交订单扣费记录 + * @param orderId 订单id + * @param recordId 成交记录 + * @param realAmount 最终获得的数量 + * @param tradingType 成交订单类型 吃单|挂单 + * @param serviceChargeRatio 手续费比例 + * @param serviceCharge 实际手续费 + * @param coinName 币种 + * @return + */ + private String insertTradingDetail(String orderId, String recordId, BigDecimal tradingNum, BigDecimal pirce, + BigDecimal realAmount, String tradingType, BigDecimal serviceChargeRatio, + BigDecimal serviceCharge, String coinName) { + TradingDetail detail = new TradingDetail(); + String uuid = UUID.randomUUID().toString(); + detail.setId(UUID.randomUUID().toString()); + detail.setRecordId(recordId); + detail.setPublishId(orderId); + detail.setTotalAmount(realAmount.add(serviceCharge)); + detail.setRealAmount(realAmount); + detail.setTradingNum(tradingNum); + detail.setUnitPrice(pirce); + detail.setServiceCharge(serviceCharge); + detail.setChargeRatio(serviceChargeRatio); + detail.setCoinName(coinName); + detail.setTradingType(tradingType); + detail.setCreateTime(new Date()); + detailService.insertTradingDetail(detail); + return uuid; + } + + /*** + * 插入成交双方订单记录 + * @param takerId + * @param makerId + * @param takerPrice + * @param makerPrice + * @param transNum + * @param coinName + * @param unitName + * @param tradingType + * @return + */ + private String insertTradingRecord(String takerId, String makerId, BigDecimal takerPrice, + BigDecimal makerPrice, BigDecimal transNum, + String coinName, String unitName, String tradingType) { + String uuid = UUID.randomUUID().toString(); + Date now = new Date(); + TradingRecord record = new TradingRecord(); + record.setId(uuid); + record.setTakerId(takerId); + record.setMakerId(makerId); + record.setTakerPrice(takerPrice); + record.setMakerPrice(makerPrice); + record.setTradingNum(transNum); + record.setTradingType(tradingType); + record.setCoinName(coinName); + record.setUnitName(unitName); + record.setCreateTime(now); + recordService.insertTradingRecord(record); + return uuid; + } + + /*** + * 异步调用 + * 插入最新行情、广播前端、发送消息通知 + * @param match + * @param bymatch + * @param bymatchPrice + * @param bymatchTransNum + */ + private void asyncSend(PublishOrder match, PublishOrder bymatch, BigDecimal bymatchPrice, BigDecimal bymatchTransNum) { + //使用异步线程 + cachedThreadPool.execute(new Runnable() { + @Override + public void run() { + + String coinName = match.getCoinName(); + String unitName = match.getUnitName(); + + //发送广播到前端,更新前端行情 + redisTool.send(coinName, unitName); + + //发送最新用户订单给当前用户 + redisTool.sendToUserOrder(match.getUserId(), coinName, unitName, match.getOrderType()); + redisTool.sendToUserOrder(bymatch.getUserId(), coinName, unitName, bymatch.getOrderType()); + + //发送消息通知给用户手机 + pushToSingle(match.getUserId(), match.getId(), PushEnums.CCT_HISTORY_MARK.getPushType()); + pushToSingle(bymatch.getUserId(), bymatch.getId(), PushEnums.CCT_HISTORY_MARK.getPushType()); + + //开启插入最新行情 + currencyFeign.save(coinName, unitName, bymatchPrice, bymatchTransNum, new Date().getTime(), bymatch.getOrderType()); + } + }); + } + + /*** + * 发送手机消息通知(个推消息推送API) + * @param userId + * @param orderId + * @param pushType + */ + private void pushToSingle(String userId, String orderId, String pushType) { + Map payload = new HashMap<>(); + payload.put(PushConstants.ORDER_ID, orderId); + payload.put(PushConstants.PUSH_TYPE, pushType); + pushFeign.pushToSingle(userId, pushType, payload); + } + + /*** + * 判断是否存在key + * 存在则进行加锁并更新redis盘口数据 + * @param coinName + * @param unitName + * @param orderType + * @param price + * @param transNum + */ + private void lockRedisOrderList(String coinName, String unitName, String orderType, Double price, BigDecimal transNum) { + //是否成功获取锁标识 + boolean lockFlag = false; + //加锁,进行添加数据 + while (!lockFlag) { + //循环,直到获取锁为止 + lockFlag = orderCache.tryFairLock(redissonClient, coinName, unitName, orderType); + //拿到锁,处理数据 + if (lockFlag) { + //根据单价(sortSet排序分数)获取订单数据 + Set redisOrder = orderCache.getZSetByScore(coinName, unitName, orderType, price); + //用迭代器更新数据 + Iterator iterator = redisOrder.iterator(); + //处理数据 + while (iterator.hasNext()) { + MarketDTO order = (MarketDTO) iterator.next(); + //更新剩余数量 + order.setTotalLastNum(order.getTotalLastNum().subtract(transNum)); + //删除旧数据 + orderCache.removeZSetByScore(coinName, unitName, orderType, price); + //如果剩余数量大于0,添加数据 + if (order.getTotalLastNum().compareTo(BigDecimal.ZERO) > 0) { + //覆盖数据 + orderCache.setZSetValue(coinName, unitName, orderType, order, price); + } + } + //释放锁 + orderCache.unFairLock(redissonClient, coinName, unitName, orderType); + } + } + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/PublishOrderServiceImpl.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/PublishOrderServiceImpl.java new file mode 100644 index 0000000..3e81c35 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/PublishOrderServiceImpl.java @@ -0,0 +1,709 @@ +package com.blockchain.server.cct.service.impl; + +import com.blockchain.common.base.dto.MarketDTO; +import com.blockchain.server.cct.common.enums.CctDataEnums; +import com.blockchain.server.cct.common.enums.CctEnums; +import com.blockchain.server.cct.common.exception.CctException; +import com.blockchain.server.cct.common.util.CCTExceptionPreconditionUtils; +import com.blockchain.server.cct.common.util.CheckConfigUtil; +import com.blockchain.server.cct.dto.matchconfig.MatchConfigDTO; +import com.blockchain.server.cct.dto.order.PublishOrderParamDTO; +import com.blockchain.server.cct.entity.Coin; +import com.blockchain.server.cct.entity.Config; +import com.blockchain.server.cct.entity.PublishOrder; +import com.blockchain.server.cct.feign.UserFeign; +import com.blockchain.server.cct.mapper.PublishOrderMapper; +import com.blockchain.server.cct.redis.PublishOrderCache; +import com.blockchain.server.cct.service.*; +import com.codingapi.tx.annotation.ITxTransaction; +import com.codingapi.tx.annotation.TxTransaction; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.text.MessageFormat; +import java.util.*; + +@Service +public class PublishOrderServiceImpl implements PublishOrderService, ITxTransaction { + + @Autowired + private CoinService coinService; + @Autowired + private WalletService walletService; + @Autowired + private PublishOrderMapper publishOrderMapper; + @Autowired + private UserFeign userFeign; + @Autowired + private CheckConfigUtil checkConfigUtil; + @Autowired + private RedissonClient redissonClient; + @Autowired + private PublishOrderCache orderCache; + @Autowired + private MatchConfigService matchConfigService; + @Autowired + private ConfigService configService; + + private static final BigDecimal MINUS_ONE = new BigDecimal("-1");//负一 + + @Override + @Transactional + @TxTransaction(isStart = true) + public String handleLimitBuy(PublishOrderParamDTO paramDTO) { + //发布参数判空、是否合法 + checkPublishStrParam(paramDTO); + checkLimitPublishNum(paramDTO); + //是否休市 + checkConfigUtil.checkIsClosed(); + //币对是否可用 + Coin coin = coinService.checkCoinIsYes(paramDTO.getCoinName(), paramDTO.getUnitName()); + //校验数量、单价小数位是否合法 + checkPublishDecimal(paramDTO.getTotalNum(), coin.getCoinDecimals(), CctEnums.ORDER_NUM_ERROR); + checkPublishDecimal(paramDTO.getUnitPrice(), coin.getUnitDecimals(), CctEnums.ORDER_PRICE_ERROR); + //检查单价是否符合交易限额 + checkLimitPrice(paramDTO.getUnitPrice(), coin.getCoinName(), coin.getUnitName()); + + //用户是否有权限交易 + userFeign.hasLowAuthAndUserList(paramDTO.getUserId()); + //校验密码 + walletService.isPassword(paramDTO.getPass()); + + //计算数据 + BigDecimal totalNum = paramDTO.getTotalNum(); //发布数量 + BigDecimal price = paramDTO.getUnitPrice(); //单价 + //交易额 + BigDecimal turnover = totalNum.multiply(price).setScale(CctDataEnums.DECIMAL_LENGTH.getIntValue(), BigDecimal.ROUND_DOWN); + //设置交易额 + paramDTO.setTurnover(turnover); + //计算冻结金额 + BigDecimal deduct = turnover.multiply(MINUS_ONE); + //插入订单 + String publishId = insertOrder(paramDTO, CctDataEnums.ORDER_TYPE_BUY.getStrVlue(), + CctDataEnums.PUBLISH_TYPE_LIMIT.getStrVlue(), CctDataEnums.ORDER_STATUS_NEW.getStrVlue()); + //冻结余额 + walletService.handleBalance(paramDTO.getUserId(), publishId, coin.getUnitName(), coin.getUnitNet(), deduct, turnover); + + //将新发布的盘口数据存入缓存中 + addRedisOrderList(paramDTO, CctDataEnums.ORDER_TYPE_BUY.getStrVlue()); + + return publishId; + } + + @Override + @Transactional + @TxTransaction(isStart = true) + public String handleLimitSell(PublishOrderParamDTO paramDTO) { + //发布参数判空、是否合法 + checkPublishStrParam(paramDTO); + checkLimitPublishNum(paramDTO); + //是否休市 + checkConfigUtil.checkIsClosed(); + //币对是否可用 + Coin coin = coinService.checkCoinIsYes(paramDTO.getCoinName(), paramDTO.getUnitName()); + //校验数量、单价小数位是否合法 + checkPublishDecimal(paramDTO.getTotalNum(), coin.getCoinDecimals(), CctEnums.ORDER_NUM_ERROR); + checkPublishDecimal(paramDTO.getUnitPrice(), coin.getUnitDecimals(), CctEnums.ORDER_PRICE_ERROR); + //检查单价是否符合交易限额 + checkLimitPrice(paramDTO.getUnitPrice(), coin.getCoinName(), coin.getUnitName()); + + //用户是否有交易权限 + userFeign.hasLowAuthAndUserList(paramDTO.getUserId()); + //校验密码 + walletService.isPassword(paramDTO.getPass()); + + + BigDecimal totalNum = paramDTO.getTotalNum(); //发布数量 + BigDecimal price = paramDTO.getUnitPrice(); //单价 + //交易额 + BigDecimal turnover = totalNum.multiply(price).setScale(CctDataEnums.DECIMAL_LENGTH.getIntValue(), BigDecimal.ROUND_DOWN); + //设置交易额 + paramDTO.setTurnover(turnover); + //计算冻结余额 + BigDecimal deduct = totalNum.multiply(MINUS_ONE); + //插入订单 + String publishId = insertOrder(paramDTO, CctDataEnums.ORDER_TYPE_SELL.getStrVlue(), + CctDataEnums.PUBLISH_TYPE_LIMIT.getStrVlue(), CctDataEnums.ORDER_STATUS_NEW.getStrVlue()); + //冻结余额 + walletService.handleBalance(paramDTO.getUserId(), publishId, coin.getCoinName(), coin.getCoinNet(), deduct, totalNum); + + //将新发布的盘口数据存入缓存中 + addRedisOrderList(paramDTO, CctDataEnums.ORDER_TYPE_SELL.getStrVlue()); + + return publishId; + } + + @Override + @Transactional + @TxTransaction + public String handleLimitBuyToBot(String userId, String coinName, String unitName, BigDecimal totalNum, BigDecimal price) { + //币对是否可用 + Coin coin = coinService.checkCoinIsYes(coinName, unitName); + //交易额 + BigDecimal turnover = totalNum.multiply(price).setScale(CctDataEnums.DECIMAL_LENGTH.getIntValue(), BigDecimal.ROUND_DOWN); + //构造发布参数对象 + PublishOrderParamDTO paramDTO = new PublishOrderParamDTO(userId, null, unitName, coinName, totalNum, price, turnover); + //计算冻结金额 + BigDecimal deduct = turnover.multiply(MINUS_ONE); + //插入订单 + String publishId = insertOrder(paramDTO, CctDataEnums.ORDER_TYPE_BUY.getStrVlue(), + CctDataEnums.PUBLISH_TYPE_LIMIT.getStrVlue(), CctDataEnums.ORDER_STATUS_MATCH.getStrVlue()); + //冻结余额 + walletService.handleBalance(userId, publishId, coin.getUnitName(), coin.getUnitNet(), deduct, turnover); + //将新发布的盘口数据存入缓存中 + addRedisOrderList(paramDTO, CctDataEnums.ORDER_TYPE_BUY.getStrVlue()); + //返回订单id + return publishId; + } + + @Override + @Transactional + @TxTransaction + public String handleLimitSellToBot(String userId, String coinName, String unitName, BigDecimal totalNum, BigDecimal price) { + //币对是否可用 + Coin coin = coinService.checkCoinIsYes(coinName, unitName); + //交易额 + BigDecimal turnover = totalNum.multiply(price).setScale(CctDataEnums.DECIMAL_LENGTH.getIntValue(), BigDecimal.ROUND_DOWN); + //构造发布参数对象 + PublishOrderParamDTO paramDTO = new PublishOrderParamDTO(userId, null, unitName, coinName, totalNum, price, turnover); + //计算冻结余额 + BigDecimal deduct = totalNum.multiply(MINUS_ONE); + //插入订单 + String publishId = insertOrder(paramDTO, CctDataEnums.ORDER_TYPE_SELL.getStrVlue(), + CctDataEnums.PUBLISH_TYPE_LIMIT.getStrVlue(), CctDataEnums.ORDER_STATUS_MATCH.getStrVlue()); + //冻结余额 + walletService.handleBalance(userId, publishId, coin.getCoinName(), coin.getCoinNet(), deduct, totalNum); + //将新发布的盘口数据存入缓存中 + addRedisOrderList(paramDTO, CctDataEnums.ORDER_TYPE_SELL.getStrVlue()); + //返回订单id + return publishId; + } + + @Override + @Transactional + @TxTransaction(isStart = true) + public String handleMarketBuy(PublishOrderParamDTO paramDTO) { + //发布参数判空、是否合法 + checkPublishStrParam(paramDTO); + checkMarketPublishNum(paramDTO, CctDataEnums.ORDER_TYPE_BUY.getStrVlue()); + //是否休市 + checkConfigUtil.checkIsClosed(); + //币对是否可用 + Coin coin = coinService.checkCoinIsYes(paramDTO.getCoinName(), paramDTO.getUnitName()); + //校验数量、单价小数位是否合法 + checkPublishDecimal(paramDTO.getTurnover(), coin.getUnitDecimals(), CctEnums.ORDER_TURNOVER_ERROR); + + //用户是否有权限交易 + userFeign.hasLowAuthAndUserList(paramDTO.getUserId()); + //校验密码 + walletService.isPassword(paramDTO.getPass()); + + + //计算冻结余额 + BigDecimal deduct = paramDTO.getTurnover().multiply(MINUS_ONE); + //插入订单 + String publishId = insertOrder(paramDTO, CctDataEnums.ORDER_TYPE_BUY.getStrVlue(), + CctDataEnums.PUBLISH_TYPE_MARKET.getStrVlue(), CctDataEnums.ORDER_STATUS_NEW.getStrVlue()); + //冻结余额 + walletService.handleBalance(paramDTO.getUserId(), publishId, coin.getUnitName(), coin.getUnitNet(), deduct, paramDTO.getTurnover()); + + return publishId; + } + + @Override + @Transactional + @TxTransaction(isStart = true) + public String handleMarketSell(PublishOrderParamDTO paramDTO) { + //发布参数判空、是否合法 + checkPublishStrParam(paramDTO); + checkMarketPublishNum(paramDTO, CctDataEnums.ORDER_TYPE_SELL.getStrVlue()); + //是否休市 + checkConfigUtil.checkIsClosed(); + //币对是否可用 + Coin coin = coinService.checkCoinIsYes(paramDTO.getCoinName(), paramDTO.getUnitName()); + //校验数量、单价小数位是否合法 + checkPublishDecimal(paramDTO.getTotalNum(), coin.getCoinDecimals(), CctEnums.ORDER_NUM_ERROR); + + //用户是否有权限交易 + userFeign.hasLowAuthAndUserList(paramDTO.getUserId()); + //校验密码 + walletService.isPassword(paramDTO.getPass()); + + + //计算冻结余额 + BigDecimal deduct = paramDTO.getTotalNum().multiply(MINUS_ONE); + //插入订单 + String publishId = insertOrder(paramDTO, CctDataEnums.ORDER_TYPE_SELL.getStrVlue(), + CctDataEnums.PUBLISH_TYPE_MARKET.getStrVlue(), CctDataEnums.ORDER_STATUS_NEW.getStrVlue()); + //冻结余额 + walletService.handleBalance(paramDTO.getUserId(), publishId, coin.getCoinName(), coin.getCoinNet(), deduct, paramDTO.getTotalNum()); + + return publishId; + } + + @Override + @Transactional + @TxTransaction(isStart = true) + public int handleCancelOrder(String orderId, String userId) { + //参数判空 + CCTExceptionPreconditionUtils.checkStringNotBlank(orderId, CctEnums.ORDER_ID_NULL); + CCTExceptionPreconditionUtils.checkStringNotBlank(userId, CctEnums.ORDER_USERID_NULL); + + //查询订单(排他锁) + PublishOrder order = publishOrderMapper.selectByIdForUpdate(orderId); + //判断订单是否为空 + CCTExceptionPreconditionUtils.checkNotNull(order, CctEnums.ORDER_NULL); + //操作用户不匹配 + if (!order.getUserId().equals(userId)) { + throw new CctException(CctEnums.ORDER_USERID_ERROR); + } + //订单状态不等于新建和已撮合 + if (!order.getOrderStatus().equals(CctDataEnums.ORDER_STATUS_NEW.getStrVlue()) && + !order.getOrderStatus().equals(CctDataEnums.ORDER_STATUS_MATCH.getStrVlue())) { + throw new CctException(CctEnums.ORDER_CANCEL_ERROR); + } + + //退回撤单余额 + checkCancelOrderType(order); + + //更新订单 + order.setOrderStatus(CctDataEnums.ORDER_STATUS_CANCEL.getStrVlue()); + order.setModifyTime(new Date()); + int row = publishOrderMapper.updateByPrimaryKeySelective(order); + if (row > 0) { + Double price = Double.parseDouble(order.getUnitPrice().toString()); + //更新redis缓存中的订单数据 + updateRedisOrderList(price, order.getLastNum(), order.getCoinName(), order.getUnitName(), order.getOrderType()); + return row; + } else { + return row; + } + } + + @Override + @Transactional + @TxTransaction(isStart = true) + public int handleCancelOrder(String orderId) { + //查询订单(排他锁) + PublishOrder order = publishOrderMapper.selectByIdForUpdate(orderId); + //判断订单是否为空 + if (order == null) { + return 0; + } + //判断订单状态是否是已撮合 + if (!order.getOrderStatus().equals(CctDataEnums.ORDER_STATUS_MATCH.getStrVlue())) { + return 0; + } + //退回撤单余额 + checkCancelOrderType(order); + + //更新订单 + order.setOrderStatus(CctDataEnums.ORDER_STATUS_CANCEL.getStrVlue()); + order.setModifyTime(new Date()); + return publishOrderMapper.updateByPrimaryKeySelective(order); + } + + @Override + @Transactional + @TxTransaction + public int handleCancelOrderToBot(String orderId, String userId) { + //查询订单(排他锁) + PublishOrder order = publishOrderMapper.selectByIdForUpdate(orderId); + //判断订单是否为空 + CCTExceptionPreconditionUtils.checkNotNull(order, CctEnums.ORDER_NULL); + //操作用户不匹配 + if (!order.getUserId().equals(userId)) { + return 0; + } + //订单状态不等于新建和已撮合 + if (!order.getOrderStatus().equals(CctDataEnums.ORDER_STATUS_NEW.getStrVlue()) && + !order.getOrderStatus().equals(CctDataEnums.ORDER_STATUS_MATCH.getStrVlue())) { + return 0; + } + //退回撤单余额 + checkCancelOrderType(order); + + //更新订单 + order.setOrderStatus(CctDataEnums.ORDER_STATUS_CANCEL.getStrVlue()); + order.setModifyTime(new Date()); + int row = publishOrderMapper.updateByPrimaryKeySelective(order); + if (row > 0) { + Double price = Double.parseDouble(order.getUnitPrice().toString()); + //更新redis缓存中的订单数据 + updateRedisOrderList(price, order.getLastNum(), order.getCoinName(), order.getUnitName(), order.getOrderType()); + return row; + } else { + return row; + } + } + + @Override + @Transactional + public int updateByPrimaryKeySelective(PublishOrder order) { + return publishOrderMapper.updateByPrimaryKeySelective(order); + } + + @Override + @Transactional + public int updateStatusInVersionLock(String orderId, String beforeStatus, String laterStatus) { + return publishOrderMapper.updateStatusInVersionLock(orderId, beforeStatus, laterStatus, new Date()); + } + + @Override + public PublishOrder selectById(String orderId) { + return publishOrderMapper.selectById(orderId); + } + + @Override + public PublishOrder selectByIdForUpdate(String orderId) { + return publishOrderMapper.selectByIdForUpdate(orderId); + } + + @Override + public List listOrder(String coinName, String unitName, String orderType, String orderStatus, String publishType, String sort) { + //redis是否存在对应的key + boolean flag = orderCache.hasKey(coinName, unitName, orderType); + //redis存在key + if (flag) { + //返回数据结构 + List resultOrders = new ArrayList<>(); + //升序 + if (sort.equals(CctDataEnums.SORT_ASC.getStrVlue())) { + //range升序获取Redis的sortSet数据 + Set redisOrders = orderCache.getZSetASC(coinName, unitName, orderType, 0, -1); + //用迭代器封装返回数据 + Iterator iterator = redisOrders.iterator(); + while (iterator.hasNext()) { + resultOrders.add(iterator.next()); + } + } + //倒序 + if (sort.equals(CctDataEnums.SORT_DESC.getStrVlue())) { + //reverseRange降序获取Redis的sortSet数据 + Set redisOrders = orderCache.getZSetDESC(coinName, unitName, orderType, 0, -1); + //用迭代器封装返回数据 + Iterator iterator = redisOrders.iterator(); + while (iterator.hasNext()) { + resultOrders.add(iterator.next()); + } + } + //返回数据 + return resultOrders; + } else { + //redis不存在key + //返回空数据 + return new ArrayList<>(); + } + } + + @Override + public List listOrderByCoinAndUnitAndStatus(String coinName, String unitName, String orderStatus, String orderType, String sort) { + return publishOrderMapper.listOrderByCoinAndUnitAndStatus(coinName, unitName, orderStatus, + orderType, CctDataEnums.PUBLISH_TYPE_LIMIT.getStrVlue(), sort); + } + + @Override + @Transactional(isolation = Isolation.READ_UNCOMMITTED) + public List listUserOrder(String userId, String coinName, String unitName, String orderType, String publishType, String[] orderStatus) { + return publishOrderMapper.listUserOrder(userId, coinName, unitName, orderType, publishType, orderStatus); + } + + /*** + * 判断撤销订单类型,并解冻余额 + */ + private void checkCancelOrderType(PublishOrder order) { + //查询币种信息 + Coin coin = coinService.selectCoin(order.getCoinName(), order.getUnitName(), CctDataEnums.COMMON_STATUS_YES.getStrVlue()); + + BigDecimal amount = BigDecimal.ZERO; //退回的可用余额 + BigDecimal decut = BigDecimal.ZERO; //解冻的冻结余额 + String coinName = ""; //退回的币种钱包 + String coinNet = ""; //退回的币种主网标识 + + //买单退回剩余交易额,单位是二级货币 + if (order.getOrderType().equals(CctDataEnums.ORDER_TYPE_BUY.getStrVlue())) { + amount = order.getLastTurnover(); + decut = amount.multiply(MINUS_ONE); + coinName = order.getUnitName(); + coinNet = coin.getUnitNet(); + } + //卖单退回剩余数量,单位是基本货币 + if (order.getOrderType().equals(CctDataEnums.ORDER_TYPE_SELL.getStrVlue())) { + amount = order.getLastNum(); + decut = amount.multiply(MINUS_ONE); + coinName = order.getCoinName(); + coinNet = coin.getCoinNet(); + } + + //解冻余额 + walletService.handleBalance(order.getUserId(), order.getId(), coinName, coinNet, amount, decut); + } + + /*** + * 校验限价订单的发布数量 + * @param paramDTO + */ + private void checkLimitPublishNum(PublishOrderParamDTO paramDTO) { + //单价、数量判空 + CCTExceptionPreconditionUtils.checkNotNull(paramDTO.getTotalNum(), CctEnums.ORDER_NUM_NULL); + CCTExceptionPreconditionUtils.checkNotNull(paramDTO.getUnitPrice(), CctEnums.ORDER_PRICE_NULL); + //是否小于等于0 + CCTExceptionPreconditionUtils.checkBigDecimalLessThanOrEqualZero(paramDTO.getTotalNum(), CctEnums.ORDER_NUM_ERROR); + CCTExceptionPreconditionUtils.checkBigDecimalLessThanOrEqualZero(paramDTO.getUnitPrice(), CctEnums.ORDER_PRICE_ERROR); + } + + /*** + * 检查限价订单是否符合单价范围 + * @param price + * @param coinName + * @param unitName + */ + private void checkLimitPrice(BigDecimal price, String coinName, String unitName) { + //判断币种是否打开下单限制 + String publishLimitKey = CctDataEnums.CONFIG_PUBLISH_LIMIT.getStrVlue(); + String coinPublishLimitKey = MessageFormat.format(publishLimitKey, coinName, unitName); + Config config = configService.selectByKey(coinPublishLimitKey, CctDataEnums.COMMON_STATUS_YES.getStrVlue()); + //没打开限制,返回 + if (config == null) { + return; + } + + //查询限制的单价范围 + MatchConfigDTO matchConfigDTO = matchConfigService.getByCoinAndUnitBeforeToday(coinName, unitName); + + //判空 + if (matchConfigDTO != null) { + BigDecimal minPrice = matchConfigDTO.getMinPrice(); + BigDecimal maxPrice = matchConfigDTO.getMaxPrice(); + //最小单价判空 + if (minPrice != null) { + //下单单价未达到最低单价,抛出异常 + if (price.compareTo(minPrice) < 0) { + throw new CctException(CctEnums.ORDER_OVER_MIN_LIMIT); + } + } + //最大单价判空 + if (maxPrice != null) { + //下单单价超出最大单价,抛出异常 + if (price.compareTo(maxPrice) > 0) { + throw new CctException(CctEnums.ORDER_OVER_MAX_LIMIT); + } + } + } + } + + /*** + * 校验市价订单的发布数量 + * @param paramDTO + * @param orderType + */ + private void checkMarketPublishNum(PublishOrderParamDTO paramDTO, String orderType) { + //判断买卖类型 + if (orderType.equals(CctDataEnums.ORDER_TYPE_BUY.getStrVlue())) { + //判空 + if (paramDTO.getTurnover() == null) { + throw new CctException(CctEnums.ORDER_TURNOVER_NULL); + } + //小于0 + if (paramDTO.getTurnover().compareTo(BigDecimal.ZERO) <= 0) { + throw new CctException(CctEnums.ORDER_TURNOVER_ERROR); + } + } + if (orderType.equals(CctDataEnums.ORDER_TYPE_SELL.getStrVlue())) { + //判空 + if (paramDTO.getTotalNum() == null) { + throw new CctException(CctEnums.ORDER_NUM_NULL); + } + //小于0 + if (paramDTO.getTotalNum().compareTo(BigDecimal.ZERO) <= 0) { + throw new CctException(CctEnums.ORDER_NUM_ERROR); + } + } + } + + + /*** + * 校验订单的发布参数 + * @param paramDTO + */ + private void checkPublishStrParam(PublishOrderParamDTO paramDTO) { + //用户id、手机、密码、交易对 + CCTExceptionPreconditionUtils.checkStringNotBlank(paramDTO.getUserId(), CctEnums.ORDER_USERID_NULL); + CCTExceptionPreconditionUtils.checkStringNotBlank(paramDTO.getPass(), CctEnums.ORDER_PASS_NULL); + CCTExceptionPreconditionUtils.checkStringNotBlank(paramDTO.getCoinName(), CctEnums.ORDER_COINNAME_NULL); + CCTExceptionPreconditionUtils.checkStringNotBlank(paramDTO.getUnitName(), CctEnums.ORDER_UNITNAME_NULL); + } + + + /*** + * 判断小数长度是否超出限制 + * @param checkBigDecimal + * @param decimal + */ + private void checkPublishDecimal(BigDecimal checkBigDecimal, Integer decimal, CctEnums enums) { + //转为String类型 + String str = checkBigDecimal.toPlainString(); + + //判断小数位长度是否超出限制 + if ((str.length() - str.indexOf(".") - 1) > decimal) { + throw new CctException(enums); + } + } + + + /*** + * 插入订单 + * @param paramDTO + * @param orderType + * @param publishType + * @param orderStatus + * @return + */ + private String insertOrder(PublishOrderParamDTO paramDTO, String orderType, String publishType, String orderStatus) { + PublishOrder order = new PublishOrder(); + String uuid = UUID.randomUUID().toString(); + Date now = new Date(); + order.setId(uuid); + order.setUserId(paramDTO.getUserId()); + order.setUnitPrice(paramDTO.getUnitPrice()); + order.setTotalNum(paramDTO.getTotalNum()); + order.setLastNum(paramDTO.getTotalNum()); + order.setTotalTurnover(paramDTO.getTurnover()); + order.setLastTurnover(paramDTO.getTurnover()); + order.setCoinName(paramDTO.getCoinName()); + order.setUnitName(paramDTO.getUnitName()); + order.setOrderType(orderType); + order.setPublishType(publishType); + order.setOrderStatus(orderStatus); + order.setCreateTime(now); + order.setModifyTime(now); + order.setVersion(0); + publishOrderMapper.insertSelective(order); + return uuid; + } + + /*** + * 发布订单后将订单数据添加进redis中 + * @param paramDTO + * @param tradingType 交易方向/订单类型,买/卖 + */ + private void addRedisOrderList(PublishOrderParamDTO paramDTO, String tradingType) { + String coinName = paramDTO.getCoinName(); + String unitName = paramDTO.getUnitName(); + Double price = Double.parseDouble(paramDTO.getUnitPrice().toString()); + + //redis是否存在对应的key + Boolean flag = orderCache.hasKey(coinName, unitName, tradingType); + + //redis存在key + if (flag) { + //是否成功获取锁标识 + boolean lockFlag = false; + //加锁,进行添加数据 + while (!lockFlag) { + //循环,直到获取锁为止 + lockFlag = orderCache.tryFairLock(redissonClient, coinName, unitName, tradingType); + //拿到锁,处理数据 + if (lockFlag) { + //根据单价(sortSet排序分数)获取订单数据 + Set redisOrder = orderCache.getZSetByScore(coinName, unitName, tradingType, price); + //防止为空,无法更新数组中对应的对象! + if (redisOrder.size() > 0) { + //用迭代器更新数据 + Iterator iterator = redisOrder.iterator(); + //处理数据 + while (iterator.hasNext()) { + MarketDTO order = (MarketDTO) iterator.next(); + //增加总数量和剩余数量 + order.setTotalNum(order.getTotalNum().add(paramDTO.getTotalNum())); + order.setTotalLastNum(order.getTotalLastNum().add(paramDTO.getTotalNum())); + //删除旧数据 + orderCache.removeZSetByScore(coinName, unitName, tradingType, price); + //添加数据 + orderCache.setZSetValue(coinName, unitName, tradingType, order, price); + } + } else { + //为空时,直接添加新的单价对象 + addRedisOrder(paramDTO, tradingType); + } + + //释放锁 + orderCache.unFairLock(redissonClient, coinName, unitName, tradingType); + } + } + } else { + //redis不存在key + addRedisOrder(paramDTO, tradingType); + } + } + + /*** + * 添加一个盘口数据对象到Redis中 + * @param paramDTO + * @param tradingType + */ + private void addRedisOrder(PublishOrderParamDTO paramDTO, String tradingType) { + //构建数据对象 + MarketDTO order = new MarketDTO(); + order.setUnitPrice(paramDTO.getUnitPrice()); + order.setTotalNum(paramDTO.getTotalNum()); + order.setTotalLastNum(paramDTO.getTotalNum()); + order.setCoinName(paramDTO.getCoinName()); + order.setUnitName(paramDTO.getUnitName()); + order.setTradingType(tradingType); + //添加进缓存中 + orderCache.setZSetValue(paramDTO.getCoinName(), paramDTO.getUnitName(), + tradingType, order, Double.valueOf(paramDTO.getUnitPrice().toString())); + } + + /** + * 撤单后将订单数据更新到redis中 + * + * @param price + * @param updateNum + * @param coinName + * @param unitName + * @param tradingType + */ + private void updateRedisOrderList(Double price, BigDecimal updateNum, String coinName, String unitName, String tradingType) { + //redis是否存在对应的key + boolean flag = orderCache.hasKey(coinName, unitName, tradingType); + //redis存在key + if (flag) { + //是否成功获取锁标识 + boolean lockFlag = false; + //加锁,进行添加数据 + while (!lockFlag) { + //循环,直到获取锁为止 + lockFlag = orderCache.tryFairLock(redissonClient, coinName, unitName, tradingType); + //拿到锁,处理数据 + if (lockFlag) { + //根据单价(sortSet排序分数)获取订单数据 + Set redisOrder = orderCache.getZSetByScore(coinName, unitName, tradingType, price); + //用迭代器更新数据 + Iterator iterator = redisOrder.iterator(); + //处理数据 + while (iterator.hasNext()) { + MarketDTO order = (MarketDTO) iterator.next(); + //更新剩余数量 + order.setTotalLastNum(order.getTotalLastNum().subtract(updateNum)); + //删除旧数据 + orderCache.removeZSetByScore(coinName, unitName, tradingType, price); + //如果剩余数量大于0,添加数据 + if (order.getTotalLastNum().compareTo(BigDecimal.ZERO) > 0) { + //覆盖数据 + orderCache.setZSetValue(coinName, unitName, tradingType, order, price); + } + } + //释放锁 + orderCache.unFairLock(redissonClient, coinName, unitName, tradingType); + } + } + } + } + +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/TradingDetailServiceImpl.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/TradingDetailServiceImpl.java new file mode 100644 index 0000000..d94d118 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/TradingDetailServiceImpl.java @@ -0,0 +1,35 @@ +package com.blockchain.server.cct.service.impl; + +import com.blockchain.server.cct.dto.trading.DetailByOrderIdDTO; +import com.blockchain.server.cct.dto.trading.ListUserDetailDTO; +import com.blockchain.server.cct.entity.TradingDetail; +import com.blockchain.server.cct.mapper.TradingDetailMapper; +import com.blockchain.server.cct.service.TradingDetailService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +public class TradingDetailServiceImpl implements TradingDetailService { + + @Autowired + private TradingDetailMapper tradingDetailMapper; + + @Override + @Transactional + public int insertTradingDetail(TradingDetail detail) { + return tradingDetailMapper.insertSelective(detail); + } + + @Override + public List listUserDetail(String userId, String coinName, String unitName, String beginTime, String lastTime, String status) { + return tradingDetailMapper.listUserDetail(userId, coinName, unitName, beginTime, lastTime, status); + } + + @Override + public List listDetailByOrderId(String orderId) { + return tradingDetailMapper.listDetailByOrderId(orderId); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/TradingRecordServiceImpl.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/TradingRecordServiceImpl.java new file mode 100644 index 0000000..4ddc200 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/TradingRecordServiceImpl.java @@ -0,0 +1,33 @@ +package com.blockchain.server.cct.service.impl; + +import com.blockchain.server.cct.entity.TradingRecord; +import com.blockchain.server.cct.mapper.TradingRecordMapper; +import com.blockchain.server.cct.service.TradingRecordService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +public class TradingRecordServiceImpl implements TradingRecordService { + + @Autowired + private TradingRecordMapper tradingRecordMapper; + + @Override + @Transactional + public int insertTradingRecord(TradingRecord record) { + return tradingRecordMapper.insertSelective(record); + } + + @Override + public List listRecordByCoinAndUnit(String coinName, String unitName) { + return tradingRecordMapper.listRecordByCoinAndUnit(coinName, unitName); + } + + @Override + public TradingRecord getRecordByCoinAndUnitLimitOne(String coinName, String unitName) { + return tradingRecordMapper.selectByCoinAndUnitLimitOne(coinName, unitName); + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/WalletServiceImpl.java b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/WalletServiceImpl.java new file mode 100644 index 0000000..9e7e437 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/java/com/blockchain/server/cct/service/impl/WalletServiceImpl.java @@ -0,0 +1,101 @@ +package com.blockchain.server.cct.service.impl; + +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import com.blockchain.server.cct.common.enums.CctEnums; +import com.blockchain.server.cct.common.exception.CctException; +import com.blockchain.server.cct.feign.BTCFeign; +import com.blockchain.server.cct.feign.EOSFeign; +import com.blockchain.server.cct.feign.ETHFeign; +import com.blockchain.server.cct.service.WalletService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; + +@Service +public class WalletServiceImpl implements WalletService { + + private static final Logger LOG = LoggerFactory.getLogger(WalletServiceImpl.class); + + @Autowired + private BTCFeign btcFeign; + @Autowired + private EOSFeign eosFeign; + @Autowired + private ETHFeign ethFeign; + + //主网标识 + private static final String BTC_NET = "BTC"; + private static final String ETH_NET = "ETH"; + private static final String EOS_NET = "EOS"; + + //币币交易调用钱包Feign调用应用标识 + private static final String CCT_APP = "CCT"; + + @Override + @Transactional + public void handleBalance(String userId, String publishId, String tokenName, String coinNet, BigDecimal freeBalance, BigDecimal freezeBalance) { + WalletOrderDTO order = new WalletOrderDTO(); + order.setUserId(userId); + order.setRecordId(publishId); + order.setTokenName(tokenName); + order.setWalletType(CCT_APP); + order.setFreeBalance(freeBalance); + order.setFreezeBalance(freezeBalance); + LOG.info("调用钱包Feign,入参: order :" + order.toString() + " coinNet :" + coinNet); + //根据主网标识,区分微服务调用 + switch (coinNet) { + case BTC_NET: + btcFeign.order(order); + break; + case ETH_NET: + ethFeign.order(order); + break; + case EOS_NET: + eosFeign.order(order); + break; + default: + LOG.error("更新余额失败,钱包处理出现未知主网标识!"); + throw new CctException(CctEnums.PUBLISH_ORDER_WALLET_ERROR); + } + } + + @Override + @Transactional + public void handleRealBalance(String userId, String recordId, String tokenName, String coinNet, BigDecimal freeBalance, BigDecimal freezeBalance, BigDecimal gasBalance) { + WalletChangeDTO change = new WalletChangeDTO(); + change.setUserId(userId); + change.setRecordId(recordId); + change.setTokenName(tokenName); + change.setFreeBalance(freeBalance); + change.setFreezeBalance(freezeBalance); + change.setGasBalance(gasBalance); + change.setWalletType(CCT_APP); + LOG.info("调用钱包Feign,入参: change :" + change.toString() + " coinNet :" + coinNet); + //根据主网标识,区分微服务调用 + switch (coinNet) { + case BTC_NET: + btcFeign.change(change); + break; + case ETH_NET: + ethFeign.change(change); + break; + case EOS_NET: + eosFeign.change(change); + break; + default: + LOG.error("扣款或加钱失败,钱包处理出现未知主网标识!"); + throw new CctException(CctEnums.PUBLISH_ORDER_WALLET_ERROR); + } + } + + @Override + public void isPassword(String pass) { + LOG.info("校验密码,参数:" + pass); +// ethFeign.isPassword(pass); TODO + } +} diff --git a/blockchain-server/blockchain-server-cct/src/main/resources/application.yml b/blockchain-server/blockchain-server-cct/src/main/resources/application.yml new file mode 100644 index 0000000..95aad49 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/resources/application.yml @@ -0,0 +1,3 @@ +#日志配置路径 +logging: + config: classpath:logback/logback-${spring.cloud.config.profile}.xml \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/resources/bootstrap.yml b/blockchain-server/blockchain-server-cct/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..56bba98 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/resources/bootstrap.yml @@ -0,0 +1,22 @@ +server: + port: 8501 +#注册中心 +eureka: + client: + service-url: + defaultZone: http://eureka:8001/eureka/ + instance: + prefer-ip-address: true + instance-id: ${spring.cloud.client.ip-address}:${server.port} + hostname: ${spring.cloud.client.ip-address} +spring: + cloud: + #配置中心 + config: + discovery: + service-id: dapp-config-server + enabled: true + profile: dev + name: springconf,springcloudconf,redisconf,dbconf,txconf,xssconf,ipconf + application: + name: dapp-cct-server diff --git a/blockchain-server/blockchain-server-cct/src/main/resources/logback/logback-dev.xml b/blockchain-server/blockchain-server-cct/src/main/resources/logback/logback-dev.xml new file mode 100644 index 0000000..9a0e324 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/resources/logback/logback-dev.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/resources/logback/logback-pro.xml b/blockchain-server/blockchain-server-cct/src/main/resources/logback/logback-pro.xml new file mode 100644 index 0000000..5b55059 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/resources/logback/logback-pro.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/resources/mapper/CoinMapper.xml b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/CoinMapper.xml new file mode 100644 index 0000000..d4efb53 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/CoinMapper.xml @@ -0,0 +1,53 @@ + + + + + app_cct_coin + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/resources/mapper/CommissionMapper.xml b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/CommissionMapper.xml new file mode 100644 index 0000000..17c4806 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/CommissionMapper.xml @@ -0,0 +1,49 @@ + + + + + app_cct_commission + + + + + + + + + + + + + + + UPDATE + + SET + amount = amount + #{amount}, + modify_time = #{modifyTime} + WHERE + user_id = #{userId} + AND + status = #{status} + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/resources/mapper/ConfigMapper.xml b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/ConfigMapper.xml new file mode 100644 index 0000000..ec3527e --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/ConfigMapper.xml @@ -0,0 +1,34 @@ + + + + + app_cct_config + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/resources/mapper/MatchConfigHandleLogMapper.xml b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/MatchConfigHandleLogMapper.xml new file mode 100644 index 0000000..8b636fe --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/MatchConfigHandleLogMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/resources/mapper/MatchConfigMapper.xml b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/MatchConfigMapper.xml new file mode 100644 index 0000000..a98cedb --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/MatchConfigMapper.xml @@ -0,0 +1,30 @@ + + + + + app_cct_match_config + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/resources/mapper/PublishOrderMapper.xml b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/PublishOrderMapper.xml new file mode 100644 index 0000000..2169958 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/PublishOrderMapper.xml @@ -0,0 +1,170 @@ + + + + + app_cct_publish_order + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UPDATE + + SET order_status = #{laterStatus}, + modify_time = #{time} + WHERE id = #{orderId} + AND order_status = #{beforeStatus} + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/resources/mapper/TradingDetailMapper.xml b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/TradingDetailMapper.xml new file mode 100644 index 0000000..851a23c --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/TradingDetailMapper.xml @@ -0,0 +1,112 @@ + + + + + app_cct_trading_detail + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-cct/src/main/resources/mapper/TradingRecordMapper.xml b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/TradingRecordMapper.xml new file mode 100644 index 0000000..800aa59 --- /dev/null +++ b/blockchain-server/blockchain-server-cct/src/main/resources/mapper/TradingRecordMapper.xml @@ -0,0 +1,40 @@ + + + + + app_cct_trading_record + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/pom.xml b/blockchain-server/blockchain-server-currency/pom.xml new file mode 100644 index 0000000..4d45139 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/pom.xml @@ -0,0 +1,39 @@ + + + + blockchain-server + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + blockchain-server-currency + 1.0-SNAPSHOT + + + com.blockchain + blockchain-server-base + 1.0-SNAPSHOT + + + redis.clients + jedis + + + org.redisson + redisson + 3.10.2 + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/CurrencyApplication.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/CurrencyApplication.java new file mode 100644 index 0000000..250ac49 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/CurrencyApplication.java @@ -0,0 +1,19 @@ +package com.blockchain.server.currency; + +import com.blockchain.server.base.BaseConf; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * \*

Desciption:

+ * \* CreateTime: 2019/2/20 17:35 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +@SpringBootApplication(scanBasePackageClasses = {BaseConf.class, CurrencyApplication.class}) +public class CurrencyApplication { + public static void main(String[] args) { + SpringApplication.run(CurrencyApplication.class,args); + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/Scheduling/CurrencyMarketScheduling.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/Scheduling/CurrencyMarketScheduling.java new file mode 100644 index 0000000..997bc55 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/Scheduling/CurrencyMarketScheduling.java @@ -0,0 +1,92 @@ +package com.blockchain.server.currency.Scheduling; + +import com.alibaba.fastjson.JSONObject; +import com.blockchain.server.currency.common.constant.BaseCoinEnums; +import com.blockchain.server.currency.common.constant.RatesEnums; +import com.blockchain.server.currency.redis.MarketLegalCache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Configurable; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.util.List; +import java.util.Map; + +@Component +@Configurable +@EnableScheduling +public class CurrencyMarketScheduling { + + @Autowired + private MarketLegalCache legalCache; + + @Autowired + private RestTemplate restTemplate; + + @Value("${market.coinbase.USD}") + private String usdMarketUrl; + + @Value("${market.kraken.url}") + private String krakenUrl; + + @Scheduled(cron = "*/5 * * * * ?") + public void getCurrencyMarket() { + try { + setLegalMarket(); + } catch (Exception e) { +// e.printStackTrace(); + } + } + + private void setLegalMarket() { + Map rates = getCoinbaseMarket(); + //USD + double usdcny = new Double(rates.get(RatesEnums.CNY.getValue()).toString()); + double usdhkd = new Double(rates.get(RatesEnums.HKD.getValue()).toString()); + double usdeur = new Double(rates.get(RatesEnums.EUR.getValue()).toString()); + + //BTC + setLegalMarket(BaseCoinEnums.BTC.getValue(), usdcny, usdhkd, usdeur, 1d / new Double(rates.get(BaseCoinEnums.BTC.getValue()).toString())); + //ETH + setLegalMarket(BaseCoinEnums.ETH.getValue(), usdcny, usdhkd, usdeur, 1d / new Double(rates.get(BaseCoinEnums.ETH.getValue()).toString())); + //USDT + setLegalMarket(BaseCoinEnums.USDT.getValue(), usdcny, usdhkd, usdeur, 1d); + //EOS + setLegalMarket(BaseCoinEnums.EOS.getValue(), usdcny, usdhkd, usdeur, getKrakenMarket("EOSUSD")); + } + + private void setLegalMarket(String coin, double usdcny, double usdhkd, double usdeur, double coinusd) { + legalCache.setMarketCache(coin + RatesEnums.USD.getValue(), coinusd); + legalCache.setMarketCache(coin + RatesEnums.CNY.getValue(), coinusd * usdcny); + legalCache.setMarketCache(coin + RatesEnums.HKD.getValue(), coinusd * usdhkd); + legalCache.setMarketCache(coin + RatesEnums.EUR.getValue(), coinusd * usdeur); + } + + /** + * coinbase上的美元行情 + * + * @return + */ + private Map getCoinbaseMarket() { + JSONObject result = restTemplate.getForEntity(usdMarketUrl, JSONObject.class).getBody(); + Map dataMap = (Map) result.get("data"); + return (Map) dataMap.get("rates"); + } + + /** + * 获取kraken上的行情 + * + * @param currencyPair :行情币对 + * @return + */ + private double getKrakenMarket(String currencyPair) { + JSONObject result = restTemplate.getForEntity(krakenUrl + currencyPair, JSONObject.class).getBody(); + Map resultMap = (Map) result.get("result"); + List> trades = (List>) resultMap.get(currencyPair); + return new Double(trades.get(trades.size() - 1).get(0).toString()); + } + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/Scheduling/HuobiMarketScheduling.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/Scheduling/HuobiMarketScheduling.java new file mode 100644 index 0000000..90fc534 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/Scheduling/HuobiMarketScheduling.java @@ -0,0 +1,89 @@ +package com.blockchain.server.currency.Scheduling; + +import com.alibaba.fastjson.JSONObject; +import com.blockchain.common.base.util.HttpUtilManager; +import com.blockchain.server.currency.dto.CurrencyMarketDTO; +import com.blockchain.server.currency.service.CurrencyMarketService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Configurable; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +//@Component +//@Configurable +//@EnableScheduling +public class HuobiMarketScheduling { + + @Autowired + private CurrencyMarketService currencyMarketService; + + @Value("${market.huobi.url}") + private String huobiUrl; + + @Value("${market.huobi.currencys}") + private String huobiCurrencys; + + private static ExecutorService cachedThreadPool = Executors.newSingleThreadExecutor(); + + @Scheduled(cron = "*/1 * * * * ?") + public void getCurrencyMarket() { + cachedThreadPool.execute(new Runnable() { + @Override + public void run() { + setHuobiMarket(); + } + }); + } + + private void setHuobiMarket() { + List currencys = JSONObject.parseArray(huobiCurrencys,List.class); + for (List data : currencys){ + saveMarket(getHuobiMarket(huobiUrl + data.get(0).toString()), + data.get(1).toString(),data.get(2).toString()); + } + } + + /** + * + * @param list + * @param coinName + * @param unitName + */ + private void saveMarket(List list, String coinName, String unitName) { + CurrencyMarketDTO now = currencyMarketService.get(coinName + "-" + unitName); + for (JSONObject obj : list) { + Long timestamp = Long.parseLong(obj.get("ts").toString()); + if(timestamp >= now.getTimestamp()) { + List data = (List)obj.get("data"); + currencyMarketService.save(coinName, unitName, + new BigDecimal(data.get(0).get("price").toString()), + new BigDecimal(data.get(0).get("amount").toString()), + timestamp,data.get(0).get("direction").toString().toUpperCase()); + } else { + break; + } + } + } + + + /** + * coinbase上的美元行情 + * + * @return + */ + private List getHuobiMarket(String url) { + HttpUtilManager manager = HttpUtilManager.getInstance(); + String result = manager.requestHttpGet(url); + JSONObject jsonObject = JSONObject.parseObject(result); + return (List) jsonObject.get("data"); + } + + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/conf/InterceptorConf.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/conf/InterceptorConf.java new file mode 100644 index 0000000..cc29e73 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/conf/InterceptorConf.java @@ -0,0 +1,13 @@ +package com.blockchain.server.currency.common.conf; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class InterceptorConf implements WebMvcConfigurer { + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**").allowedOrigins("*"); + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/BaseCoinEnums.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/BaseCoinEnums.java new file mode 100644 index 0000000..3cb7555 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/BaseCoinEnums.java @@ -0,0 +1,26 @@ +package com.blockchain.server.currency.common.constant; + +/** + * \*

Desciption:主币

+ * \* CreateTime: 2019/3/21 20:00 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +public enum BaseCoinEnums { + BTC("BTC"), + ETH("ETH"), + EOS("EOS"), + USDT("USDT") + ; + + private String value; + + BaseCoinEnums(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/BaseCurrencyEnums.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/BaseCurrencyEnums.java new file mode 100644 index 0000000..5417a4a --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/BaseCurrencyEnums.java @@ -0,0 +1,25 @@ +package com.blockchain.server.currency.common.constant; + +/** + * \*

Desciption:

+ * \* CreateTime: 2019/2/25 10:50 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +public enum BaseCurrencyEnums { + BTC("BTC"), + ETH("ETH"), + EOS("EOS"), + ; + + private String value; + + BaseCurrencyEnums(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/CommonConstants.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/CommonConstants.java new file mode 100644 index 0000000..fbd556a --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/CommonConstants.java @@ -0,0 +1,26 @@ +package com.blockchain.server.currency.common.constant; + +/** + * \*

Desciption:

+ * \* CreateTime: 2019/2/21 9:36 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +public class CommonConstants { + /** + * 行情数据长度 + */ + public final static int MARKET_SIZE = 1000; + + /** + * 涨跌榜长度 + */ + public final static int TOP_SIZE = 10; + + /** + * 历史成交单长度 + */ + public final static int HISTORY_SIZE = 25; + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/DatatablesEnums.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/DatatablesEnums.java new file mode 100644 index 0000000..fe50227 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/DatatablesEnums.java @@ -0,0 +1,30 @@ +package com.blockchain.server.currency.common.constant; + +/** + * \*

Desciption:

+ * \* CreateTime: 2019/2/22 9:38 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +public enum DatatablesEnums { + USABLE_STATUS(1,"可用"), + UNUSABLE_STATUS(0,"不可用") + ; + + private Object value; + private String remark; + + DatatablesEnums(Object value, String remark) { + this.value = value; + this.remark = remark; + } + + public Object getValue() { + return value; + } + + public String getRemark() { + return remark; + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/MarketKEnums.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/MarketKEnums.java new file mode 100644 index 0000000..2bb245b --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/MarketKEnums.java @@ -0,0 +1,32 @@ +package com.blockchain.server.currency.common.constant; + +/** + * \*

Desciption:

+ * \* CreateTime: 2019/2/25 10:50 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +public enum MarketKEnums { + MINUTE("MINUTE", 60000), + HOUR("HOUR", 3600000), + DAY("DAY", 86400000), + WEEK("WEEK", 604800000), + MONTH("MONTH", 0); + + private String value; + private int second; + + MarketKEnums(String value, int second) { + this.value = value; + this.second = second; + } + + public String getValue() { + return value; + } + + public int getSecond() { + return second; + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/MarketKTypeEnums.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/MarketKTypeEnums.java new file mode 100644 index 0000000..b1ef3d2 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/MarketKTypeEnums.java @@ -0,0 +1,37 @@ +package com.blockchain.server.currency.common.constant; + +/** + * \*

Desciption:

+ * \* CreateTime: 2019/2/25 10:50 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +public enum MarketKTypeEnums { + ONEMINUTE("MINUTE", 1), + FIVEMINUTE("MINUTE", 5), + FIFMINUTE("MINUTE", 15), + THRMINUTE("MINUTE", 30), + ONEHOUR("HOUR", 1), + fourHOUR("HOUR", 4), + TWHOUR("HOUR", 12), + ONEDAY("DAY", 1), + ONEWEEK("WEEK", 1); + + private String timeType; + private int timeNumber; + + MarketKTypeEnums(String timeType, int timeNumber) { + this.timeType = timeType; + this.timeNumber = timeNumber; + } + + public String getTimeType() { + return timeType; + } + + public int getTimeNumber() { + return timeNumber; + } + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/RatesEnums.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/RatesEnums.java new file mode 100644 index 0000000..689172d --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/RatesEnums.java @@ -0,0 +1,26 @@ +package com.blockchain.server.currency.common.constant; + +/** + * \*

Desciption:汇率枚举

+ * \* CreateTime: 2019/3/21 20:00 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +public enum RatesEnums { + CNY("CNY"), + USD("USD"), + HKD("HKD"), + EUR("EUR") + ; + + private String value; + + RatesEnums(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/TradingTypeEnums.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/TradingTypeEnums.java new file mode 100644 index 0000000..90e7977 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/constant/TradingTypeEnums.java @@ -0,0 +1,24 @@ +package com.blockchain.server.currency.common.constant; + +/** + * \*

Desciption:汇率枚举

+ * \* CreateTime: 2019/3/21 20:00 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +public enum TradingTypeEnums { + BUY("BUY"), + SELL("SELL"), + ; + + private String value; + + TradingTypeEnums(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/enums/CurrencyMarketResultEnums.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/enums/CurrencyMarketResultEnums.java new file mode 100644 index 0000000..5e7ee19 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/enums/CurrencyMarketResultEnums.java @@ -0,0 +1,34 @@ +package com.blockchain.server.currency.common.enums; + +public enum CurrencyMarketResultEnums { + CURRENCY_PAIR_NULL(4001,"币对不能为空","Currency pair can not be null","幣懟不能為空"), + CURRENCY_PAIR_ERROR(4002,"币对不存在","Currency pair is not find","幣懟不存在"), + CURRENCY_PAIR_UNUSABLE(4002,"币对不可用","Currency pair is usabled","幣懟不可用"); + private int code; + private String cnmsg; + private String enMsg; + private String hkmsg; + + CurrencyMarketResultEnums(int code, String cnmsg, String enMsg, String hkmsg) { + this.code = code; + this.cnmsg = cnmsg; + this.enMsg = enMsg; + this.hkmsg = hkmsg; + } + + public int getCode() { + return code; + } + + public String getCnmsg() { + return cnmsg; + } + + public String getEnMsg() { + return enMsg; + } + + public String getHkmsg() { + return hkmsg; + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/exception/CurrencyMarketExeption.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/exception/CurrencyMarketExeption.java new file mode 100644 index 0000000..180f4c8 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/common/exception/CurrencyMarketExeption.java @@ -0,0 +1,39 @@ +package com.blockchain.server.currency.common.exception; + + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.currency.common.enums.CurrencyMarketResultEnums; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +public class CurrencyMarketExeption extends BaseException { + public CurrencyMarketExeption(CurrencyMarketResultEnums rs) { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + //可能是定时器调用,避免获取request空指针 + if (servletRequestAttributes == null) { + this.code = rs.getCode(); + this.msg = rs.getHkmsg(); + } else { + HttpServletRequest request = servletRequestAttributes.getRequest(); + String userLocale = HttpRequestUtil.getUserLocale(request); + String msg = ""; + switch (userLocale) { + case BaseConstant.USER_LOCALE_EN_US: + msg = rs.getEnMsg(); + break; + case BaseConstant.USER_LOCALE_ZH_CN: + msg = rs.getCnmsg(); + break; + default: + msg = rs.getHkmsg(); + break; + } + this.code = rs.getCode(); + this.msg = msg; + } + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/CurrencyController.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/CurrencyController.java new file mode 100644 index 0000000..10895a2 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/CurrencyController.java @@ -0,0 +1,55 @@ +package com.blockchain.server.currency.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.currency.controller.api.CurrencyApi; +import com.blockchain.server.currency.service.CurrencyPairService; +import com.blockchain.server.currency.service.CurrencyService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +@Api(CurrencyApi.MARKET_CONTROLLER_API) +@RestController +@RequestMapping("/currency") +public class CurrencyController { + + @Autowired + private CurrencyPairService currencyPairService; + + @Autowired + private CurrencyService currencyService; + + @ApiOperation(value = CurrencyApi.GetUsableList.METHOD_API_NAME, + notes = CurrencyApi.GetUsableList.METHOD_API_NOTE) + @RequestMapping(value = "/getUsableList", method = RequestMethod.GET) + public ResultDTO getUsableList() { + + return ResultDTO.requstSuccess(currencyPairService.getUsableList()); + } + + @ApiOperation(value = CurrencyApi.GetCurrencyInfo.METHOD_API_NAME, + notes = CurrencyApi.GetCurrencyInfo.METHOD_API_NOTE) + @RequestMapping(value = "/getCurrencyInfo", method = RequestMethod.GET) + public ResultDTO getCurrencyInfo( + HttpServletRequest request, + @ApiParam(CurrencyApi.GetCurrencyInfo.METHOD_API_CURRENCY_NAME) String currencyName) { + + return ResultDTO.requstSuccess(currencyService.getCurrencyInfo(currencyName, HttpRequestUtil.getUserLocale(request))); + } + + @ApiOperation(value = CurrencyApi.GetQuoteCurrency.METHOD_API_NAME, + notes = CurrencyApi.GetQuoteCurrency.METHOD_API_NOTE) + @RequestMapping(value = "/getQuoteCurrency", method = RequestMethod.GET) + public ResultDTO getQuoteCurrency() { + + return ResultDTO.requstSuccess(currencyService.getQuoteCurrency()); + } + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/CurrencyMarketController.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/CurrencyMarketController.java new file mode 100644 index 0000000..858b876 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/CurrencyMarketController.java @@ -0,0 +1,124 @@ +package com.blockchain.server.currency.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.currency.controller.api.CurrencyMarketApi; +import com.blockchain.server.currency.service.CurrencyMarketService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Api(CurrencyMarketApi.MARKET_CONTROLLER_API) +@RestController +@RequestMapping("/market") +public class CurrencyMarketController { + + @Autowired + private CurrencyMarketService currencyMarketService; + + @ApiOperation(value = CurrencyMarketApi.Get.METHOD_API_NAME, + notes = CurrencyMarketApi.Get.METHOD_API_NOTE) + @RequestMapping(value = "/get", method = RequestMethod.GET) + public ResultDTO get( + @ApiParam(CurrencyMarketApi.Get.METHOD_API_CURRENCY_PAIR) String currencyPair) { + + return ResultDTO.requstSuccess(currencyMarketService.get(currencyPair)); + } + + @ApiOperation(value = CurrencyMarketApi.GetLast.METHOD_API_NAME, + notes = CurrencyMarketApi.GetLast.METHOD_API_NOTE) + @RequestMapping(value = "/getLast", method = RequestMethod.GET) + public ResultDTO getLast( + @ApiParam(CurrencyMarketApi.GetLast.METHOD_API_CURRENCY_PAIR) String currencyPair) { + + return ResultDTO.requstSuccess(currencyMarketService.getLast(currencyPair)); + } + + @ApiOperation(value = CurrencyMarketApi.GetRates.METHOD_API_NAME, + notes = CurrencyMarketApi.GetRates.METHOD_API_NOTE) + @RequestMapping(value = "/getRates", method = RequestMethod.GET) + public ResultDTO getRates(@ApiParam(CurrencyMarketApi.GetRates.METHOD_API_COIN) String coin) { + + return ResultDTO.requstSuccess(currencyMarketService.getRates(coin)); + } + + @ApiOperation(value = CurrencyMarketApi.GetList.METHOD_API_NAME, + notes = CurrencyMarketApi.GetList.METHOD_API_NOTE) + @RequestMapping(value = "/getList", method = RequestMethod.GET) + public ResultDTO getList() { + + return ResultDTO.requstSuccess(currencyMarketService.getList()); + } + + @ApiOperation(value = CurrencyMarketApi.GetHomeList.METHOD_API_NAME, + notes = CurrencyMarketApi.GetHomeList.METHOD_API_NOTE) + @RequestMapping(value = "/getHomeList", method = RequestMethod.GET) + public ResultDTO getHomeList() { + + return ResultDTO.requstSuccess(currencyMarketService.getHomeList()); + } + + @ApiOperation(value = CurrencyMarketApi.GetTopList.METHOD_API_NAME, + notes = CurrencyMarketApi.GetTopList.METHOD_API_NOTE) + @RequestMapping(value = "/getTopList", method = RequestMethod.GET) + public ResultDTO getTopList() { + + return ResultDTO.requstSuccess(currencyMarketService.getTopList()); + } + + @ApiOperation(value = CurrencyMarketApi.GetHistoryList.METHOD_API_NAME, + notes = CurrencyMarketApi.GetHistoryList.METHOD_API_NOTE) + @RequestMapping(value = "/getHistoryList", method = RequestMethod.GET) + public ResultDTO getHistoryList(@ApiParam(CurrencyMarketApi.GetHistoryList.METHOD_API_CURRENCY_PAIR) String currencyPair) { + + return ResultDTO.requstSuccess(currencyMarketService.getHistoryList(currencyPair)); + } + + @ApiOperation(value = CurrencyMarketApi.GetQuoteList.METHOD_API_NAME, + notes = CurrencyMarketApi.GetQuoteList.METHOD_API_NOTE) + @RequestMapping(value = "/getQuoteList", method = RequestMethod.GET) + public ResultDTO getQuoteList( + @ApiParam(CurrencyMarketApi.GetQuoteList.METHOD_API_CURRENCY_NAME) String currencyName) { + + return ResultDTO.requstSuccess(currencyMarketService.getQuoteList(currencyName)); + } + + @ApiOperation(value = CurrencyMarketApi.Query.METHOD_API_NAME, + notes = CurrencyMarketApi.Query.METHOD_API_NOTE) + @RequestMapping(value = "/query", method = RequestMethod.GET) + public ResultDTO query( + @ApiParam(CurrencyMarketApi.Query.METHOD_API_CURRENCY_PAIR) String currencyPair, + @RequestParam(defaultValue = "MINUTE") + @ApiParam(CurrencyMarketApi.Query.METHOD_API_TIME_TYPE) String timeType, + @RequestParam(defaultValue = "15") + @ApiParam(CurrencyMarketApi.Query.METHOD_API_TIME_NUMBER) Integer timeNumber, + @RequestParam(defaultValue = "0") + @ApiParam(CurrencyMarketApi.Query.METHOD_API_START) Integer start, + @RequestParam(defaultValue = "1000") + @ApiParam(CurrencyMarketApi.Query.METHOD_API_STOP) Integer stop) { + + return ResultDTO.requstSuccess(currencyMarketService.queryForK(currencyPair, timeType, timeNumber, start, stop)); + } + + @ApiOperation(value = CurrencyMarketApi.QuerySortDesc.METHOD_API_NAME, + notes = CurrencyMarketApi.QuerySortDesc.METHOD_API_NOTE) + @RequestMapping(value = "/querySortDesc", method = RequestMethod.GET) + public ResultDTO querySortDesc( + @ApiParam(CurrencyMarketApi.QuerySortDesc.METHOD_API_CURRENCY_PAIR) String currencyPair, + @RequestParam(defaultValue = "MINUTE") + @ApiParam(CurrencyMarketApi.QuerySortDesc.METHOD_API_TIME_TYPE) String timeType, + @RequestParam(defaultValue = "15") + @ApiParam(CurrencyMarketApi.QuerySortDesc.METHOD_API_TIME_NUMBER) Integer timeNumber, + @RequestParam(defaultValue = "0") + @ApiParam(CurrencyMarketApi.QuerySortDesc.METHOD_API_START) Integer start, + @RequestParam(defaultValue = "1000") + @ApiParam(CurrencyMarketApi.QuerySortDesc.METHOD_API_STOP) Integer stop) { + + return ResultDTO.requstSuccess(currencyMarketService.queryForKSortDESC(currencyPair, timeType, timeNumber, start, stop)); + } + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/api/CurrencyApi.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/api/CurrencyApi.java new file mode 100644 index 0000000..fd1eddf --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/api/CurrencyApi.java @@ -0,0 +1,27 @@ +package com.blockchain.server.currency.controller.api; + +public class CurrencyApi { + public static final String MARKET_CONTROLLER_API = "数字货币控制器"; + + public static class GetUsableList{ + public static final String METHOD_API_NAME = "获取可用数字货币对"; + public static final String METHOD_API_NOTE = "获取可用数字货币对"; + } + + public static class GetCurrencyInfo{ + public static final String METHOD_API_NAME = "获取数字货币详情"; + public static final String METHOD_API_NOTE = "获取数字货币详情"; + public static final String METHOD_API_CURRENCY_NAME = "货币名称"; + } + + public static class GetQuoteCurrency{ + public static final String METHOD_API_NAME = "主要数字货币"; + public static final String METHOD_API_NOTE = "获取主要数字货币"; + } + + public static class List{ + public static final String METHOD_API_NAME = "主要数字货币"; + public static final String METHOD_API_NOTE = "获取主要数字货币"; + } + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/api/CurrencyMarketApi.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/api/CurrencyMarketApi.java new file mode 100644 index 0000000..3226198 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/controller/api/CurrencyMarketApi.java @@ -0,0 +1,71 @@ +package com.blockchain.server.currency.controller.api; + +public class CurrencyMarketApi { + public static final String MARKET_CONTROLLER_API = "数字货币行情控制器"; + + public static class Get { + public static final String METHOD_API_NAME = "最新行情"; + public static final String METHOD_API_NOTE = "获取数字货币最新行情"; + public static final String METHOD_API_CURRENCY_PAIR = "数字货币对"; + } + + public static class GetLast { + public static final String METHOD_API_NAME = "获取今天之前的最新行情"; + public static final String METHOD_API_NOTE = "获取今天之前的最新行情"; + public static final String METHOD_API_CURRENCY_PAIR = "数字货币对"; + } + + public static class GetRates { + public static final String METHOD_API_NAME = "获取所有法币行情"; + public static final String METHOD_API_NOTE = "获取所有法币行情"; + public static final String METHOD_API_COIN = "法币"; + } + + public static class GetList { + public static final String METHOD_API_NAME = "最新行情列表"; + public static final String METHOD_API_NOTE = "获取数字货币最新行情列表"; + } + + public static class GetHomeList { + public static final String METHOD_API_NAME = "首页行情"; + public static final String METHOD_API_NOTE = "获取首页行情列表"; + } + + public static class GetTopList { + public static final String METHOD_API_NAME = "涨跌幅榜单"; + public static final String METHOD_API_NOTE = "获取涨跌幅榜单行情列表"; + } + + public static class GetHistoryList { + public static final String METHOD_API_NAME = "成交历史"; + public static final String METHOD_API_NOTE = "获取成交历史列表"; + public static final String METHOD_API_CURRENCY_PAIR = "数字货币对"; + } + + public static class GetQuoteList { + public static final String METHOD_API_NAME = "获取主要货币的币对"; + public static final String METHOD_API_NOTE = "根据主要货币获取主要货币的币对"; + public static final String METHOD_API_CURRENCY_NAME = "数字货"; + } + + public static class Query { + public static final String METHOD_API_NAME = "获取数字货币行情列表"; + public static final String METHOD_API_NOTE = "获取数字货币行情列表"; + public static final String METHOD_API_CURRENCY_PAIR = "数字货币对"; + public static final String METHOD_API_TIME_TYPE = "时间类型,MINUTE:分,HOUR:小时,DAY:天,MONTH:月,WEEK:周"; + public static final String METHOD_API_TIME_NUMBER = "时间数,如15分钟则为15"; + public static final String METHOD_API_START = "开始位置,默认0"; + public static final String METHOD_API_STOP = "结束位置,默认100,最大1000"; + } + + public static class QuerySortDesc { + public static final String METHOD_API_NAME = "获取数字货币行情列表-排序倒序"; + public static final String METHOD_API_NOTE = "获取数字货币行情列表-排序倒序"; + public static final String METHOD_API_CURRENCY_PAIR = "数字货币对"; + public static final String METHOD_API_TIME_TYPE = "时间类型,MINUTE:分,HOUR:小时,DAY:天,MONTH:月,WEEK:周"; + public static final String METHOD_API_TIME_NUMBER = "时间数,如15分钟则为15"; + public static final String METHOD_API_START = "开始位置,默认0"; + public static final String METHOD_API_STOP = "结束位置,默认100,最大1000"; + } + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyDTO.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyDTO.java new file mode 100644 index 0000000..a0b8074 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyDTO.java @@ -0,0 +1,28 @@ +package com.blockchain.server.currency.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * \*

Desciption:

+ * \* CreateTime: 2019/2/26 11:30 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CurrencyDTO { + private String currencyName; + private String currencyFullName; + private String issueTime; + private String totalSupply; + private String totalCirculation; + private String icoAmount; + private String whitePaper; + private String officialWebsite; + private String blockUrl; + private String descr; +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyMarketDTO.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyMarketDTO.java new file mode 100644 index 0000000..1784184 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyMarketDTO.java @@ -0,0 +1,33 @@ +package com.blockchain.server.currency.dto; + +import com.blockchain.common.base.dto.BaseDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CurrencyMarketDTO extends BaseDTO implements Comparable{ + + private String currencyPair; + private BigDecimal amount; + private Long timestamp; + private float percent; + private double usdAmount; + private double cnyAmount; + private double hkdAmount; + private double eurAmount; + private BigDecimal lowest;//最低 + private BigDecimal highest;//最高 + private BigDecimal open;//开盘 + private BigDecimal total;//成交量 + + @Override + public int compareTo(CurrencyMarketDTO o) { + return (int)(10000*o.percent - 10000*this.percent); + } +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyMarketHistoryDTO.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyMarketHistoryDTO.java new file mode 100644 index 0000000..0de1335 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyMarketHistoryDTO.java @@ -0,0 +1,21 @@ +package com.blockchain.server.currency.dto; + +import com.blockchain.common.base.dto.BaseDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CurrencyMarketHistoryDTO extends BaseDTO{ + private BigDecimal makerPrice; + private BigDecimal tradingNum; + private String coinName; + private String unitName; + private String createTime; + private String tradingType; +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyMarketKDTO.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyMarketKDTO.java new file mode 100644 index 0000000..51733f7 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyMarketKDTO.java @@ -0,0 +1,26 @@ +package com.blockchain.server.currency.dto; + +import com.blockchain.common.base.dto.BaseDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CurrencyMarketKDTO extends BaseDTO { + + private BigDecimal open;//开盘 + private BigDecimal close;//收盘 + private Long openTime;//开盘 + private Long closeTime;//收盘 + private BigDecimal lowest;//最低 + private BigDecimal highest;//最高 + private BigDecimal total;//成交量 + private String date; + private Long timestamp; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyPairDTO.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyPairDTO.java new file mode 100644 index 0000000..c703a3d --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/dto/CurrencyPairDTO.java @@ -0,0 +1,15 @@ +package com.blockchain.server.currency.dto; + +import com.blockchain.common.base.dto.BaseDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CurrencyPairDTO extends BaseDTO { + + private String currencyPair; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/inner/CurrencyMarketInnerController.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/inner/CurrencyMarketInnerController.java new file mode 100644 index 0000000..375ad8b --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/inner/CurrencyMarketInnerController.java @@ -0,0 +1,39 @@ +package com.blockchain.server.currency.inner; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.currency.inner.api.CurrencyMarketInnerApi; +import com.blockchain.server.currency.service.CurrencyMarketService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.math.BigDecimal; +import java.util.Date; + +@Api(CurrencyMarketInnerApi.MARKET_CONTROLLER_API) +@RestController +@RequestMapping("/inner/market") +public class CurrencyMarketInnerController { + + @Autowired + private CurrencyMarketService currencyMarketService; + + @ApiOperation(value = CurrencyMarketInnerApi.Save.METHOD_API_NAME, + notes = CurrencyMarketInnerApi.Save.METHOD_API_NOTE) + @RequestMapping(value = "/save", method = RequestMethod.POST) + public ResultDTO save( + @ApiParam(CurrencyMarketInnerApi.Save.METHOD_API_COIN_NAME) String coinName, + @ApiParam(CurrencyMarketInnerApi.Save.METHOD_API_UNIT_NAME) String unitName, + @ApiParam(CurrencyMarketInnerApi.Save.METHOD_API_AMOUNT) BigDecimal amount, + @ApiParam(CurrencyMarketInnerApi.Save.METHOD_API_TOTAL) BigDecimal total, + @ApiParam(CurrencyMarketInnerApi.Save.METHOD_API_TIMESTAMP) Long timestamp, + @ApiParam(CurrencyMarketInnerApi.Save.METHOD_API_TRADINGTYPE) String tradingType){ + currencyMarketService.save(coinName, unitName, amount, total, timestamp,tradingType); + return ResultDTO.requstSuccess(null); + } + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/inner/api/CurrencyMarketInnerApi.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/inner/api/CurrencyMarketInnerApi.java new file mode 100644 index 0000000..488a448 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/inner/api/CurrencyMarketInnerApi.java @@ -0,0 +1,17 @@ +package com.blockchain.server.currency.inner.api; + +public class CurrencyMarketInnerApi { + public static final String MARKET_CONTROLLER_API = "内部数字货币行情控制器"; + + public static class Save{ + public static final String METHOD_API_NAME = "保存数字货币最新行情"; + public static final String METHOD_API_NOTE = "保存数字货币最新行情"; + public static final String METHOD_API_COIN_NAME = "币种名称"; + public static final String METHOD_API_UNIT_NAME="币种单位"; + public static final String METHOD_API_AMOUNT = "成交价格"; + public static final String METHOD_API_TOTAL = "成交数量"; + public static final String METHOD_API_TIMESTAMP = "成交时间"; + public static final String METHOD_API_TRADINGTYPE = "成交类型"; + } + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/mapper/CurrencyMapper.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/mapper/CurrencyMapper.java new file mode 100644 index 0000000..d246a5e --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/mapper/CurrencyMapper.java @@ -0,0 +1,23 @@ +package com.blockchain.server.currency.mapper; + +import com.blockchain.server.currency.dto.CurrencyDTO; +import com.blockchain.server.currency.model.Currency; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +@Repository +public interface CurrencyMapper extends Mapper { + + /** + * 获取货币信息 + * @param currencyName :货币名称 + * @param lg :语种 + * @return + */ + CurrencyDTO getByCurrencyName(@Param("currencyName")String currencyName,@Param("lg")String lg); + + List getQuoteCurrency(); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/mapper/CurrencyMarketMapper.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/mapper/CurrencyMarketMapper.java new file mode 100644 index 0000000..d7eee6e --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/mapper/CurrencyMarketMapper.java @@ -0,0 +1,25 @@ +package com.blockchain.server.currency.mapper; + +import com.blockchain.server.currency.dto.CurrencyMarketDTO; +import com.blockchain.server.currency.dto.CurrencyMarketKDTO; +import com.blockchain.server.currency.model.CurrencyMarket; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +@Repository +public interface CurrencyMarketMapper extends Mapper { + /** + * K线数据查询 + * @param params + * @return + */ + List queryForK(Map params); + + + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/mapper/CurrencyPairMapper.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/mapper/CurrencyPairMapper.java new file mode 100644 index 0000000..4b56862 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/mapper/CurrencyPairMapper.java @@ -0,0 +1,20 @@ +package com.blockchain.server.currency.mapper; + +import com.blockchain.server.currency.dto.CurrencyPairDTO; +import com.blockchain.server.currency.model.CurrencyPair; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; +import java.util.Map; + +@Repository +public interface CurrencyPairMapper extends Mapper { + + List getUsableList(); + List getHomeList(); + List getQuoteList(@Param("currencyName")String currencyName); + + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/model/Currency.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/model/Currency.java new file mode 100644 index 0000000..00f3e1d --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/model/Currency.java @@ -0,0 +1,52 @@ +package com.blockchain.server.currency.model; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +@Table(name = "dapp_currency") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Currency extends BaseModel { + + @Id + @Column(name = "currency_name") + private String currencyName; + @Column(name = "currency_name_cn") + private String currencyNameCn; + @Column(name = "currency_name_en") + private String currencyNameEn; + @Column(name = "currency_name_hk") + private String currencyNameHk; + @Column(name = "issue_time") + private String issueTime; + @Column(name = "total_supply") + private String totalSupply; + @Column(name = "total_circulation") + private String totalCirculation; + @Column(name = "descr_cn") + private String descrCn; + @Column(name = "descr_en") + private String descrEn; + @Column(name = "descr_hk") + private String descrHk; + @Column(name = "ico_amount") + private String icoAmount; + @Column(name = "white_paper") + private String whitePaper; + @Column(name = "official_website") + private String officialWebsite; + @Column(name = "block_url") + private String blockUrl; + @Column(name = "status") + private String status; + @Column(name = "order_by") + private String orderBy; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/model/CurrencyMarket.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/model/CurrencyMarket.java new file mode 100644 index 0000000..b274b48 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/model/CurrencyMarket.java @@ -0,0 +1,30 @@ +package com.blockchain.server.currency.model; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.math.BigDecimal; + +@Table(name = "dapp_currency_market") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CurrencyMarket extends BaseModel { + + @Id + @GeneratedValue(strategy= GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + @Column(name = "currency_pair") + private String currencyPair; + @Column(name = "amount") + private BigDecimal amount; + @Column(name = "total") + private BigDecimal total; + @Column(name = "timestamp") + private Long timestamp; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/model/CurrencyPair.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/model/CurrencyPair.java new file mode 100644 index 0000000..856d940 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/model/CurrencyPair.java @@ -0,0 +1,30 @@ +package com.blockchain.server.currency.model; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +@Table(name = "dapp_currency_pair") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CurrencyPair extends BaseModel { + + @Id + @Column(name = "currency_pair") + private String currencyPair; + @Column(name = "status") + private Byte status; + @Column(name = "order_by") + private String orderBy; + @Column(name = "is_home") + private Byte isHome; + @Column(name = "is_cct") + private Byte isCct; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/HistoryCache.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/HistoryCache.java new file mode 100644 index 0000000..37fdc18 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/HistoryCache.java @@ -0,0 +1,38 @@ +package com.blockchain.server.currency.redis; + +import com.blockchain.server.currency.common.constant.CommonConstants; +import com.blockchain.server.currency.dto.CurrencyMarketHistoryDTO; +import com.blockchain.server.currency.dto.CurrencyPairDTO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * \*

Desciption:

+ * \* CreateTime: 2019/2/12 10:08 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +@Component +public class HistoryCache { + private static final String MARKET_LIST_HISTORY_CACHE = "market:list:history:"; + @Autowired + private RedisTemplate redisTemplate; + + public List push(String currencyPair, CurrencyMarketHistoryDTO dto){ + String key = MARKET_LIST_HISTORY_CACHE + currencyPair; + redisTemplate.opsForList().leftPush(key,dto); + if (redisTemplate.opsForList().size(key) > CommonConstants.HISTORY_SIZE){ + redisTemplate.opsForList().rightPop(key); + } + return redisTemplate.opsForList().range(key,0, CommonConstants.HISTORY_SIZE); + } + + public List getList(String currencyPair){ + String key = MARKET_LIST_HISTORY_CACHE + currencyPair; + return redisTemplate.opsForList().range(key,0, CommonConstants.HISTORY_SIZE); + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/MarketCache.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/MarketCache.java new file mode 100644 index 0000000..0db2b4d --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/MarketCache.java @@ -0,0 +1,53 @@ +package com.blockchain.server.currency.redis; + +import com.blockchain.server.currency.dto.CurrencyPairDTO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.SortedMap; +import java.util.concurrent.TimeUnit; + +/** + * \*

Desciption:

+ * \* CreateTime: 2019/2/12 10:08 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +@Component +public class MarketCache { + private static final String MARKET_LIST_LIST_CACHE = "market:list:list"; + private static final String MARKET_LIST_HOME_CACHE = "market:list:home"; + + @Autowired + private RedisTemplate redisTemplate; + + public void setHomeList( List dtoList){ + redisTemplate.opsForValue().set(MARKET_LIST_HOME_CACHE, dtoList, 365, TimeUnit.DAYS); + } + + public List getHomeList(){ + String key = MARKET_LIST_HOME_CACHE ; + if (redisTemplate.hasKey(key)) {//如果有token信息,更新token的有效时间 + List dtoList = (List) redisTemplate.opsForValue().get(key); + return dtoList; + } + return null; + } + + public void setList( List dtoList){ + redisTemplate.opsForValue().set(MARKET_LIST_LIST_CACHE, dtoList, 365, TimeUnit.DAYS); + } + + public List getList(){ + String key = MARKET_LIST_LIST_CACHE ; + if (redisTemplate.hasKey(key)) {//如果有token信息,更新token的有效时间 + List dtoList = (List) redisTemplate.opsForValue().get(key); + return dtoList; + } + return null; + } + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/MarketKCache.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/MarketKCache.java new file mode 100644 index 0000000..2f68194 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/MarketKCache.java @@ -0,0 +1,72 @@ +package com.blockchain.server.currency.redis; + +import com.blockchain.server.currency.dto.CurrencyMarketKDTO; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.SortedMap; +import java.util.concurrent.TimeUnit; + +/** + * \*

Desciption:

+ * \* CreateTime: 2019/4/13 11:56 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +@Component +public class MarketKCache { + private static final String MARKET_LIST_CACHE = "market:list:"; + private static final String MARKET_LIST_LOCK_CACHE = "market:list:lock:"; + private static final int LOCK_WAIT_TIME = 10;//获取锁等待时间 + private static final int LOCK_LEASE_TIME = 10;//释放时间 + + @Autowired + private RedisTemplate redisTemplate; + + /** + * 尝试获取K线缓存行情锁 + * @param redisson + * @param currencyPair + * @param timeType + * @param timeNumber + */ + public boolean tryFairLock(RedissonClient redisson, String currencyPair, String timeType, int timeNumber){ + return RedissonTool.tryFairLock(redisson, + MARKET_LIST_LOCK_CACHE + currencyPair + ":" + timeNumber + timeType, + LOCK_WAIT_TIME,LOCK_LEASE_TIME, TimeUnit.SECONDS); + } + + /** + * 释放K线缓存行情锁 + * @param redisson + * @param currencyPair + * @param timeType + * @param timeNumber + */ + public void unFairLock(RedissonClient redisson,String currencyPair, String timeType, int timeNumber){ + RedissonTool.unFairLock(redisson, MARKET_LIST_LOCK_CACHE + currencyPair + ":" + timeNumber + timeType); + } + + public void setMarketKListCache(SortedMap map, String currencyPair, String timeType, int timeNumber){ + redisTemplate.opsForValue().set(MARKET_LIST_CACHE + currencyPair + ":" + timeNumber + timeType + ,map); + } + + public SortedMap getMarketKListCache(String currencyPair, String timeType, int timeNumber){ + String key = MARKET_LIST_CACHE + currencyPair + ":" + timeNumber + timeType; + if (redisTemplate.hasKey(key)) { + SortedMap map = (SortedMap) redisTemplate.opsForValue().get(key); + return map; + } + return null; + } + + public void removeMarketKListCache(String currencyPair,String timeType,int timeNumber){ + String key = MARKET_LIST_CACHE + currencyPair + ":" + timeNumber + timeType; + if(redisTemplate.hasKey(key)) + redisTemplate.delete(key); + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/MarketLegalCache.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/MarketLegalCache.java new file mode 100644 index 0000000..4925b9d --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/MarketLegalCache.java @@ -0,0 +1,36 @@ +package com.blockchain.server.currency.redis; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +/** + * \*

Desciption:法币缓存

+ * \* CreateTime: 2019/2/21 20:07 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +@Component +public class MarketLegalCache { + public static final String MARKET_LEGAL_CACHE = "market:legal:"; + public static final long MARKET_REDIS_TIME = 365; + + @Autowired + private RedisTemplate redisTemplate; + + public void setMarketCache(String currencyPair,double amount){ + redisTemplate.opsForValue().set(MARKET_LEGAL_CACHE + currencyPair, amount, MARKET_REDIS_TIME, TimeUnit.DAYS); + } + + public double getMarketCache(String currencyPair){ + String key = MARKET_LEGAL_CACHE + currencyPair; + if (redisTemplate.hasKey(key)) {//如果有token信息,更新token的有效时间 + return (double) redisTemplate.opsForValue().get(key); + } + return 0; + } + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/RedissonConfig.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/RedissonConfig.java new file mode 100644 index 0000000..8f2dd35 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/RedissonConfig.java @@ -0,0 +1,41 @@ +package com.blockchain.server.currency.redis; + + +import org.apache.commons.lang3.StringUtils; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.SingleServerConfig; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RedissonConfig { + + @Value("${spring.redis.host}") + private String host; + + @Value("${spring.redis.port}") + private String port; + + @Value("${spring.redis.password}") + private String password; + + @Value("${spring.redis.database}") + private int database; + + @Bean + public RedissonClient getRedisson() { + //redisson配置类 + Config config = new Config(); + //使用redisson单机模式,并设置相关配置 + SingleServerConfig singleServerConfig = config.useSingleServer(); + singleServerConfig.setAddress("redis://" + host + ":" + port).setDatabase(database); + if (StringUtils.isNotBlank(password)) { + singleServerConfig.setPassword(password); + } + //创建redisson客户端 + return Redisson.create(config); + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/RedissonTool.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/RedissonTool.java new file mode 100644 index 0000000..83c4174 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/redis/RedissonTool.java @@ -0,0 +1,48 @@ +package com.blockchain.server.currency.redis; + +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; + +import java.util.concurrent.TimeUnit; + +public class RedissonTool { + + /*** + * 获得公平锁 + * @param redisson + * @param lockKey 锁key名 + * @return + */ + public static RLock fairLock(RedissonClient redisson, String lockKey) { + RLock fairLock = redisson.getFairLock(lockKey); + fairLock.lock(); + return fairLock; + } + + /*** + * 尝试获得锁(公平锁) + * @param redisson 客户端 + * @param lockKey 锁名 + * @param waitTime 获取锁的等待时间 + * @param leaseTime 锁超时时间 + * @param unit 参数的时间单位 + * @return + */ + public static boolean tryFairLock(RedissonClient redisson, String lockKey, int waitTime, int leaseTime, TimeUnit unit) { + RLock fairLock = redisson.getFairLock(lockKey); + try { + return fairLock.tryLock(waitTime, leaseTime, unit); + } catch (InterruptedException e) { + return false; + } + } + + /*** + * 释放锁(公平锁) + * @param redisson + * @param lockKey + */ + public static void unFairLock(RedissonClient redisson, String lockKey) { + redisson.getFairLock(lockKey).unlock(); + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/CurrencyMarketService.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/CurrencyMarketService.java new file mode 100644 index 0000000..b9f137f --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/CurrencyMarketService.java @@ -0,0 +1,142 @@ +package com.blockchain.server.currency.service; + + +import com.blockchain.server.currency.dto.CurrencyMarketDTO; +import com.blockchain.server.currency.dto.CurrencyMarketHistoryDTO; +import com.blockchain.server.currency.dto.CurrencyMarketKDTO; +import com.blockchain.server.currency.model.CurrencyMarket; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.Map; + +public interface CurrencyMarketService { + + /** + * 保存行情信息 + * + * @param currencyMarket + * @return + */ + CurrencyMarket save(CurrencyMarket currencyMarket); + + + /** + * 保存最新交易行情信息 + * + * @param coinName 币种名称 + * @param unitName 币种单位 + * @param amount + * @param total + * @param timestamp + * @return + */ + CurrencyMarketDTO save(String coinName, + String unitName, + BigDecimal amount, + BigDecimal total, + Long timestamp); + + /** + * 保存最新交易行情信息 + * + * @param coinName 币种名称 + * @param unitName 币种单位 + * @param amount + * @param total + * @param timestamp + * @param tradingType + * @return + */ + CurrencyMarketDTO save(String coinName, + String unitName, + BigDecimal amount, + BigDecimal total, + Long timestamp, + String tradingType); + + /** + * 获取币对的最新行情信息 + * + * @param currencyPair + * @return + */ + CurrencyMarketDTO get(String currencyPair); + + /*** + * 获取币对的今天之前的最新行情信息 + * @param currencyPair + * @return + */ + CurrencyMarketDTO getLast(String currencyPair); + + /** + * 获取法币对应的所有主要数字货币行情 + * + * @param coin + * @return + */ + Map getRates(String coin); + + /** + * 获取可用行情列表 + * + * @return + */ + List getList(); + + /** + * 获取首页行情列表 + * + * @return + */ + List getHomeList(); + + /** + * 获取行情涨跌榜 + * + * @return + */ + List getTopList(); + + /** + * 获取历史成交列表 + * + * @param currencyPair + * @return + */ + List getHistoryList(String currencyPair); + + /** + * 根据主要货币获取其所有币对行情 + * + * @param currencyName + * @return + */ + List getQuoteList(String currencyName); + + /** + * 查询行情K线图数据 + * + * @param currencyPair + * @param timeType 时间段类型:MINUTE,HOUR,DAY,WEEK,MONTH + * @param timeNumber 时间间隔数据 + * @param start + * @param stop + * @return + */ + List queryForK(String currencyPair, String timeType, Integer timeNumber, Integer start, Integer stop); + + /** + * 查询行情K线图数据 - 倒序 + * + * @param currencyPair + * @param timeType 时间段类型:MINUTE,HOUR,DAY,WEEK,MONTH + * @param timeNumber 时间间隔数据 + * @param start + * @param stop + * @return + */ + List queryForKSortDESC(String currencyPair, String timeType, Integer timeNumber, Integer start, Integer stop); +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/CurrencyPairService.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/CurrencyPairService.java new file mode 100644 index 0000000..6b1bd9e --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/CurrencyPairService.java @@ -0,0 +1,35 @@ +package com.blockchain.server.currency.service; + + +import com.blockchain.server.currency.dto.CurrencyPairDTO; +import com.blockchain.server.currency.model.CurrencyPair; + +import java.util.List; + +public interface CurrencyPairService { + + CurrencyPair get(String currencyPair); + + void update(CurrencyPair currencyPair); + + /** + * 获取可用币对列表 + * @return + */ + List getUsableList(); + + + /** + * 获取首页列表 + * @return + */ + List getHomeList(); + + /** + * 根据主要货币获取其所有币对 + * @param currencyName + * @return + */ + List getQuoteList(String currencyName); + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/CurrencyService.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/CurrencyService.java new file mode 100644 index 0000000..a7ac8f0 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/CurrencyService.java @@ -0,0 +1,17 @@ +package com.blockchain.server.currency.service; + + +import com.blockchain.server.currency.dto.CurrencyDTO; +import com.blockchain.server.currency.model.Currency; + +import java.util.List; + +public interface CurrencyService { + + Currency get(String currencyName); + + CurrencyDTO getCurrencyInfo(String currencyName, String lg); + + List getQuoteCurrency(); + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/impl/CurrencyMarketServiceImpl.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/impl/CurrencyMarketServiceImpl.java new file mode 100644 index 0000000..f5bf687 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/impl/CurrencyMarketServiceImpl.java @@ -0,0 +1,627 @@ +package com.blockchain.server.currency.service.impl; + +import com.blockchain.common.base.enums.BaseResultEnums; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.common.base.util.DateTimeUtils; +import com.blockchain.common.base.util.JsonUtils; +import com.blockchain.server.currency.common.constant.*; +import com.blockchain.server.currency.common.enums.CurrencyMarketResultEnums; +import com.blockchain.server.currency.common.exception.CurrencyMarketExeption; +import com.blockchain.server.currency.dto.CurrencyMarketDTO; +import com.blockchain.server.currency.dto.CurrencyMarketHistoryDTO; +import com.blockchain.server.currency.dto.CurrencyMarketKDTO; +import com.blockchain.server.currency.dto.CurrencyPairDTO; +import com.blockchain.server.currency.mapper.CurrencyMarketMapper; +import com.blockchain.server.currency.model.CurrencyMarket; +import com.blockchain.server.currency.model.CurrencyPair; +import com.blockchain.server.currency.redis.HistoryCache; +import com.blockchain.server.currency.redis.MarketCache; +import com.blockchain.server.currency.redis.MarketKCache; +import com.blockchain.server.currency.redis.MarketLegalCache; +import com.blockchain.server.currency.service.CurrencyMarketService; +import com.blockchain.server.currency.service.CurrencyPairService; +import com.blockchain.server.currency.websocket.WebSocketSendMsgUtils; +import org.redisson.api.RedissonClient; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Service +public class CurrencyMarketServiceImpl implements CurrencyMarketService { + + @Autowired + private CurrencyMarketMapper currencyMarketMapper; + + @Autowired + private CurrencyPairService currencyPairService; + + @Autowired + private MarketCache marketCache; + + @Autowired + private MarketKCache marketKCache; + + @Autowired + private MarketLegalCache legalCache; + + @Autowired + private HistoryCache historyCache; + + @Autowired + private SimpMessagingTemplate template; + + @Autowired + private RedissonClient redissonClient; + + private static ExecutorService cachedThreadPool = Executors.newSingleThreadExecutor(); + + @Override + public CurrencyMarket save(CurrencyMarket currencyMarket) { + currencyMarketMapper.insert(currencyMarket); + return currencyMarket; + } + + /** + * 保存最新交易行情信息 + * + * @param coinName 币种名称 + * @param unitName 币种单位 + * @param amount + * @param total + * @param timestamp + * @return + */ + @Override + public CurrencyMarketDTO save(String coinName, String unitName, BigDecimal amount, BigDecimal total, Long timestamp) { + return save(coinName, unitName, amount, total, timestamp, TradingTypeEnums.SELL.getValue()); + } + + /** + * 保存最新交易行情信息 + * + * @param coinName 币种名称 + * @param unitName 币种单位 + * @param amount + * @param total + * @param timestamp + * @param tradingType + * @return + */ + @Override + public CurrencyMarketDTO save(String coinName, String unitName, BigDecimal amount, BigDecimal total, Long timestamp, String tradingType) { + String currencyPair = coinName + "-" + unitName; + //检测币对是否合法 + checkCurrency(currencyPair); + CurrencyMarket currencyMarket = new CurrencyMarket(); + currencyMarket.setCurrencyPair(currencyPair); + currencyMarket.setTotal(total); + currencyMarket.setAmount(amount); + currencyMarket.setTimestamp(timestamp); + //保存新数据 + save(currencyMarket); + CurrencyMarketDTO dto = new CurrencyMarketDTO(); + BeanUtils.copyProperties(currencyMarket, dto); + //发送历史成交记录信息 + sendHistoryMsg(dto, tradingType); + //添加缓存数据,发送行情变动信息 + setMarketCache(dto); + + return dto; + } + + @Override + public CurrencyMarketDTO get(String currencyPair) { + //检测币对是否合法 + checkCurrency(currencyPair); + //获取一天的K线行情 + SortedMap map = getOneDayMarketK(currencyPair, + MarketKTypeEnums.ONEDAY.getTimeType(), MarketKTypeEnums.ONEDAY.getTimeNumber()); + CurrencyMarketDTO dto = getMarketInfo(currencyPair, map); + return dto; + } + + @Override + public CurrencyMarketDTO getLast(String currencyPair) { + //检测币对是否合法 + checkCurrency(currencyPair); + //获取一天的K线行情 + SortedMap map = getOneDayMarketK(currencyPair, + MarketKTypeEnums.ONEDAY.getTimeType(), MarketKTypeEnums.ONEDAY.getTimeNumber()); + //获取今天之前的行情 + CurrencyMarketDTO dto = getLastDayMarketInfo(currencyPair, map); + return dto; + } + + /** + * 获取所有法币对应的数据货币行情 + * + * @return + */ + @Override + public Map getRates(String coin) { + Map rates = new HashMap(); + for (BaseCoinEnums value : BaseCoinEnums.values()) { + rates.put(value.getValue(), legalCache.getMarketCache(value.getValue() + coin)); + } + return rates; + } + + /** + * 获取可用行情列表 + * + * @return + */ + @Override + public List getList() { + List currencyPairDTOList = marketCache.getList(); + if (currencyPairDTOList == null) { + currencyPairDTOList = currencyPairService.getUsableList(); + marketCache.setList(currencyPairDTOList); + } + List currencyMarketDTOList = new ArrayList(); + for (CurrencyPairDTO currencyPairDTO : currencyPairDTOList) { + currencyMarketDTOList.add(get(currencyPairDTO.getCurrencyPair())); + } + return currencyMarketDTOList; + } + + /** + * 获取首页行情列表 + * + * @return + */ + @Override + public List getHomeList() { + List dtoList = marketCache.getHomeList(); + if (dtoList == null) { + dtoList = currencyPairService.getHomeList(); + marketCache.setHomeList(dtoList); + } + List list = new ArrayList(); + for (CurrencyPairDTO dto : dtoList) { + list.add(get(dto.getCurrencyPair())); + } + return list; + } + + /** + * 获取行情涨跌榜 + * + * @return + */ + @Override + public List getTopList() { + List list = new ArrayList(); + List usableList = currencyPairService.getUsableList(); + for (int i = 0; i < usableList.size(); i++) { + list.add(get(usableList.get(i).getCurrencyPair())); + } + Collections.sort(list); + List topList = new ArrayList(); + for (int i = 0; i < CommonConstants.TOP_SIZE && i < usableList.size(); i++) { + topList.add(list.get(i)); + } + return topList; + } + + /** + * 获取历史成交列表 + * + * @param currencyPair + * @return + */ + @Override + public List getHistoryList(String currencyPair) { + return historyCache.getList(currencyPair); + } + + /** + * 根据主要货币获取其所有币对 + * + * @param currencyName + * @return + */ + @Override + public List getQuoteList(String currencyName) { + List list = new ArrayList(); + List quoteList = currencyPairService.getQuoteList(currencyName); + for (int i = 0; i < quoteList.size(); i++) { + list.add(get(quoteList.get(i).getCurrencyPair())); + } + return list; + } + + /** + * 查询行情K线图数据 + * + * @param currencyPair + * @param timeType 时间段类型:MINUTE,HOUR,DAY,WEEK,MONTH + * @param timeNumber 时间间隔数据 + * @param start + * @param stop + * @return + */ + @Override + public List queryForK(String currencyPair, String timeType, Integer timeNumber, Integer start, Integer stop) { + SortedMap map = getKMap(currencyPair, timeType, timeNumber); + List list = new ArrayList<>(); + Integer index = 0; + for (CurrencyMarketKDTO obj : map.values()) { + if (index >= start && index < stop) { + list.add(obj); + } + index++; + if (index == stop) { + break; + } + } + return list; + } + + @Override + public List queryForKSortDESC(String currencyPair, String timeType, Integer timeNumber, Integer start, Integer stop) { + List list = this.queryForK(currencyPair, timeType, timeNumber, start, stop); + + //倒序 + Collections.sort(list, new Comparator() { + @Override + public int compare(CurrencyMarketKDTO o1, CurrencyMarketKDTO o2) { + long diff = o1.getTimestamp() - o2.getTimestamp(); + if (diff > 0) { + return -1; + } else if (diff < 0) { + return 1; + } + return 0; + } + }); + + return list; + } + + /** + * 获取情信息 + * + * @param currencyPair + * @param map + * @return + */ + private CurrencyMarketDTO getMarketInfo(String currencyPair, SortedMap map) { + CurrencyMarketKDTO kdto = map.get(map.lastKey()); + //查询最新行情 + CurrencyMarketDTO dto = new CurrencyMarketDTO(); + dto.setCurrencyPair(currencyPair); + dto.setOpen(kdto.getOpen()); + dto.setLowest(kdto.getLowest()); + dto.setHighest(kdto.getHighest()); + dto.setAmount(kdto.getClose()); + dto.setTotal(kdto.getTotal()); + dto.setTimestamp(kdto.getCloseTime()); + if (new Date().getTime() - 86400000 < kdto.getCloseTime()) { + dto.setPercent(kdto.getClose().subtract(kdto.getOpen()).divide(kdto.getOpen(), 4, BigDecimal.ROUND_HALF_UP).floatValue()); + } else { + dto.setPercent(0); + } + //添加法币值 + dto = setLegalMarket(currencyPair.split("-")[1], dto); + return dto; + } + + /*** + * 获取今天之前最新的行情 + * @param currencyPair + * @param map + * @return + */ + private CurrencyMarketDTO getLastDayMarketInfo(String currencyPair, SortedMap map) { + //获取最后一个 + CurrencyMarketKDTO kdto = map.get(map.lastKey()); + + //获取当前年月日毫秒数 + MarketKTypeEnums ktype = MarketKTypeEnums.ONEDAY; + Date now = new Date(); + Long timestamp = (now.getTime() / (ktype.getTimeNumber() * MarketKEnums.valueOf(ktype.getTimeType()).getSecond())) * ktype.getTimeNumber() * MarketKEnums.valueOf(ktype.getTimeType()).getSecond(); + + //数据量在 1-2以内时,获取第一天数据 + if (map.size() > 0 && map.size() < 3) { + kdto = map.get(map.firstKey()); + } else { + //数据量大于2时,获取倒数第二条 + //循环 + while (map != null || map.size() != 0) { + String lastKey = map.lastKey(); + //如果最后一个Key毫秒数大于当前年月日毫秒数 + if (Long.valueOf(lastKey) >= timestamp) { + //删除最后一个 + map.remove(lastKey); + } else { + //获取最后一个 + kdto = map.get(lastKey); + break; + } + } + } + + //查询最新行情 + CurrencyMarketDTO dto = new CurrencyMarketDTO(); + dto.setCurrencyPair(currencyPair); + dto.setOpen(kdto.getOpen()); + dto.setLowest(kdto.getLowest()); + dto.setHighest(kdto.getHighest()); + dto.setAmount(kdto.getClose()); + dto.setTotal(kdto.getTotal()); + dto.setTimestamp(kdto.getCloseTime()); + if (new Date().getTime() - 86400000 < kdto.getCloseTime()) { + dto.setPercent(kdto.getClose().subtract(kdto.getOpen()).divide(kdto.getOpen(), 4, BigDecimal.ROUND_HALF_UP).floatValue()); + } else { + dto.setPercent(0); + } + //添加法币值 + dto = setLegalMarket(currencyPair.split("-")[1], dto); + return dto; + + } + + /** + * 获取K线图map数据 + * + * @param currencyPair + * @param timeType + * @param timeNumber + * @return + */ + private SortedMap getKMap(String currencyPair, String timeType, Integer timeNumber) { + if (currencyPair == null) { + throw new CurrencyMarketExeption(CurrencyMarketResultEnums.CURRENCY_PAIR_NULL); + } + //检测币对是否合法 + checkCurrency(currencyPair); + return getOneDayMarketK(currencyPair, timeType, timeNumber); + } + + /** + * 保存历史成交记录缓存 + * + * @param dto + * @param tradingType + * @return + */ + private List pushHistory(CurrencyMarketDTO dto, String tradingType) { + CurrencyMarketHistoryDTO currencyMarketHistoryDTO = new CurrencyMarketHistoryDTO(); + String[] currencys = dto.getCurrencyPair().split("-"); + currencyMarketHistoryDTO.setCoinName(currencys[0]); + currencyMarketHistoryDTO.setUnitName(currencys[1]); + currencyMarketHistoryDTO.setCreateTime(DateTimeUtils.format(dto.getTimestamp(), DateTimeUtils.DATE_TIME_FORMAT)); + currencyMarketHistoryDTO.setMakerPrice(dto.getAmount()); + currencyMarketHistoryDTO.setTradingNum(dto.getTotal()); + currencyMarketHistoryDTO.setTradingType(tradingType); + return historyCache.push(dto.getCurrencyPair(), currencyMarketHistoryDTO); + } + + /** + * 添加法币价格 + * + * @param unitName + * @param dto + */ + private CurrencyMarketDTO setLegalMarket(String unitName, CurrencyMarketDTO dto) { + dto.setCnyAmount(legalCache.getMarketCache(unitName + RatesEnums.CNY.getValue())); + dto.setUsdAmount(legalCache.getMarketCache(unitName + RatesEnums.USD.getValue())); + dto.setHkdAmount(legalCache.getMarketCache(unitName + RatesEnums.HKD.getValue())); + dto.setEurAmount(legalCache.getMarketCache(unitName + RatesEnums.EUR.getValue())); + return dto; + } + + /** + * 添加缓存数据 + * + * @param dto 新行情数据 + */ + private void setMarketCache(CurrencyMarketDTO dto) { + for (MarketKTypeEnums ktype : MarketKTypeEnums.values()) { + boolean lock = false; + while (!lock) { + //若拿到锁则结束,若拿不到循环尝试 + lock = marketKCache.tryFairLock(redissonClient, dto.getCurrencyPair(), + ktype.getTimeType(), ktype.getTimeNumber()); + if (lock) { + SortedMap map = marketKCache.getMarketKListCache(dto.getCurrencyPair(), ktype.getTimeType(), ktype.getTimeNumber()); + if (map == null) + map = selectMarketK(dto.getCurrencyPair(), ktype.getTimeType(), ktype.getTimeNumber()); + if (map == null) continue; + Long timestamp = (dto.getTimestamp() / (ktype.getTimeNumber() * MarketKEnums.valueOf(ktype.getTimeType()).getSecond())) * ktype.getTimeNumber() * MarketKEnums.valueOf(ktype.getTimeType()).getSecond(); + CurrencyMarketKDTO kdto = map.get(timestamp.toString()); + if (kdto == null) { + if (map.size() >= CommonConstants.MARKET_SIZE) { + map.remove(map.firstKey()); + } + kdto = new CurrencyMarketKDTO(); + kdto.setHighest(dto.getAmount()); + kdto.setLowest(dto.getAmount()); + kdto.setOpen(dto.getAmount()); + kdto.setClose(dto.getAmount()); + kdto.setOpenTime(dto.getTimestamp()); + kdto.setCloseTime(dto.getTimestamp()); + kdto.setDate(getDate(dto.getTimestamp(), ktype.getTimeType(), ktype.getTimeNumber())); + kdto.setTotal(dto.getTotal()); + kdto.setTimestamp(timestamp); + } else { + kdto.setTotal(kdto.getTotal().add(dto.getTotal())); + //判断更新开盘收盘 + if (kdto.getOpenTime() > dto.getTimestamp()) { + kdto.setOpenTime(dto.getTimestamp()); + kdto.setOpen(dto.getAmount()); + } else if (kdto.getCloseTime() < dto.getTimestamp()) { + kdto.setCloseTime(dto.getTimestamp()); + kdto.setClose(dto.getAmount()); + } + //判断更新最高最低 + if (kdto.getLowest().compareTo(dto.getAmount()) == 1) { + kdto.setLowest(dto.getAmount()); + } else if (kdto.getHighest().compareTo(dto.getAmount()) == -1) { + kdto.setHighest(dto.getAmount()); + } + } + //发送行情信息 + if (ktype == MarketKTypeEnums.ONEDAY) { + sendMarketMsg(dto.getCurrencyPair(), map); + } + sendMarketKMsg(dto.getCurrencyPair(), kdto, ktype.getTimeType(), ktype.getTimeNumber()); + map.put(timestamp.toString(), kdto); + marketKCache.setMarketKListCache(map, dto.getCurrencyPair(), ktype.getTimeType(), ktype.getTimeNumber()); + //释放锁 + marketKCache.unFairLock(redissonClient, dto.getCurrencyPair(), + ktype.getTimeType(), ktype.getTimeNumber()); + } + } + } + + } + + /** + * 发送K线变化信息 + * + * @param kdto + * @param timeType + * @param timeNumber + */ + private void sendMarketKMsg(String currencyPair, CurrencyMarketKDTO kdto, String timeType, Integer timeNumber) { + cachedThreadPool.execute(new Runnable() { + @Override + public void run() { + WebSocketSendMsgUtils.sendMarketKMsg(template, currencyPair, timeNumber + timeType, JsonUtils.objectToJson(kdto)); + } + }); + } + + /** + * 发送行情信息 + * + * @param currencyPair + * @param map + */ + private void sendMarketMsg(String currencyPair, SortedMap map) { + cachedThreadPool.execute(new Runnable() { + @Override + public void run() { + WebSocketSendMsgUtils.sendMarketMsg(template, JsonUtils.objectToJson(getMarketInfo(currencyPair, map))); + } + }); + } + + /** + * 发送历史成交记录信息 + * + * @param dto + * @param tradingType + */ + private void sendHistoryMsg(CurrencyMarketDTO dto, String tradingType) { + cachedThreadPool.execute(new Runnable() { + @Override + public void run() { + List historyList = pushHistory(dto, tradingType); + WebSocketSendMsgUtils.sendHistoryMsg(template, dto.getCurrencyPair(), JsonUtils.objectToJson(historyList)); + } + }); + } + + /** + * 查询数据库获取K线行情 + * + * @param currencyPair + * @param timeType + * @param timeNumber + * @return + */ + private SortedMap selectMarketK(String currencyPair, String timeType, Integer timeNumber) { + Map params = new HashMap(); + params.put("currencyPair", currencyPair); + params.put("timeType", timeType); + params.put("timeNumber", timeNumber); + params.put("second", timeNumber * MarketKEnums.valueOf(timeType).getSecond()); + List list = currencyMarketMapper.queryForK(params); + SortedMap map = new TreeMap(); + for (CurrencyMarketKDTO kdto : list) { + Long timestamp = (kdto.getOpenTime() / (timeNumber * MarketKEnums.valueOf(timeType).getSecond())) * timeNumber * MarketKEnums.valueOf(timeType).getSecond(); + kdto.setDate(getDate(kdto.getOpenTime(), timeType, timeNumber)); + kdto.setTimestamp(timestamp); + map.put(timestamp.toString(), kdto); + } + return map; + } + + /** + * 获取格式化日期 + * + * @param timestamp + * @param timeType + * @param timeNumber + * @return + */ + private String getDate(Long timestamp, String timeType, Integer timeNumber) { + Calendar calendar = new GregorianCalendar(); + calendar.setTimeInMillis(timestamp); + if (timeType.equals(MarketKEnums.MINUTE.getValue())) { + int minute = calendar.get(Calendar.MINUTE) / timeNumber * timeNumber; + String date = DateTimeUtils.format(timestamp, DateTimeUtils.MONTH_DAY_HOUR_FORMAT) + ":" + + (minute >= 10 ? minute : "0" + minute); + return date; + } else if (timeType.equals(MarketKEnums.HOUR.getValue())) { + int hour = calendar.get(Calendar.HOUR_OF_DAY) / timeNumber * timeNumber; + String date = DateTimeUtils.format(timestamp, DateTimeUtils.MONTH_DAY_FORMAT) + " " + + (hour >= 10 ? hour : "0" + hour) + ":00"; + return date; + } else if (timeType.equals(MarketKEnums.DAY.getValue())) { + return DateTimeUtils.format(timestamp, DateTimeUtils.YEAR_MONTH_DATE_FORMAT); + } + return null; + } + + /** + * 检测币对合法性 + * + * @param currencyPair + */ + private void checkCurrency(String currencyPair) { + if (currencyPair == null) { + throw new CurrencyMarketExeption(CurrencyMarketResultEnums.CURRENCY_PAIR_NULL); + } + CurrencyPair currencyPairModel = currencyPairService.get(currencyPair); + if (currencyPairModel == null) { + throw new CurrencyMarketExeption(CurrencyMarketResultEnums.CURRENCY_PAIR_ERROR); + } + if (currencyPairModel.getStatus() != 1) { + throw new CurrencyMarketExeption(CurrencyMarketResultEnums.CURRENCY_PAIR_UNUSABLE); + } + } + + /** + * 获取一天的K线行情 + * + * @param currencyPair + */ + private SortedMap getOneDayMarketK(String currencyPair, String timeType, Integer timeNumber) { + SortedMap map = marketKCache.getMarketKListCache(currencyPair, timeType, timeNumber); + if (map == null) { + boolean lock = marketKCache.tryFairLock(redissonClient, currencyPair, + timeType, timeNumber); + if (lock) { + map = selectMarketK(currencyPair, timeType, timeNumber); + //保存K线缓存 + marketKCache.setMarketKListCache(map, currencyPair, timeType, timeNumber); + //释放锁 + marketKCache.unFairLock(redissonClient, currencyPair, + timeType, timeNumber); + } else { + throw new BaseException(BaseResultEnums.BUSY); + } + } + return map; + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/impl/CurrencyPairServiceImpl.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/impl/CurrencyPairServiceImpl.java new file mode 100644 index 0000000..3ebf06d --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/impl/CurrencyPairServiceImpl.java @@ -0,0 +1,59 @@ +package com.blockchain.server.currency.service.impl; + +import com.blockchain.server.currency.dto.CurrencyPairDTO; +import com.blockchain.server.currency.mapper.CurrencyPairMapper; +import com.blockchain.server.currency.model.CurrencyPair; +import com.blockchain.server.currency.service.CurrencyPairService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class CurrencyPairServiceImpl implements CurrencyPairService { + + @Autowired + private CurrencyPairMapper currencyPairMapper; + + @Override + public CurrencyPair get(String currencyPair) { + return currencyPairMapper.selectByPrimaryKey(currencyPair); + } + + @Override + public void update(CurrencyPair currencyPair) { + currencyPairMapper.updateByPrimaryKeySelective(currencyPair); + } + + /** + * 获取可用币对列表 + * + * @return + */ + @Override + public List getUsableList() { + return currencyPairMapper.getUsableList(); + } + + /** + * 获取首页列表 + * + * @return + */ + @Override + public List getHomeList() { + return currencyPairMapper.getHomeList(); + } + + /** + * 根据主要货币获取其所有币对 + * + * @param currencyName + * @return + */ + @Override + public List getQuoteList(String currencyName) { + return currencyPairMapper.getQuoteList("%-" + currencyName); + } + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/impl/CurrencyServiceImpl.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/impl/CurrencyServiceImpl.java new file mode 100644 index 0000000..9272441 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/service/impl/CurrencyServiceImpl.java @@ -0,0 +1,32 @@ +package com.blockchain.server.currency.service.impl; + +import com.blockchain.server.currency.dto.CurrencyDTO; +import com.blockchain.server.currency.mapper.CurrencyMapper; +import com.blockchain.server.currency.model.Currency; +import com.blockchain.server.currency.service.CurrencyService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class CurrencyServiceImpl implements CurrencyService { + + @Autowired + private CurrencyMapper currencyMapper; + + @Override + public Currency get(String currencyName) { + return currencyMapper.selectByPrimaryKey(currencyName); + } + + @Override + public CurrencyDTO getCurrencyInfo(String currencyName, String lg) { + return currencyMapper.getByCurrencyName(currencyName, lg); + } + + @Override + public List getQuoteCurrency() { + return currencyMapper.getQuoteCurrency(); + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/websocket/WebSocketConfig.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/websocket/WebSocketConfig.java new file mode 100644 index 0000000..52d203f --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/websocket/WebSocketConfig.java @@ -0,0 +1,39 @@ +package com.blockchain.server.currency.websocket; + +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.util.AntPathMatcher; +import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +/** + * Created by xiancw on 2017/5/4. + */ +@Configuration +@EnableWebSocketMessageBroker +public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + /** + * 配置消息代理 + * + * @param registry + */ + @Override + public void configureMessageBroker(MessageBrokerRegistry registry) { + //设置客户端订阅号前缀,有两个:/topic(一对多)、/queue(一对一) + registry.enableSimpleBroker("/topic", "/queue"); + // 设置Controller的@MessageMapping前缀,也就是客户端要向服务端发送消息,必须要有该前缀,这个前缀是任意字符串 + registry.setApplicationDestinationPrefixes("/app"); + //webSocket默认是使用"."来分割路径,spring是使用"/" + registry.setPathMatcher(new AntPathMatcher("/")); + } + + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + //设置端点,客户端使用"appApplicationUrl/websocket"进行连接 + //setAllowedOrigins的字符串如果不是"*",必须是"HTTP:"或者是"HTTPS:"开头 + registry.addEndpoint("/websocket").setAllowedOrigins("*").withSockJS(); + } + +} diff --git a/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/websocket/WebSocketSendMsgUtils.java b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/websocket/WebSocketSendMsgUtils.java new file mode 100644 index 0000000..e42d1cf --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/java/com/blockchain/server/currency/websocket/WebSocketSendMsgUtils.java @@ -0,0 +1,37 @@ +package com.blockchain.server.currency.websocket; + +import org.springframework.messaging.simp.SimpMessagingTemplate; + +/** + * Created by HXL on 2017/5/8. + */ +public class WebSocketSendMsgUtils { + private static final String BROKER = "/topic"; + + /** + * 行情更新消息 + * + * @param template + */ + public static void sendMarketMsg(SimpMessagingTemplate template, Object json) { + template.convertAndSend(BROKER + "/market", json); + } + + /** + * 行情更新消息 + * + * @param template + */ + public static void sendHistoryMsg(SimpMessagingTemplate template,String currencyPair, Object json) { + template.convertAndSend(BROKER + "/history/" + currencyPair, json); + } + + /** + * K线行情更新消息 + * + * @param template + */ + public static void sendMarketKMsg(SimpMessagingTemplate template,String currencyPair,String ktype, Object json) { + template.convertAndSend(BROKER + "/marketk/" + currencyPair + "/" + ktype, json); + } +} diff --git a/blockchain-server/blockchain-server-currency/src/main/resources/application.yml b/blockchain-server/blockchain-server-currency/src/main/resources/application.yml new file mode 100644 index 0000000..de0b943 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/resources/application.yml @@ -0,0 +1,4 @@ +#日志配置路径 +logging: + config: classpath:logback/logback-${spring.cloud.config.profile}.xml + diff --git a/blockchain-server/blockchain-server-currency/src/main/resources/bootstrap.yml b/blockchain-server/blockchain-server-currency/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..d7860ea --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/resources/bootstrap.yml @@ -0,0 +1,22 @@ +server: + port: 8601 +spring: + cloud: + #配置中心 + config: + profile: dev + name: springconf,springcloudconf,redisconf,dbconf,xssconf,marketconf,ipconf + discovery: + service-id: dapp-config-server + enabled: true + application: + name: dapp-currency-server +#注册中心 +eureka: + client: + service-url: + defaultZone: http://eureka:8001/eureka/ + instance: + prefer-ip-address: true + instance-id: ${spring.cloud.client.ip-address}:${server.port} + hostname: ${spring.cloud.client.ip-address} diff --git a/blockchain-server/blockchain-server-currency/src/main/resources/logback/logback-dev.xml b/blockchain-server/blockchain-server-currency/src/main/resources/logback/logback-dev.xml new file mode 100644 index 0000000..9a0e324 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/resources/logback/logback-dev.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/src/main/resources/logback/logback-pro.xml b/blockchain-server/blockchain-server-currency/src/main/resources/logback/logback-pro.xml new file mode 100644 index 0000000..5b55059 --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/resources/logback/logback-pro.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/src/main/resources/mapper/CurrencyMapper.xml b/blockchain-server/blockchain-server-currency/src/main/resources/mapper/CurrencyMapper.xml new file mode 100644 index 0000000..39507dc --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/resources/mapper/CurrencyMapper.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + SELECT + currency_name,issue_time,total_supply,total_circulation,ico_amount,white_paper,official_website,block_url, + + + currency_name_cn AS currency_full_name, + descr_cn AS descr + + + currency_name_en AS currency_full_name, + descr_en AS descr + + + currency_name_hk AS currency_full_name, + descr_hk AS descr + + + FROM dapp_currency + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/src/main/resources/mapper/CurrencyMarketMapper.xml b/blockchain-server/blockchain-server-currency/src/main/resources/mapper/CurrencyMarketMapper.xml new file mode 100644 index 0000000..990449b --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/resources/mapper/CurrencyMarketMapper.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-currency/src/main/resources/mapper/CurrencyPairMapper.xml b/blockchain-server/blockchain-server-currency/src/main/resources/mapper/CurrencyPairMapper.xml new file mode 100644 index 0000000..750db8b --- /dev/null +++ b/blockchain-server/blockchain-server-currency/src/main/resources/mapper/CurrencyPairMapper.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-databot/pom.xml b/blockchain-server/blockchain-server-databot/pom.xml new file mode 100644 index 0000000..73e37d9 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/pom.xml @@ -0,0 +1,57 @@ + + + + blockchain-server + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + blockchain-server-databot + + + com.blockchain + blockchain-server-base + 1.0-SNAPSHOT + compile + + + org.redisson + redisson + 3.10.2 + + + org.springframework.boot + spring-boot-test + test + + + junit + junit + test + + + org.springframework + spring-test + 5.1.4.RELEASE + test + + + com.blockchain + blockchain-common-tx + 1.0-SNAPSHOT + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/BotApplication.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/BotApplication.java new file mode 100644 index 0000000..8067c7e --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/BotApplication.java @@ -0,0 +1,12 @@ +package com.blockchain.server.databot; + +import com.blockchain.server.base.BaseConf; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication(scanBasePackageClasses = {BaseConf.class, BotApplication.class}) +public class BotApplication { + public static void main(String[] args) { + SpringApplication.run(BotApplication.class); + } +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/constant/CommonConstant.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/constant/CommonConstant.java new file mode 100644 index 0000000..eb28e3f --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/constant/CommonConstant.java @@ -0,0 +1,12 @@ +package com.blockchain.server.databot.common.constant; + +public class CommonConstant { + public static final String YES = "Y"; //状态:启用 + public static final String NO = "N"; //状态:禁用 + + public static final String BUY = "BUY"; //交易方向:买入 + public static final String SELL = "SELL"; //交易方向:卖出 + + public static final String PRICE = "PRICE"; //根据定价范围进行撮合 + public static final String PERCENT = "PERCENT"; //根据最新成交价涨跌幅范围进行撮合 +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/enums/DataBotEnums.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/enums/DataBotEnums.java new file mode 100644 index 0000000..df77b86 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/enums/DataBotEnums.java @@ -0,0 +1,34 @@ +package com.blockchain.server.databot.common.enums; + +public enum DataBotEnums { + UNKNOWN_TRADING_TYPE(8901, "未知交易方向!", "Unknown trading direction!", "未知交易方向!"), + UNKNOWN_STATUS(8902, "未知状态!", "Unknown state!", "未知狀態"), + ; + private int code; + private String cnmsg; + private String enMsg; + private String hkmsg; + + DataBotEnums(int code, String cnmsg, String enMsg, String hkmsg) { + this.code = code; + this.cnmsg = cnmsg; + this.enMsg = enMsg; + this.hkmsg = hkmsg; + } + + public int getCode() { + return code; + } + + public String getCnmsg() { + return cnmsg; + } + + public String getEnMsg() { + return enMsg; + } + + public String getHkmsg() { + return hkmsg; + } +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/exception/DataBotExeption.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/exception/DataBotExeption.java new file mode 100644 index 0000000..8dff772 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/exception/DataBotExeption.java @@ -0,0 +1,39 @@ +package com.blockchain.server.databot.common.exception; + + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.databot.common.enums.DataBotEnums; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +public class DataBotExeption extends BaseException { + public DataBotExeption(DataBotEnums rs) { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + //可能是定时器调用,避免获取request空指针 + if (servletRequestAttributes == null) { + this.code = rs.getCode(); + this.msg = rs.getHkmsg(); + } else { + HttpServletRequest request = servletRequestAttributes.getRequest(); + String userLocale = HttpRequestUtil.getUserLocale(request); + String msg = ""; + switch (userLocale) { + case BaseConstant.USER_LOCALE_EN_US: + msg = rs.getEnMsg(); + break; + case BaseConstant.USER_LOCALE_ZH_CN: + msg = rs.getCnmsg(); + break; + default: + msg = rs.getHkmsg(); + break; + } + this.code = rs.getCode(); + this.msg = msg; + } + } +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/redisson/RedissonConfig.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/redisson/RedissonConfig.java new file mode 100644 index 0000000..bb4110b --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/redisson/RedissonConfig.java @@ -0,0 +1,41 @@ +package com.blockchain.server.databot.common.redisson; + + +import org.apache.commons.lang3.StringUtils; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.SingleServerConfig; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RedissonConfig { + + @Value("${spring.redis.host}") + private String host; + + @Value("${spring.redis.port}") + private String port; + + @Value("${spring.redis.password}") + private String password; + + @Value("${spring.redis.database}") + private int database; + + @Bean + public RedissonClient getRedisson() { + //redisson配置类 + Config config = new Config(); + //使用redisson单机模式,并设置相关配置 + SingleServerConfig singleServerConfig = config.useSingleServer(); + singleServerConfig.setAddress("redis://" + host + ":" + port).setDatabase(database); + if (StringUtils.isNotBlank(password)) { + singleServerConfig.setPassword(password); + } + //创建redisson客户端 + return Redisson.create(config); + } +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/redisson/RedissonTool.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/redisson/RedissonTool.java new file mode 100644 index 0000000..d15d32a --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/common/redisson/RedissonTool.java @@ -0,0 +1,48 @@ +package com.blockchain.server.databot.common.redisson; + +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; + +import java.util.concurrent.TimeUnit; + +public class RedissonTool { + + /*** + * 获得公平锁 + * @param redisson + * @param lockKey 锁key名 + * @return + */ + public static RLock fairLock(RedissonClient redisson, String lockKey) { + RLock fairLock = redisson.getFairLock(lockKey); + fairLock.lock(); + return fairLock; + } + + /*** + * 尝试获得锁(公平锁) + * @param redisson 客户端 + * @param lockKey 锁名 + * @param waitTime 获取锁的等待时间 + * @param leaseTime 锁超时时间 + * @param unit 参数的时间单位 + * @return + */ + public static boolean tryFairLock(RedissonClient redisson, String lockKey, int waitTime, int leaseTime, TimeUnit unit) { + RLock fairLock = redisson.getFairLock(lockKey); + try { + return fairLock.tryLock(waitTime, leaseTime, unit); + } catch (InterruptedException e) { + return false; + } + } + + /*** + * 释放锁(公平锁) + * @param redisson + * @param lockKey + */ + public static void unFairLock(RedissonClient redisson, String lockKey) { + redisson.getFairLock(lockKey).unlock(); + } +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/dto/rpc/CurrencyMarketDTO.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/dto/rpc/CurrencyMarketDTO.java new file mode 100644 index 0000000..4de4e39 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/dto/rpc/CurrencyMarketDTO.java @@ -0,0 +1,32 @@ +package com.blockchain.server.databot.dto.rpc; + +import com.blockchain.common.base.dto.BaseDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CurrencyMarketDTO extends BaseDTO implements Comparable{ + + private String currencyPair; + private BigDecimal amount; + private Long timestamp; + private float percent; + private double usdAmount; + private double cnyAmount; + private double hkdAmount; + private double eurAmount; + private BigDecimal lowest;//最低 + private BigDecimal highest;//最高 + private BigDecimal open;//开盘 + private BigDecimal total;//成交量 + + @Override + public int compareTo(CurrencyMarketDTO o) { + return (int)(10000*o.percent - 10000*this.percent); + } +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/dto/rpc/PublishOrderDTO.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/dto/rpc/PublishOrderDTO.java new file mode 100644 index 0000000..5da1711 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/dto/rpc/PublishOrderDTO.java @@ -0,0 +1,25 @@ +package com.blockchain.server.databot.dto.rpc; + +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +public class PublishOrderDTO implements Serializable { + private String id; + private String userId; + private BigDecimal unitPrice; + private BigDecimal totalNum; + private BigDecimal lastNum; + private BigDecimal totalTurnover; + private BigDecimal lastTurnover; + private String coinName; + private String unitName; + private String orderStatus; + private String orderType; + private String publishType; + private int version; + private java.util.Date createTime; + private java.util.Date modifyTime; +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/entity/CurrencyConfig.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/entity/CurrencyConfig.java new file mode 100644 index 0000000..303c979 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/entity/CurrencyConfig.java @@ -0,0 +1,62 @@ +package com.blockchain.server.databot.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * CurrencyConfig 数据传输类 + * + * @version 1.0 + * @date 2019-06-03 11:37:01 + */ +@Table(name = "bot_currency_config") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CurrencyConfig extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "currency_pair") + private String currencyPair; + @Column(name = "status") + private String status; + @Column(name = "price_type") + private String priceType; + @Column(name = "k_change_percent") + private Float kChangePercent; + @Column(name = "k_max_change_percent") + private Float kMaxChangePercent; + @Column(name = "k_day_total_amount") + private Float kDayTotalAmount; + @Column(name = "k_max_price") + private Float kMaxPrice; + @Column(name = "k_min_price") + private Float kMinPrice; + @Column(name = "buy_max_price") + private Float buyMaxPrice; + @Column(name = "buy_min_price") + private Float buyMinPrice; + @Column(name = "buy_price_percent") + private Float buyPricePercent; + @Column(name = "buy_total_amount") + private Float buyTotalAmount; + @Column(name = "sell_max_price") + private Float sellMaxPrice; + @Column(name = "sell_min_price") + private Float sellMinPrice; + @Column(name = "sell_price_percent") + private Float sellPricePercent; + @Column(name = "sell_total_amount") + private Float sellTotalAmount; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/entity/MatchConfig.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/entity/MatchConfig.java new file mode 100644 index 0000000..2258cb3 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/entity/MatchConfig.java @@ -0,0 +1,50 @@ +package com.blockchain.server.databot.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * MatchConfig 数据传输类 + * + * @version 1.0 + * @date 2019-06-25 13:52:32 + */ +@Table(name = "bot_match_config") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MatchConfig extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "user_id") + private String userId; + @Column(name = "coin_name") + private String coinName; + @Column(name = "unit_name") + private String unitName; + @Column(name = "min_price") + private BigDecimal minPrice; + @Column(name = "max_price") + private BigDecimal maxPrice; + @Column(name = "min_percent") + private BigDecimal minPercent; + @Column(name = "max_percent") + private BigDecimal maxPercent; + @Column(name = "price_type") + private String priceType; + @Column(name = "status") + private String status; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/feign/CctFeign.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/feign/CctFeign.java new file mode 100644 index 0000000..d1328e5 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/feign/CctFeign.java @@ -0,0 +1,85 @@ +package com.blockchain.server.databot.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.base.annotation.BypassedFeign; +import com.blockchain.server.databot.dto.rpc.PublishOrderDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.math.BigDecimal; +import java.util.List; + +@FeignClient(name = "dapp-cct-server", path = "/inner") +public interface CctFeign { + + /*** + * 推送前端买卖盘口数据请求 + * @param coinName + * @param unitName + * @return + */ + @GetMapping("/send") + ResultDTO send(@RequestParam("coinName") String coinName, + @RequestParam("unitName") String unitName); + + /*** + * 查询单价范围内的挂单 + * @param coinName + * @param unitName + * @param minPrice + * @param maxPrice + * @return + */ + @PostMapping("/listBeMatchOrderToBot") + ResultDTO> listBeMatchOrderToBot(@RequestParam("coinName") String coinName, + @RequestParam("unitName") String unitName, + @RequestParam("minPrice") BigDecimal minPrice, + @RequestParam("maxPrice") BigDecimal maxPrice); + + /**** + * 发布限价买单 - 用于撮合机器人 + * @param userId + * @param coinName + * @param unitName + * @param totalNum + * @param price + * @return + */ + @PostMapping("/handleLimitBuyToBot") + @BypassedFeign + ResultDTO handleLimitBuyToBot(@RequestParam("userId") String userId, + @RequestParam("coinName") String coinName, + @RequestParam("unitName") String unitName, + @RequestParam("totalNum") BigDecimal totalNum, + @RequestParam("price") BigDecimal price); + + /**** + * 发布限价卖单 - 用于撮合机器人 + * @param userId + * @param coinName + * @param unitName + * @param totalNum + * @param price + * @return + */ + @PostMapping("/handleLimitSellToBot") + @BypassedFeign + ResultDTO handleLimitSellToBot(@RequestParam("userId") String userId, + @RequestParam("coinName") String coinName, + @RequestParam("unitName") String unitName, + @RequestParam("totalNum") BigDecimal totalNum, + @RequestParam("price") BigDecimal price); + + /*** + * 撮合 - 用于撮合机器人 + * @param matchId + * @param bymatchId + * @return + */ + @PostMapping("/handleMatchToBot") + @BypassedFeign + ResultDTO handleMatchToBot(@RequestParam("matchId") String matchId, + @RequestParam("bymatchId") String bymatchId); +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/feign/CurrencyFeign.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/feign/CurrencyFeign.java new file mode 100644 index 0000000..80896a4 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/feign/CurrencyFeign.java @@ -0,0 +1,42 @@ +package com.blockchain.server.databot.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.databot.dto.rpc.CurrencyMarketDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.math.BigDecimal; + +@FeignClient(name = "dapp-currency-server") +public interface CurrencyFeign { + + String INNER_PATH = "/inner"; //inner内部接口访问前缀 + String MARKET_PATH = "/market"; //market开放接口访问前缀 + + /*** + * 保存最新行情信息 + * @param coinName + * @param unitName + * @param amount + * @param total + * @param timestamp + * @return + */ + @PostMapping(INNER_PATH + "/market/save") + ResultDTO save(@RequestParam("coinName") String coinName, + @RequestParam("unitName") String unitName, + @RequestParam("amount") BigDecimal amount, + @RequestParam("total") BigDecimal total, + @RequestParam("timestamp") Long timestamp, + @RequestParam("tradingType") String tradingType); + + /*** + * 获取数字货币最新行情 + * @param currencyPair + * @return + */ + @GetMapping(MARKET_PATH + "/get") + ResultDTO get(@RequestParam("currencyPair") String currencyPair); +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/mapper/CurrencyConfigMapper.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/mapper/CurrencyConfigMapper.java new file mode 100644 index 0000000..f544043 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/mapper/CurrencyConfigMapper.java @@ -0,0 +1,32 @@ +package com.blockchain.server.databot.mapper; + +import com.blockchain.server.databot.entity.CurrencyConfig; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * CurrencyConfigMapper 数据访问类 + * + * @version 1.0 + * @date 2019-06-03 11:37:01 + */ +@Repository +public interface CurrencyConfigMapper extends Mapper { + + /*** + * 根据币对查询币对配置 + * @param currencyPair + * @return + */ + CurrencyConfig selectByCurrencyPair(@Param("currencyPair") String currencyPair); + + /*** + * 根据状态查询币对配置 + * @return + */ + List selectByStatus(@Param("status") String status); + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/mapper/MatchConfigMapper.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/mapper/MatchConfigMapper.java new file mode 100644 index 0000000..9cf6a1e --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/mapper/MatchConfigMapper.java @@ -0,0 +1,28 @@ +package com.blockchain.server.databot.mapper; + +import com.blockchain.server.databot.entity.MatchConfig; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + + +/** + * MatchConfigMapper 数据访问类 + * + * @version 1.0 + * @date 2019-06-25 13:52:32 + */ +@Repository +public interface MatchConfigMapper extends Mapper { + + /*** + * 根据基本货币、二级货币、状态查询订单 + * @param coinName + * @param unitName + * @param stauts + * @return + */ + MatchConfig selectByCoinAndUnitAndStauts(@Param("coinName") String coinName, + @Param("unitName") String unitName, + @Param("status") String stauts); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/redis/CurrencyConfigCache.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/redis/CurrencyConfigCache.java new file mode 100644 index 0000000..4c89f22 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/redis/CurrencyConfigCache.java @@ -0,0 +1,143 @@ +package com.blockchain.server.databot.redis; + +import com.blockchain.server.databot.common.redisson.RedissonTool; +import com.blockchain.server.databot.entity.CurrencyConfig; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ScanOptions; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Component +public class CurrencyConfigCache { + + @Autowired + private RedisTemplate redisTemplate; + @Autowired + private RedissonClient redissonClient; + + private static final int LOCK_WAIT_TIME = 5; //获取锁等待时间 + private static final int LOCK_LEASE_TIME = 5; //释放时间 + private static final String CURRENCY_CONFIG_LIST_CACHE = "bot:currency:config";//币对配置列表数据key + private static final String CURRENCY_CONFIG_LOCK_CACHE = "bot:currency:config:distributed:lock";//币对配置数据锁key + private static final String CURRENCY_K_DAY_TOTAL_AMOUNT_CACHE = "bot:currency:config:kamount:{0}"; //K线每日总数量key + private static final String CURRENCY_K_DAY_TOTAL_AMOUNT_LOCK_CACHE = "bot:currency:config:kamount:distributed:lock:{0}"; //K线每日总数量锁key + + /*** + * 获取K线每日总数量Key + * @param currencyPair + * @return + */ + public String getCurrencyKDayTotalAmountKey(String currencyPair) { + return MessageFormat.format(CURRENCY_K_DAY_TOTAL_AMOUNT_CACHE, currencyPair); + } + + /*** + * 获取币对配置列表数据Key + * @return + */ + public String getCurrencyConfigListKey() { + return CURRENCY_CONFIG_LIST_CACHE; + } + + /*** + * 获取币对配置列表数据锁key + * @return + */ + public String getCurrencyConfigLockKey() { + return CURRENCY_CONFIG_LOCK_CACHE; + } + + /*** + * 获取K线每日总数量锁Key + * @param currencyPair + * @return + */ + public String getCurrencyKDayAmountLockKey(String currencyPair) { + return MessageFormat.format(CURRENCY_K_DAY_TOTAL_AMOUNT_LOCK_CACHE, currencyPair); + } + + /*** + * 获取Hash数据 + * 通过迭代器封装List返回 + * @param status + * @return List + */ + public List getHashToList(String status) { + //获取Hash的迭代器 + Cursor> scan = redisTemplate.opsForHash().scan(CURRENCY_CONFIG_LIST_CACHE, ScanOptions.NONE); + //返回数据类 + List list = new ArrayList<>(); + //遍历,封装返回数据 + while (scan.hasNext()) { + CurrencyConfig currency = scan.next().getValue(); + //判断状态 + if (!currency.getStatus().equals(status)) { + continue; + } + list.add(currency); + } + return list; + } + + /*** + * 判断是否存在币对配置key是否存在 + * @return + */ + public boolean hasConfigListKey() { + return redisTemplate.hasKey(CURRENCY_CONFIG_LIST_CACHE); + } + + /*** + * 判断是否存在K线数量key是否存在 + * @param currencyPair + * @return + */ + public boolean hasKDayAmountKey(String currencyPair) { + return redisTemplate.hasKey(this.getCurrencyKDayTotalAmountKey(currencyPair)); + } + + /*** + * 添加数据进缓存中 + * @param hashKey + * @param value + */ + public void setHashValue(String redisKey, String hashKey, Object value) { + redisTemplate.opsForHash().put(redisKey, hashKey, value); + } + + + /*** + * 获取K线总数量缓存 + * @param currencyPair + * @param hashKey + * @return + */ + public BigDecimal getHashKDayAmount(String currencyPair, String hashKey) { + return (BigDecimal) redisTemplate.opsForHash().get(this.getCurrencyKDayTotalAmountKey(currencyPair), hashKey); + } + + /*** + * 获取分布式锁 + * @return + */ + public boolean tryFairLock(String redisLockKey) { + return RedissonTool.tryFairLock(redissonClient, redisLockKey, + LOCK_WAIT_TIME, LOCK_LEASE_TIME, TimeUnit.SECONDS); + } + + /*** + * 释放锁 + */ + public void unFairLock(String redisLockKey) { + RedissonTool.unFairLock(redissonClient, redisLockKey); + } +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/redis/MatchConfigCache.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/redis/MatchConfigCache.java new file mode 100644 index 0000000..0558e47 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/redis/MatchConfigCache.java @@ -0,0 +1,88 @@ +package com.blockchain.server.databot.redis; + +import com.blockchain.server.databot.common.redisson.RedissonTool; +import com.blockchain.server.databot.entity.MatchConfig; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.text.MessageFormat; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Component +public class MatchConfigCache { + + @Autowired + private RedisTemplate redisTemplate; + @Autowired + private RedissonClient redissonClient; + + private static final int LOCK_WAIT_TIME = 5; //获取锁等待时间 + private static final int LOCK_LEASE_TIME = 5; //释放时间 + private static final String MATCH_CONFIG_CACHE = "bot:match:config:{0}-{1}"; //撮合机器人配置列表数据key + private static final String MATCH_CONFIG_LOCK_CACHE = "bot:match:config:distributed:lock:{0}-{1}";//撮合机器人配置数据锁key + + /*** + * 获取撮合机器人配置列表数据Key + * @return + */ + public String getKey(String coinName, String unitName) { + return MessageFormat.format(MATCH_CONFIG_CACHE, coinName, unitName); + } + + /*** + * 获取撮合机器人配置数据锁Key + * @param coinName + * @param unitName + * @return + */ + public String getLockKey(String coinName, String unitName) { + return MessageFormat.format(MATCH_CONFIG_LOCK_CACHE, coinName, unitName); + } + + /*** + * 获取撮合机器人配置数据 + * @param key + * @return + */ + public MatchConfig getValue(String key) { + return (MatchConfig) redisTemplate.opsForValue().get(key); + } + + /*** + * 设置撮合机器人配置数据 + * @param matchConfig + */ + public void setValue(String key, MatchConfig matchConfig) { + redisTemplate.opsForValue().set(key, matchConfig); + } + + /*** + * 是否存在Key + * @param key + * @return + */ + public boolean hasKey(String key) { + return redisTemplate.hasKey(key); + } + + /*** + * 获取分布式锁 + * @param lockKey + * @return + */ + public boolean tryFairLock(String lockKey) { + return RedissonTool.tryFairLock(redissonClient, lockKey, + LOCK_WAIT_TIME, LOCK_LEASE_TIME, TimeUnit.SECONDS); + } + + /*** + * 释放锁 + * @param lockKey + */ + public void unFairLock(String lockKey) { + RedissonTool.unFairLock(redissonClient, lockKey); + } +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/redis/PublishOrderCache.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/redis/PublishOrderCache.java new file mode 100644 index 0000000..b64b744 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/redis/PublishOrderCache.java @@ -0,0 +1,170 @@ +package com.blockchain.server.databot.redis; + +import com.blockchain.common.base.dto.MarketDTO; +import com.blockchain.server.databot.common.redisson.RedissonTool; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.stereotype.Component; + +import java.text.MessageFormat; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +@Component +public class PublishOrderCache { + + @Autowired + private RedisTemplate redisTemplate; + @Autowired + private RedissonClient redissonClient; + + private static final int LOCK_WAIT_TIME = 5; //获取锁等待时间 + private static final int LOCK_LEASE_TIME = 5; //释放时间 + private static final String ORDER_LIST_CACHE = "cct:list:order:{0}:{1}-{2}";//本地系统盘口数据key + private static final String ORDER_LOCK_LIST_CACHE = "cct:distributed:lock:order:{0}:{1}-{2}";//本地系统盘口数据Key的分布式锁 + + /*** + * 获取订单列表key + * @param coinName + * @param unitName + * @param orderType + * @return + */ + public static String getOrderListKey(String coinName, String unitName, String orderType) { + return MessageFormat.format(ORDER_LIST_CACHE, orderType, coinName, unitName); + } + + /*** + * 获取订单列表锁key + * @param coinName + * @param unitName + * @param orderType + * @return + */ + public static String getOrderLockListKey(String coinName, String unitName, String orderType) { + return MessageFormat.format(ORDER_LOCK_LIST_CACHE, orderType, coinName, unitName); + } + + /*** + * 根据分数获取sortSet + * @param coinName + * @param unitName + * @param orderType + * @param score + * @return + */ + public Set getZSetByScore(String coinName, String unitName, String orderType, double score) { + return redisTemplate.opsForZSet().rangeByScore(getOrderListKey(coinName, unitName, orderType), score, score); + } + + /*** + * 升序获取sortSet列表 + * @param coinName + * @param unitName + * @param orderType + * @param start + * @param end + * @return + */ + public Set getZSetASC(String coinName, String unitName, String orderType, int start, int end) { + return redisTemplate.opsForZSet().range(getOrderListKey(coinName, unitName, orderType), start, end); + } + + /*** + * 降序获取sorcSet列表 + * @param coinName + * @param unitName + * @param orderType + * @param start + * @param end + * @return + */ + public Set getZSetDESC(String coinName, String unitName, String orderType, int start, int end) { + return redisTemplate.opsForZSet().reverseRange(getOrderListKey(coinName, unitName, orderType), start, end); + } + + /*** + * 设值进sortSet中 + * @param coinName + * @param unitName + * @param orderType + * @param order + * @param score + * @return + */ + public boolean setZSetValue(String coinName, String unitName, String orderType, MarketDTO order, double score) { + return redisTemplate.opsForZSet().add(getOrderListKey(coinName, unitName, orderType), order, score); + } + + /*** + * 设值进sortSet中 + * @param coinName + * @param unitName + * @param orderType + * @param tuples + * @return + */ + public long setZSetValue(String coinName, String unitName, String orderType, Set> tuples) { + return redisTemplate.opsForZSet().add(getOrderListKey(coinName, unitName, orderType), tuples); + } + + /*** + * 根据分数删除sortSet中的对象 + * @param coinName + * @param unitName + * @param orderType + * @param score + * @return + */ + public long removeZSetByScore(String coinName, String unitName, String orderType, double score) { + return redisTemplate.opsForZSet().removeRangeByScore(getOrderListKey(coinName, unitName, orderType), score, score); + } + + /*** + * 根据索引删除sortSet中的对象 + * @param coinName + * @param unitName + * @param orderType + * @param start + * @param end + * @return + */ + public long removeZSetByRange(String coinName, String unitName, String orderType, int start, int end) { + return redisTemplate.opsForZSet().removeRange(getOrderListKey(coinName, unitName, orderType), start, end); + } + + /*** + * 是否存在Key + * @param coinName + * @param unitName + * @param orderType + * @return + */ + public boolean hasKey(String coinName, String unitName, String orderType) { + return redisTemplate.hasKey(getOrderListKey(coinName, unitName, orderType)); + } + + /*** + * 获取分布式锁 + * @param coinName + * @param unitName + * @param orderType + * @return + */ + public boolean tryFairLock(String coinName, String unitName, String orderType) { + return RedissonTool.tryFairLock(redissonClient, getOrderLockListKey(coinName, unitName, orderType), LOCK_WAIT_TIME, LOCK_LEASE_TIME, TimeUnit.SECONDS); + } + + /*** + * 释放锁 + * @param coinName + * @param unitName + * @param orderType + */ + public void unFairLock(String coinName, String unitName, String orderType) { + RedissonTool.unFairLock(redissonClient, getOrderLockListKey(coinName, unitName, orderType)); + } + +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/schedule/KMarketScheduling.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/schedule/KMarketScheduling.java new file mode 100644 index 0000000..f9b729c --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/schedule/KMarketScheduling.java @@ -0,0 +1,605 @@ +package com.blockchain.server.databot.schedule; + +import com.blockchain.common.base.dto.MarketDTO; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.databot.common.constant.CommonConstant; +import com.blockchain.server.databot.common.enums.DataBotEnums; +import com.blockchain.server.databot.common.exception.DataBotExeption; +import com.blockchain.server.databot.dto.rpc.CurrencyMarketDTO; +import com.blockchain.server.databot.entity.CurrencyConfig; +import com.blockchain.server.databot.feign.CctFeign; +import com.blockchain.server.databot.feign.CurrencyFeign; +import com.blockchain.server.databot.redis.CurrencyConfigCache; +import com.blockchain.server.databot.redis.PublishOrderCache; +import com.blockchain.server.databot.service.CurrencyConfigService; +import com.github.pagehelper.PageHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.DefaultTypedTuple; +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Component +public class KMarketScheduling { + + @Autowired + private CurrencyConfigService currencyConfigService; + @Autowired + private PublishOrderCache orderCache; + @Autowired + private CurrencyConfigCache currencyConfigCache; + @Autowired + private CurrencyFeign currencyFeign; + @Autowired + private CctFeign cctFeign; + + private static final Logger LOG = LoggerFactory.getLogger(KMarketScheduling.class); + private static final int DEPTH_TOTAL_AMOUNT = 150; //盘口数据总量 + private static ExecutorService cachedThreadPool = Executors.newFixedThreadPool(100); //异步线程池 + + /*** + * 每一秒创建一次K线数据和深度数据 + */ + @Scheduled(cron = "*/1 * * * * ?") + public void createMarketData() { + //查询需要生成数据的币对 + List currencyConfigs = currencyConfigService.selectByStatus(CommonConstant.YES); + for (CurrencyConfig config : currencyConfigs) { + asyncCreateMarketData(config); + } + + } + + /*** + * 每天凌晨创建一天的K线数量并放入缓存中 + */ + @Scheduled(cron = "0 0 0 * * ?") + public void createKAmount() { + //查询需要设置数量的币对 + List currencyConfigs = currencyConfigService.selectByStatus(CommonConstant.YES); + for (CurrencyConfig currencyConfig : currencyConfigs) { + setUpOneDayKTotalAmount(currencyConfig.getCurrencyPair(), BigDecimal.valueOf(currencyConfig.getKDayTotalAmount())); + } + } + + /*** + * 使用异步线程执行创建数据的操作 + * @param config + */ + private void asyncCreateMarketData(CurrencyConfig config) { + cachedThreadPool.execute(new Runnable() { + @Override + public void run() { + Date start = new Date(); + //判断是定价还是浮动价 + //根据定价创建数据 + if (config.getPriceType().equals(CommonConstant.PRICE)) { + createMarketByPrice(config); + } + //根据浮动价创建 + if (config.getPriceType().equals(CommonConstant.PERCENT)) { + createMarketByPercent(config); + } + Date end = new Date(); + LOG.info("创建K线行情方法消耗时间:" + (end.getTime() - start.getTime()) / 1000); + } + }); + } + + /*** + * 根据定价配置生成K线和深度数据 + */ + private void createMarketByPrice(CurrencyConfig config) { + //创建K线数据 + createKLineByPrice(config); + //创建深度数据 + createDepthByPrice(config); + } + + /*** + * 根据涨跌幅生成K线和深度数据 + */ + private void createMarketByPercent(CurrencyConfig config) { + //查询最新行情 + CurrencyMarketDTO market = getCurrencyMarket(config.getCurrencyPair()); + //行情对象不为空,再进行创建数据操作 + if (market != null) { + //创建K线数据 + BigDecimal newPrice = createKLineByPercent(market, config); + //设置新创单价,作为深度数据的单价标准 + market.setAmount(newPrice); + //创建深度数据 + createDepthByPercent(market, config); + } + } + + /*** + * 根据涨跌幅创建K线图数据 + * @param market + * @param config + */ + private BigDecimal createKLineByPercent(CurrencyMarketDTO market, CurrencyConfig config) { + //日涨跌幅 + float changePercent = config.getKChangePercent(); + //涨跌最大幅度(偏差) + float maxChangePercent = config.getKMaxChangePercent(); + //当前最新行情的涨跌幅 + float nowChangePercent = market.getPercent(); + //新创涨跌幅 + float newChangePercent = 0f; + //随机数对象 + Random random = new Random(); + //当前时间 + Date now = new Date(); + //当前小时数 + int hour = getNowDate(now, Calendar.HOUR_OF_DAY); + int minute = getNowDate(now, Calendar.MINUTE); + //当前时间是当天最后四小时 + if ((hour == 13 || hour == 15 || hour == 20 || hour == 21 || hour == 22 || hour == 23) && minute / 15 % 2 == 0) { + //新创涨跌幅 - 当前最新行情涨跌幅 * 随机数 / 定数 + newChangePercent = (changePercent - nowChangePercent) * (float) random.nextInt(1000) / 2880000.0F; + //当前时间秒数 + int second = getNowDate(now, Calendar.SECOND); + //当前时间 大于5秒并且小于15秒 或者 大于50秒并且小于60秒 + if (second > 5 && second < 15 || second > 50 && second < 60) { + newChangePercent = -newChangePercent; + } + } else { + //随机一个布尔值 + boolean randomFlag = random.nextBoolean(); + //如果 最新行情涨跌幅 < 涨跌最大幅度 && (随机布尔值的为真 或者 最新行情涨跌幅 < 取反的涨跌最大幅度) + if (!randomFlag || (changePercent > 0 && nowChangePercent < -maxChangePercent) + || (changePercent < 0 && nowChangePercent < maxChangePercent)) { + //新创涨跌幅 = 随机数 * 涨跌最大幅度 / 定数 + newChangePercent = random.nextFloat() * maxChangePercent / 300; + } else { + //新创涨跌幅 = -随机数 * 涨跌最大幅度 / 定数 + newChangePercent = -random.nextFloat() * maxChangePercent / 300; + } + } + + //新创成交单价 = 行情最新成交 + (行情最新成交 * 新创涨跌幅) + BigDecimal price = market.getAmount().add(market.getAmount().multiply(BigDecimal.valueOf(newChangePercent))).setScale(16, BigDecimal.ROUND_DOWN); + //新创成交数量 + BigDecimal amount = getKAmount(String.valueOf(getNowSecond(now)), config.getCurrencyPair(), config.getKDayTotalAmount()); + //币对,根据索引取:0为coinName币种,1为unitName单位 + String[] currencyPair = config.getCurrencyPair().split("-"); + + //异步线程调用行情插入数据 + asyncCurrencySave(currencyPair[0], currencyPair[1], price, amount, now.getTime(), random.nextBoolean()); + + //返回最新单价 + return price; + } + + /*** + * 根据定价创建K线图数据 + * @param config + * @return + */ + private BigDecimal createKLineByPrice(CurrencyConfig config) { + Date now = new Date(); + Random random = new Random(); + //新创成交单价 + BigDecimal price = randomNum(config.getKMinPrice(), config.getKMaxPrice()); + //新创成交数量 + BigDecimal amount = getKAmount(String.valueOf(getNowSecond(now)), config.getCurrencyPair(), config.getKDayTotalAmount()); + //币对,根据索引取:0为coinName币种,1为unitName单位 + String[] currencyPair = config.getCurrencyPair().split("-"); + //异步线程调用行情插入数据 + asyncCurrencySave(currencyPair[0], currencyPair[1], price, amount, now.getTime(), random.nextBoolean()); + //返回最新单价 + return price; + } + + /*** + * 根据定价范围创建深度图数据 + * @param config + */ + private void createDepthByPrice(CurrencyConfig config) { + //币种 + String coinName = config.getCurrencyPair().split("-")[0]; + //单位 + String unitName = config.getCurrencyPair().split("-")[1]; + + //设置买盘数据 + setDepthByPriceInRedis(config, CommonConstant.BUY, coinName, unitName); + //设置卖盘数据 + setDepthByPriceInRedis(config, CommonConstant.SELL, coinName, unitName); + } + + /*** + * 根据涨跌幅创建深度图数据 + * @param market + * @param config + */ + private void createDepthByPercent(CurrencyMarketDTO market, CurrencyConfig config) { + //币种 + String coinName = config.getCurrencyPair().split("-")[0]; + //单位 + String unitName = config.getCurrencyPair().split("-")[1]; + //当前最新单价 + float nowPrice = market.getAmount().floatValue(); + + //设置买盘数据 + setDepthByPercentInRedis(config, CommonConstant.BUY, coinName, unitName, nowPrice); + //设置卖盘数据 + setDepthByPercentInRedis(config, CommonConstant.SELL, coinName, unitName, nowPrice); + } + + /*** + * 封装定价范围生成的深度图设值进缓存中 + * @param config + * @param tradingType + * @param coinName + * @param unitName + */ + private void setDepthByPriceInRedis(CurrencyConfig config, String tradingType, String coinName, String unitName) { + //发行总数量 + float totalAmount = getTotalAmount(config, tradingType); + //盘口每条数据的数量平均值 + float avgAmount = totalAmount / DEPTH_TOTAL_AMOUNT; + //随机数对象 + Random random = new Random(); + //新创盘口数据集合 + Map newPrices = new HashMap<>(); + //创建数据 + while (totalAmount > 0) { + //新创盘口单价 + BigDecimal depthPrice = BigDecimal.ZERO; + //根据买卖的最低最高范围生成单价 + if (tradingType.equals(CommonConstant.BUY)) { + depthPrice = randomNum(config.getBuyMinPrice(), config.getBuyMaxPrice()); + } + if (tradingType.equals(CommonConstant.SELL)) { + depthPrice = randomNum(config.getSellMinPrice(), config.getSellMaxPrice()); + } + + //返回格式化后的单价 + float unitPrice = formatValue(depthPrice.floatValue()); + //判断是否存在相同单价 + if (newPrices.containsKey(unitPrice)) { + continue; + } else { + //新创盘口数量 + float newAmount = avgAmount * random.nextInt(100) / 50; + //减去发行总数量 + totalAmount -= newAmount; + + //构建盘口数据对象 + MarketDTO marketDTO = new MarketDTO(new BigDecimal(unitPrice + ""), new BigDecimal(newAmount + ""), + new BigDecimal(newAmount + ""), unitName, coinName, tradingType); + + //将新创单价添加进map中 + newPrices.put(unitPrice, marketDTO); + } + } + + //设置深度数据到缓存中 + setDepthInRedis(newPrices, coinName, unitName, tradingType); + + //发送推送前端请求 + send(coinName, unitName); + } + + /*** + * 封装涨跌幅生成的深度图设值进缓存中 + * @param config + * @param tradingType + * @param coinName + * @param unitName + * @param nowPrice + */ + private void setDepthByPercentInRedis(CurrencyConfig config, String tradingType, String coinName, String unitName, + float nowPrice) { + //发行总数量 + float totalAmount = getTotalAmount(config, tradingType); + //盘口每条数据的单价平均差 + float avgPriceDifference = getAvgPriceDifference(config, tradingType, nowPrice); + //盘口每条数据的数量平均值 + float avgAmount = totalAmount / DEPTH_TOTAL_AMOUNT; + //随机数对象 + Random random = new Random(); + //新创盘口数据集合 + Map newPrices = new HashMap<>(); + //累加的深度价格 + float depthPrice = nowPrice; + //创建数据 + while (totalAmount > 0) { + //新创盘口单价 = 单价平均差 * 随机数 / 定数 + float newPirce = avgPriceDifference * random.nextInt(10) / 5; + //根据买卖判断加或减 + if (checkBuyOrSell(tradingType)) { + depthPrice -= newPirce; + //如果累减的单价过小,跳出循环 + if (depthPrice < nowPrice - nowPrice * config.getBuyPricePercent()) { + break; + } + } else { + depthPrice += newPirce; + //如果累减的单价过大,跳出循环 + if (depthPrice > nowPrice + nowPrice * config.getSellPricePercent()) { + break; + } + } + //格式化数据 + float unitPrice = formatValue(depthPrice); + //判断是否存在相同单价 + if (newPrices.containsKey(unitPrice)) { + continue; + } else { + //新创盘口数量 + float newAmount = avgAmount * random.nextInt(100) / 50; + //减去发行总数量 + totalAmount -= newAmount; + + //构建盘口数据对象 + MarketDTO marketDTO = new MarketDTO(new BigDecimal(unitPrice + ""), new BigDecimal(newAmount + ""), + new BigDecimal(newAmount + ""), unitName, coinName, tradingType); + + //将新创单价添加进map中 + newPrices.put(unitPrice, marketDTO); + } + } + + //设置深度数据到缓存中 + setDepthInRedis(newPrices, coinName, unitName, tradingType); + + //发送推送前端请求 + send(coinName, unitName); + } + + /*** + * 设置深度数据到缓存中 + * @param newPrices + * @param coinName + * @param unitName + * @param tradingType + */ + private void setDepthInRedis(Map newPrices, String coinName, String unitName, String + tradingType) { + //缓存中的sortSet集合 + Set> tuples = new HashSet(); + //封装sortSet结构的数据 + for (Map.Entry order : newPrices.entrySet()) { + double price = order.getValue().getUnitPrice().doubleValue(); + //redis的sortSet对象 + ZSetOperations.TypedTuple tuple = new DefaultTypedTuple(order.getValue(), price); + //将对象添加进sortSet中 + tuples.add(tuple); + } + + boolean lockFlag = false; + //获取锁并更新缓存 + while (!lockFlag) { + lockFlag = orderCache.tryFairLock(coinName, unitName, tradingType); + //获取锁成功 + if (lockFlag) { + //删除集合中所有数据 + orderCache.removeZSetByRange(coinName, unitName, tradingType, 0, -1); + //将新数据设置进缓存中 + orderCache.setZSetValue(coinName, unitName, tradingType, tuples); + //释放锁 + orderCache.unFairLock(coinName, unitName, tradingType); + } + } + } + + /*** + * 根据交易方向返回发行总量 + * @param config + * @param tradingType + * @return + */ + private float getTotalAmount(CurrencyConfig config, String tradingType) { + if (checkBuyOrSell(tradingType)) { + return config.getBuyTotalAmount(); + } else { + return config.getSellTotalAmount(); + } + } + + /*** + * 根据交易方向返回单价平均差 + * @param config + * @param tradingType + * @param nowPrice + * @return + */ + private float getAvgPriceDifference(CurrencyConfig config, String tradingType, float nowPrice) { + if (checkBuyOrSell(tradingType)) { + return nowPrice * config.getBuyPricePercent() / DEPTH_TOTAL_AMOUNT; + } else { + return nowPrice * config.getSellPricePercent() / DEPTH_TOTAL_AMOUNT; + } + } + + /*** + * 判断交易方向 + * @param tradingType + * @return 买入true, 卖出false + */ + private boolean checkBuyOrSell(String tradingType) { + if (tradingType.equals(CommonConstant.BUY)) { + return true; + } + if (tradingType.equals(CommonConstant.SELL)) { + return false; + } + throw new DataBotExeption(DataBotEnums.UNKNOWN_TRADING_TYPE); + } + + /*** + * 获取当前时间数据 + * @param calendarConstant + * @return + */ + private int getNowDate(Date now, int calendarConstant) { + Calendar calendar = new GregorianCalendar(); + calendar.setTime(now); + return calendar.get(calendarConstant); + } + + /*** + * 获取当前秒数(一天的秒数为86399s) + * @param now + * @return + */ + private int getNowSecond(Date now) { + try { + SimpleDateFormat sdfOne = new SimpleDateFormat("yyyy-MM-dd"); + //当前时间毫秒数 - 凌晨0点毫秒数 / 1000 = 当前秒数 + long overTime = (now.getTime() - (sdfOne.parse(sdfOne.format(now)).getTime())) / 1000; + return (int) overTime; + } catch (Exception e) { + return new Random().nextInt(86399); + } + } + + /*** + * 获取数字货币最新行情 + * @param currencyPair + * @return + */ + private CurrencyMarketDTO getCurrencyMarket(String currencyPair) { + ResultDTO resultDTO = currencyFeign.get(currencyPair); + return resultDTO.getData(); + } + + /*** + * 当前秒数作为Key获取K线数量 + * @param nowSecond + * @param currencyPair + * @param kTotalAmount + * @return + */ + private BigDecimal getKAmount(String nowSecond, String currencyPair, float kTotalAmount) { + //是否存在缓存 + boolean hasKey = currencyConfigCache.hasKDayAmountKey(currencyPair); + //如果不存在Key或者缓存中为空 + if (!hasKey) { + //重新生成数据 + setUpOneDayKTotalAmount(currencyPair, BigDecimal.valueOf(kTotalAmount)); + } + //从缓存中获取数据 + return currencyConfigCache.getHashKDayAmount(currencyPair, nowSecond); + } + + /*** + * 使用异步线程执行行情Feign调用 + * @param coinName + * @param unitName + * @param price + * @param amount + * @param nowTime + * @param randomFlag + */ + private void asyncCurrencySave(String coinName, String unitName, BigDecimal price, BigDecimal amount, + long nowTime, boolean randomFlag) { + //插入新创K线数据 + currencyFeign.save(coinName, unitName, price, amount, nowTime, randomFlag ? CommonConstant.BUY : CommonConstant.SELL); + } + + /*** + * 推送前端消息 + * @param coinName + * @param unitName + */ + private void send(String coinName, String unitName) { + //发送推送前端请求 + cctFeign.send(coinName, unitName); + } + + /**** + * 将K线每天总量分配到每天每一秒 + * @param currencyPair + * @param totalAmount + */ + private void setUpOneDayKTotalAmount(String currencyPair, BigDecimal totalAmount) { + //锁key + String lockKey = currencyConfigCache.getCurrencyKDayAmountLockKey(currencyPair); + //锁标识 + boolean lockFlag = false; + //循环获取锁 + while (!lockFlag) { + lockFlag = currencyConfigCache.tryFairLock(lockKey); + //获取成功 + if (lockFlag) { + //创建每天每天深度数量,设置进缓存中 + createOneDayKTotalAmount(currencyPair, totalAmount); + //释放锁 + currencyConfigCache.unFairLock(lockKey); + } + } + } + + /*** + * 循环创建每日每秒数据并设置进缓存中 + * @param currencyPair + * @param totalAmount + */ + private void createOneDayKTotalAmount(String currencyPair, BigDecimal totalAmount) { + //每日交易总量 + Random random = new Random(); + //记录每秒 + int nowSecond = 0; + //每小时随机分配总量 + for (int hour = 0; hour < 24; hour++) { + BigDecimal hourAmount = totalAmount.multiply(BigDecimal.valueOf(random.nextInt(100))) + .divide(BigDecimal.valueOf(1200), 8, BigDecimal.ROUND_DOWN); + //随机出每秒的分配总量 + //每分钟随机分配总量 + for (int minute = 0; minute < 60; minute++) { + BigDecimal minuteAmount = hourAmount.multiply(BigDecimal.valueOf(random.nextInt(100))) + .divide(BigDecimal.valueOf(3000), 8, BigDecimal.ROUND_DOWN); + //随机出每秒的分配总量 + for (int second = 0; second < 60; second++) { + BigDecimal secondAmount = minuteAmount.multiply(BigDecimal.valueOf(random.nextInt(100))) + .divide(BigDecimal.valueOf(3000), 8, BigDecimal.ROUND_DOWN); + + //缓存中的key + String totalAmountKey = currencyConfigCache.getCurrencyKDayTotalAmountKey(currencyPair); + //自增记录秒数 + nowSecond += 1; + //将每一秒的单价设置进缓存中 + currencyConfigCache.setHashValue(totalAmountKey, nowSecond + "", secondAmount); + } + } + } + } + + /*** + * 格式化数据小数 + * @param value + * @return + */ + private static float formatValue(float value) { + if (value < 0.001) { + return BigDecimal.valueOf(value).setScale(8, BigDecimal.ROUND_HALF_DOWN).floatValue(); + } else if (value < 0.1) { + return BigDecimal.valueOf(value).setScale(6, BigDecimal.ROUND_HALF_DOWN).floatValue(); + } else if (value < 10) { + return BigDecimal.valueOf(value).setScale(4, BigDecimal.ROUND_HALF_DOWN).floatValue(); + } else { + return BigDecimal.valueOf(value).setScale(2, BigDecimal.ROUND_HALF_DOWN).floatValue(); + } + } + + /*** + * 根据范围随机生成小数 + * @param min + * @param max + * @return + */ + public static BigDecimal randomNum(Float min, Float max) { + return new BigDecimal(Math.random() * (max - min) + min); + } +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/schedule/MatchScheduling.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/schedule/MatchScheduling.java new file mode 100644 index 0000000..2ba66a1 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/schedule/MatchScheduling.java @@ -0,0 +1,137 @@ +package com.blockchain.server.databot.schedule; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.databot.common.constant.CommonConstant; +import com.blockchain.server.databot.dto.rpc.CurrencyMarketDTO; +import com.blockchain.server.databot.dto.rpc.PublishOrderDTO; +import com.blockchain.server.databot.entity.CurrencyConfig; +import com.blockchain.server.databot.entity.MatchConfig; +import com.blockchain.server.databot.feign.CctFeign; +import com.blockchain.server.databot.feign.CurrencyFeign; +import com.blockchain.server.databot.service.CurrencyConfigService; +import com.blockchain.server.databot.service.MatchConfigService; +import com.blockchain.server.databot.service.MatchService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; + +import java.math.BigDecimal; +import java.util.List; + +//@Component +public class MatchScheduling { + + @Autowired + private CurrencyConfigService currencyConfigService; + @Autowired + private MatchConfigService matchConfigService; + @Autowired + private MatchService matchService; + @Autowired + private CurrencyFeign currencyFeign; + @Autowired + private CctFeign cctFeign; + + @Scheduled(cron = "*/15 * * * * ?") + public void matchRobot() { + //查询行情机器人可用币种列表 + List configs = currencyConfigService.selectByStatus(CommonConstant.YES); + for (CurrencyConfig config : configs) { + //分割币对为数组,索引0是coinName、1是unitName + String[] currencyPair = config.getCurrencyPair().split("-"); + String coinName = currencyPair[0]; + String unitName = currencyPair[1]; + + //查询撮合机器人配置 + MatchConfig matchConfig = matchConfigService.getStatusIsY(coinName, unitName); + //不等于空时 + if (matchConfig != null) { + //计算撮合的单价范围,并且进行撮合 + computeRange(matchConfig); + } + } + } + + /*** + * 计算撮合单价范围 + * @param matchConfig + */ + private void computeRange(MatchConfig matchConfig) { + //最低价 + BigDecimal minPrice = BigDecimal.ZERO; + //最高价 + BigDecimal maxPrice = BigDecimal.ZERO; + + //如果单价类型是定价范围,获取最低最高定价 + if (matchConfig.getPriceType().equals(CommonConstant.PRICE)) { + minPrice = matchConfig.getMinPrice(); + maxPrice = matchConfig.getMaxPrice(); + } + + //如果单价类型是最新成交价波动范围,获取最新行情单价并设置最低最高的波动单价 + if (matchConfig.getPriceType().equals(CommonConstant.PERCENT)) { + //查询最新行情 + CurrencyMarketDTO currency = getCurrency(matchConfig.getCoinName(), matchConfig.getUnitName()); + //行情不为空 + if (currency != null) { + //最新成交价 + BigDecimal amount = currency.getAmount(); + //最低单价 = 最新成交价 - (最新成交价 * 最低跌幅) + minPrice = amount.subtract(amount.multiply(matchConfig.getMinPercent())); + //最高单价 = 最新成交价 + (最新成交价 * 最高涨幅) + maxPrice = amount.add(amount.multiply(matchConfig.getMaxPercent())); + } + } + + //防止初始化最大最小单价失败 + if (minPrice.compareTo(BigDecimal.ZERO) != 0 + && maxPrice.compareTo(BigDecimal.ZERO) != 0) { + //查询未撮合订单,并生成对手订单进行撮合 + iterationMatch(matchConfig.getUserId(), matchConfig.getCoinName(), matchConfig.getUnitName(), minPrice, maxPrice); + } + + } + + /*** + * 查询未撮合订单,循环并生成对手订单进行撮合 + * + * @param userId + * @param coinName + * @param unitName + * @param minPrice + * @param maxPrice + */ + private void iterationMatch(String userId, String coinName, String unitName, BigDecimal minPrice, BigDecimal maxPrice) { + //查询单价范围内没撮合的订单 + List bymatchs = listBeMatchOrderToBot(coinName, unitName, minPrice, maxPrice); + //循环发布对应的订单并且撮合 + for (PublishOrderDTO bymatch : bymatchs) { + //开始撮合 + matchService.match(userId, bymatch); + } + } + + /*** + * 获取最新行情 + * @param coinName + * @param unitName + * @return + */ + private CurrencyMarketDTO getCurrency(String coinName, String unitName) { + ResultDTO resultDTO = currencyFeign.get(coinName + "-" + unitName); + return resultDTO.getData(); + } + + /*** + * 查询单价范围内没撮合的订单 + * @param coinName + * @param unitName + * @param minPrice + * @param maxPrice + * @return + */ + private List listBeMatchOrderToBot(String coinName, String unitName, + BigDecimal minPrice, BigDecimal maxPrice) { + ResultDTO> resultDTO = cctFeign.listBeMatchOrderToBot(coinName, unitName, minPrice, maxPrice); + return resultDTO.getData(); + } +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/CurrencyConfigService.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/CurrencyConfigService.java new file mode 100644 index 0000000..57ae9f2 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/CurrencyConfigService.java @@ -0,0 +1,29 @@ +package com.blockchain.server.databot.service; + +import com.blockchain.server.databot.entity.CurrencyConfig; + +import java.util.List; + +public interface CurrencyConfigService { + + /*** + * 根据币对查询币种信息 + * @param currencyPair + * @return + */ + CurrencyConfig selectByCurrencyPair(String currencyPair); + + /*** + * 根据状态查询币种信息 + * @return + */ + List selectByStatus(String status); + + /*** + * 根据币对查询币种信息,判断币种是否可用 + * true为可用 + * @param currencyPair + * @return + */ + boolean checkCurrencyPairStatusIsY(String currencyPair); +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/MatchConfigService.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/MatchConfigService.java new file mode 100644 index 0000000..4528162 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/MatchConfigService.java @@ -0,0 +1,14 @@ +package com.blockchain.server.databot.service; + +import com.blockchain.server.databot.entity.MatchConfig; + +import java.util.List; + +public interface MatchConfigService { + + /*** + * 查询状态为可用的撮合配置 + * @return + */ + MatchConfig getStatusIsY(String coinName, String unitName); +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/MatchService.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/MatchService.java new file mode 100644 index 0000000..adf07b2 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/MatchService.java @@ -0,0 +1,14 @@ +package com.blockchain.server.databot.service; + +import com.blockchain.server.databot.dto.rpc.PublishOrderDTO; + +public interface MatchService { + + /*** + * 撮合机器人撮合的业务逻辑 + * @param matchUserId + * @param bymatch + * @return + */ + boolean match(String matchUserId, PublishOrderDTO bymatch); +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/impl/CurrencyConfigServiceImpl.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/impl/CurrencyConfigServiceImpl.java new file mode 100644 index 0000000..732b17b --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/impl/CurrencyConfigServiceImpl.java @@ -0,0 +1,73 @@ +package com.blockchain.server.databot.service.impl; + +import com.blockchain.server.databot.common.constant.CommonConstant; +import com.blockchain.server.databot.common.enums.DataBotEnums; +import com.blockchain.server.databot.common.exception.DataBotExeption; +import com.blockchain.server.databot.entity.CurrencyConfig; +import com.blockchain.server.databot.mapper.CurrencyConfigMapper; +import com.blockchain.server.databot.redis.CurrencyConfigCache; +import com.blockchain.server.databot.service.CurrencyConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class CurrencyConfigServiceImpl implements CurrencyConfigService { + + @Autowired + private CurrencyConfigMapper currencyConfigMapper; + @Autowired + private CurrencyConfigCache currencyConfigCache; + + @Override + public CurrencyConfig selectByCurrencyPair(String currencyPair) { + return currencyConfigMapper.selectByCurrencyPair(currencyPair); + } + + @Override + public List selectByStatus(String status) { + boolean hasKey = currencyConfigCache.hasConfigListKey(); + //是否存在缓存 + if (hasKey) { + //存在,从缓存中获取并返回 + return currencyConfigCache.getHashToList(status); + } else { + //不存在 + List configs = currencyConfigMapper.selectByStatus(status); + //锁key + String lockKey = currencyConfigCache.getCurrencyConfigLockKey(); + //是否成功获取锁 + boolean lockFlag = false; + //循环获取锁 + while (!lockFlag) { + lockFlag = currencyConfigCache.tryFairLock(lockKey); + //获取成功 + if (lockFlag) { + //查询数据库并将数据放入缓存中 + for (CurrencyConfig config : configs) { + currencyConfigCache.setHashValue(currencyConfigCache.getCurrencyConfigListKey(), + config.getCurrencyPair(), config); + } + //释放锁 + currencyConfigCache.unFairLock(lockKey); + } + } + return configs; + } + } + + @Override + public boolean checkCurrencyPairStatusIsY(String currencyPair) { + CurrencyConfig config = selectByCurrencyPair(currencyPair); + if (config.getStatus().equals(CommonConstant.YES)) { + return true; + } + if (config.getStatus().equals(CommonConstant.NO)) { + return false; + } + throw new DataBotExeption(DataBotEnums.UNKNOWN_STATUS); + } + + +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/impl/MatchConfigServiceImpl.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/impl/MatchConfigServiceImpl.java new file mode 100644 index 0000000..561c4a1 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/impl/MatchConfigServiceImpl.java @@ -0,0 +1,57 @@ +package com.blockchain.server.databot.service.impl; + +import com.blockchain.server.databot.common.constant.CommonConstant; +import com.blockchain.server.databot.entity.MatchConfig; +import com.blockchain.server.databot.mapper.MatchConfigMapper; +import com.blockchain.server.databot.redis.MatchConfigCache; +import com.blockchain.server.databot.service.MatchConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class MatchConfigServiceImpl implements MatchConfigService { + + @Autowired + private MatchConfigMapper matchConfigMapper; + @Autowired + private MatchConfigCache matchConfigCache; + + @Override + public MatchConfig getStatusIsY(String coinName, String unitName) { + //数据key + String key = matchConfigCache.getKey(coinName, unitName); + + //查询缓存是否存在Key + boolean flag = matchConfigCache.hasKey(key); + + if (flag) { + //存在-直接返回缓存数据 + return matchConfigCache.getValue(key); + } else { + //不存在-查询数据库并设置进缓存中 + MatchConfig matchConfig = matchConfigMapper.selectByCoinAndUnitAndStauts(coinName, unitName, CommonConstant.YES); + + //不等于空时,存入缓存中 + if (matchConfig != null) { + //获取锁标识 + boolean lockFlag = false; + //循环获取锁 + while (!lockFlag) { + //锁key + String lockKey = matchConfigCache.getLockKey(coinName, unitName); + //获取锁 + lockFlag = matchConfigCache.tryFairLock(lockKey); + //获取成功 + if (lockFlag) { + matchConfigCache.setValue(key, matchConfig); + //释放锁 + matchConfigCache.unFairLock(lockKey); + } + } + } + + //返回数据 + return matchConfig; + } + } +} diff --git a/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/impl/MatchServiceImpl.java b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/impl/MatchServiceImpl.java new file mode 100644 index 0000000..56c81e0 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/java/com/blockchain/server/databot/service/impl/MatchServiceImpl.java @@ -0,0 +1,87 @@ +package com.blockchain.server.databot.service.impl; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.databot.common.constant.CommonConstant; +import com.blockchain.server.databot.dto.rpc.PublishOrderDTO; +import com.blockchain.server.databot.feign.CctFeign; +import com.blockchain.server.databot.service.MatchService; +import com.codingapi.tx.annotation.ITxTransaction; +import com.codingapi.tx.annotation.TxTransaction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; + +@Service +public class MatchServiceImpl implements MatchService, ITxTransaction { + + @Autowired + private CctFeign cctFeign; + + @Override + @Transactional + @TxTransaction(isStart = true) + public boolean match(String matchUserId, PublishOrderDTO bymatch) { + BigDecimal totalNum = bymatch.getLastNum(); + BigDecimal price = bymatch.getUnitPrice(); + String coinName = bymatch.getCoinName(); + String unitName = bymatch.getUnitName(); + String matchId; + + //生成对应的订单 + if (bymatch.getOrderType().equals(CommonConstant.BUY)) { + //被撮合订单是买单,发布一条对应的卖单 + matchId = handleLimitSellToBot(matchUserId, coinName, unitName, totalNum, price); + } else if (bymatch.getOrderType().equals(CommonConstant.SELL)) { + //被撮合订单是卖单,发布一条对应的买单 + matchId = handleLimitBuyToBot(matchUserId, coinName, unitName, totalNum, price); + } else { + return false; + } + + //调用撮合方法 + return handleMatchToBot(matchId, bymatch.getId()); + } + + /*** + * 发布限价买单 - 用于撮合机器人 + * @param userId + * @param coinName + * @param unitName + * @param totalNum + * @param price + * @return + */ + private String handleLimitBuyToBot(String userId, String coinName, String unitName, + BigDecimal totalNum, BigDecimal price) { + ResultDTO resultDTO = cctFeign.handleLimitBuyToBot(userId, coinName, unitName, totalNum, price); + return resultDTO.getData(); + } + + /*** + * 发布限价卖单 - 用于撮合机器人 + * @param userId + * @param coinName + * @param unitName + * @param totalNum + * @param price + * @return + */ + private String handleLimitSellToBot(String userId, String coinName, String unitName, + BigDecimal totalNum, BigDecimal price) { + ResultDTO resultDTO = cctFeign.handleLimitSellToBot(userId, coinName, unitName, totalNum, price); + return resultDTO.getData(); + } + + /*** + * 撮合 - 用于撮合机器人 + * @param matchId + * @param bymatchId + * @return + */ + private Boolean handleMatchToBot(String matchId, String bymatchId) { + ResultDTO resultDTO = cctFeign.handleMatchToBot(matchId, bymatchId); + return resultDTO.getData(); + } +} diff --git a/blockchain-server/blockchain-server-databot/src/main/resources/application.yml b/blockchain-server/blockchain-server-databot/src/main/resources/application.yml new file mode 100644 index 0000000..95aad49 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/resources/application.yml @@ -0,0 +1,3 @@ +#日志配置路径 +logging: + config: classpath:logback/logback-${spring.cloud.config.profile}.xml \ No newline at end of file diff --git a/blockchain-server/blockchain-server-databot/src/main/resources/bootstrap.yml b/blockchain-server/blockchain-server-databot/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..b92e1c1 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/resources/bootstrap.yml @@ -0,0 +1,22 @@ +server: + port: 8901 +#注册中心 +eureka: + client: + service-url: + defaultZone: http://eureka:8001/eureka/ + instance: + prefer-ip-address: true + instance-id: ${spring.cloud.client.ip-address}:${server.port} + hostname: ${spring.cloud.client.ip-address} +spring: + cloud: + #配置中心 + config: + discovery: + service-id: dapp-config-server + enabled: true + profile: dev + name: springconf,springcloudconf,redisconf,dbconf,txconf,xssconf,ipconf + application: + name: dapp-databot-server diff --git a/blockchain-server/blockchain-server-databot/src/main/resources/logback/logback-dev.xml b/blockchain-server/blockchain-server-databot/src/main/resources/logback/logback-dev.xml new file mode 100644 index 0000000..9a0e324 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/resources/logback/logback-dev.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-databot/src/main/resources/logback/logback-pro.xml b/blockchain-server/blockchain-server-databot/src/main/resources/logback/logback-pro.xml new file mode 100644 index 0000000..5b55059 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/resources/logback/logback-pro.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-databot/src/main/resources/mapper/CurrencyConfigMapper.xml b/blockchain-server/blockchain-server-databot/src/main/resources/mapper/CurrencyConfigMapper.xml new file mode 100644 index 0000000..5eb4c16 --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/resources/mapper/CurrencyConfigMapper.xml @@ -0,0 +1,43 @@ + + + + + bot_currency_config + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-databot/src/main/resources/mapper/MatchConfigMapper.xml b/blockchain-server/blockchain-server-databot/src/main/resources/mapper/MatchConfigMapper.xml new file mode 100644 index 0000000..1c5d78b --- /dev/null +++ b/blockchain-server/blockchain-server-databot/src/main/resources/mapper/MatchConfigMapper.xml @@ -0,0 +1,33 @@ + + + + + bot_match_config + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/pom.xml b/blockchain-server/blockchain-server-eos/pom.xml new file mode 100644 index 0000000..f1589b6 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/pom.xml @@ -0,0 +1,57 @@ + + + + blockchain-server + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + blockchain-server-eos + + + com.blockchain + blockchain-server-base + 1.0-SNAPSHOT + + + com.blockchain + blockchain-common-tx + 1.0-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + com.squareup.retrofit2 + retrofit + 2.4.0 + + + com.squareup.retrofit2 + converter-jackson + 2.4.0 + + + junit + junit + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/EosApplication.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/EosApplication.java new file mode 100644 index 0000000..caa8b7e --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/EosApplication.java @@ -0,0 +1,21 @@ +package com.blockchain.server.eos; + +import com.blockchain.server.base.BaseConf; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author huangxl + * @create 2018-11-13 10:15 + */ +@SpringBootApplication(scanBasePackageClasses = {BaseConf.class, EosApplication.class}) +@RestController +public class EosApplication { + public static void main(String[] args) { + SpringApplication.run(EosApplication.class,args); + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/config/InterceptorConf.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/config/InterceptorConf.java new file mode 100644 index 0000000..37681c2 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/config/InterceptorConf.java @@ -0,0 +1,32 @@ +package com.blockchain.server.eos.common.config; + +import com.blockchain.server.base.interceptor.LoginInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class InterceptorConf implements WebMvcConfigurer { + @Bean + public LoginInterceptor loginInterceptor() { + return new LoginInterceptor(); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(loginInterceptor()) + .addPathPatterns("/**") + .excludePathPatterns("/inner/**") + .excludePathPatterns("/webjars/**") + .excludePathPatterns("/swagger-ui.html") + .excludePathPatterns("/swagger-resources/**") + .excludePathPatterns("/v2/api-docs/**"); + } + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**").allowedOrigins("*"); + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/constant/EosConstant.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/constant/EosConstant.java new file mode 100644 index 0000000..8274747 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/constant/EosConstant.java @@ -0,0 +1,90 @@ +package com.blockchain.server.eos.common.constant; + +/** + * @author Harvey + * @date 2019/2/19 15:17 + * @user WIN10 + */ +public class EosConstant { + + public static final String EOS_TOKEN_NAME = "eosio.token"; + public static final String EOS_TOKEN_TYPE = "eos"; + + public static class EosGasConfig { + public static final String EOS_GAS_PRIFIX = "eos_gas_config_"; + public static final String EOS_GAS_STATUS_USABLE = "1"; + public static final String EOS_GAS_STATUS_FORBIDDEN = "0"; + } + + public static class RedisKey { + public static final String EOS_BITMAP_KEY = "eos:blockchain:bitmap"; + public static final String EOS_BLOCKCHAIN_BEGIN = "eos:blockchain:begin"; + public static final String EOS_BLOCKCHAIN_CURRENT = "eos:blockchain:current"; + public static final String EOS_BLOCKCHAIN_OPT_HASH = "eos:blockchain:opt:hash"; + + } + + public static class BlockNumber { + // 区块状态处理失败 + public static final Character EOS_BLOCK_NUMBER_ERROR = 'N'; + // 区块状态处理成功 + public static final Character EOS_BLOCK_NUMBER_SUCCESS = 'Y'; + // 区块状态等待处理 + public static final Character EOS_BLOCK_NUMBER_WAIT = 'W'; + } + + public static class WalletInStatus { + // 充值钱包账户可用 + public static final Integer EOS_WALLET_IN_USABLE = 1; + // 充值钱包账户禁用 + public static final Integer EOS_WALLET_IN_FORBIDDEN = 0; + } + + public static class TransferType { + /** + * 提现 + */ + public static final String TRANSFER_OUT = "OUT"; + /** + * 充值 + */ + public static final String TRANSFER_IN = "IN"; + /** + * 币币交易 + */ + public static final String TRANSFER_CCT = "CCT"; + /** + * 手续费 + */ + public static final String TRANSFER_GAS = "GAS"; + + //交易类型 转内快速转账 + public static final String TYPE_FAST = "FAST"; + } + + public static class TransferStatus { + // 失败 + public static final Integer DEFEATED = 0; + // 成功 + public static final Integer SUCCESS = 1; + // 待初审提币 + public static final Integer FIRST_TRIAL = 2; + // 待复审提币 + public static final Integer RECHECK = 3; + // 待出币 + public static final Integer DAI_CHU_BI = 4; + // 已出币 + public static final Integer YI_CHU_BI = 5; + // 出币失败 + public static final Integer CHU_BI_DEFEATED = 6; + // 审核不通过 + public static final Integer REJECTED = 7; + + } + + public static class RPCConstant { + public static final Integer OFFSET = -50; + public static final Integer ADD_OFFSET = -50; + public static final Integer OFFSET_MAX = -500; + } +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/enums/EosWalletEnums.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/enums/EosWalletEnums.java new file mode 100644 index 0000000..1c3be7a --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/enums/EosWalletEnums.java @@ -0,0 +1,68 @@ +package com.blockchain.server.eos.common.enums; + +public enum EosWalletEnums { + SESSION_ERROR(201, "操作失败,用户未登录!", "The operation failed and the user is not logged in!", "操作失敗,用戶未登錄!"), + EOSWALLET_GETWALLET_ERROR(202, "该钱包不存在!", "The wallet does not exist!", "該錢包不存在!"), + ERROR(203, "操作失败!", "operation failure!", "操作失敗!"), + BALANCE_AMOUNT_ERROR(204, "操作失败,余额不足!", "The operation failed and the balance is insufficient!", "操作失敗,餘額不足!"), + PARAM_ERROR(205, "操作失败,参数有误!", "The operation failed and the parameters are incorrect!", "操作失敗,參數有誤"), + WALLETTOKENT_ADD_ERROR(209, "操作失败,该代币钱包已存在!", "The operation failed and the token wallet already exists!", "操作失敗,該代幣錢包已存在!"), + DIGCOIN_ERROR(211, "操作失败,数据异常!", "The operation failed and the data is abnormal!", "操作失敗,數據異常!"), + TOKEN_TRANSFER_UPDATEHASH_ERROR(212, "操作失败,数据不存在!", "The operation failed and the data does not exist!", "操作失敗,數據不存在!"), + TOKEN_NOTTOKENADDR_ERROR(213, "操作失败,该数字货币暂不支持", "The operation failed, the digital currency is not supported yet.", "操作失敗,該數字貨幣暫不支持!"), + CREATION_WALLET_ERROR(214, "操作失败,托管钱包创建失败,请稍后再试!", "The operation failed, the managed wallet creation failed, please try again later!", "操作失敗,托管錢包創建失敗,請稍後再試!"), + GET_PUBLICKEY_ERROR(215, "服务器繁忙,请稍后再试!", "The server is busy, please try again later!", "服務器繁忙,請稍後再試!"), + EOS_RPC_ERROR(10000, "RPC post请求异常!", "RPC post request exception!", "RPC post請求異常!"), + EOS_RPC_GET_BLOCK_ERROR(10001, "获取区块异常!", "Get block exceptions!", "獲取區塊異常!"), + WITHDRAW_ERROR(7008, "提现失败", "Failed to withdrawal.", "提現失敗"), + CURRENCY_FAILURE_ERROR(7008, "出币失败", "Currency failure.", "出幣失敗"), + INEXISTENCE_TX(12500, "该记录未找到", "The record was not found", "該記錄未找到"), + INEXISTENCE_WALLET(7017, "该钱包不存在", "The wallet does not exist.", "該錢包不存在"), + + ; + + + private int code; + private String hkmsg; + private String enMsg; + private String cnmsg; + + EosWalletEnums(int code, String cnmsg, String enMsg, String hkmsg) { + this.code = code; + this.cnmsg = cnmsg; + this.enMsg = enMsg; + this.hkmsg = hkmsg; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getHkmsg() { + return hkmsg; + } + + public void setHkmsg(String hkmsg) { + this.hkmsg = hkmsg; + } + + public String getEnMsg() { + return enMsg; + } + + public void setEnMsg(String enMsg) { + this.enMsg = enMsg; + } + + public String getCnmsg() { + return cnmsg; + } + + public void setCnmsg(String cnmsg) { + this.cnmsg = cnmsg; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/exception/EosWalletException.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/exception/EosWalletException.java new file mode 100644 index 0000000..1a80cfe --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/exception/EosWalletException.java @@ -0,0 +1,39 @@ +package com.blockchain.server.eos.common.exception; + + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.eos.common.enums.EosWalletEnums; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +public class EosWalletException extends BaseException { + public EosWalletException(EosWalletEnums rs) { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + //可能是定时器调用,避免获取request空指针 + if (servletRequestAttributes == null) { + this.code = rs.getCode(); + this.msg = rs.getCnmsg(); + } else { + HttpServletRequest request = servletRequestAttributes.getRequest(); + String userLocale = HttpRequestUtil.getUserLocale(request); + String msg = ""; + switch (userLocale) { + case BaseConstant.USER_LOCALE_EN_US: + msg = rs.getEnMsg(); + break; + case BaseConstant.USER_LOCALE_ZH_HK: + msg = rs.getHkmsg(); + break; + default: + msg = rs.getCnmsg(); + break; + } + this.code = rs.getCode(); + this.msg = msg; + } + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/util/EosUtil.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/util/EosUtil.java new file mode 100644 index 0000000..2eb033d --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/util/EosUtil.java @@ -0,0 +1,178 @@ +package com.blockchain.server.eos.common.util; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.blockchain.server.eos.common.constant.EosConstant; +import com.blockchain.server.eos.common.enums.EosWalletEnums; +import com.blockchain.server.eos.common.exception.EosWalletException; +import com.blockchain.server.eos.dto.WalletInDTO; +import com.blockchain.server.eos.entity.Wallet; +import com.blockchain.server.eos.entity.WalletTransfer; +import com.blockchain.server.eos.service.EosWalletService; +import com.blockchain.server.eos.service.EosWalletTransferService; +import com.blockchain.server.eos.service.EosWalletUtilService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * @author Harvey Luo + * @date 2019/5/27 10:54 + */ +@Component +public class EosUtil { + + @Autowired + private EosWalletService eosWalletService; + @Autowired + private EosWalletTransferService eosWalletTransferService; + @Autowired + private EosWalletUtilService eosWalletUtilService; + + // 日志 + private static final Logger LOG = LoggerFactory.getLogger(EosUtil.class); + + /** + * 递归方式判断查询返回账户历史数据 + * + * @param walletInDTO + * @param offset + * @return + */ + public JSONArray recursion(WalletInDTO walletInDTO, Integer offset) { + JSONObject bodyBody = eosWalletUtilService.getActions(walletInDTO.getAccountName(), offset.toString()); + if (bodyBody == null) return null; + JSONArray actions = bodyBody.getJSONArray("actions"); + BigInteger blockNumber = walletInDTO.getBlockNumber(); + if (actions.size() > 0) { + JSONObject job = actions.getJSONObject(0); + JSONObject action_trace = job.getJSONObject("action_trace"); + BigInteger blockNum = action_trace.getBigInteger("block_num"); + if (blockNum.compareTo(blockNumber) <= 0 || offset.equals(EosConstant.RPCConstant.OFFSET_MAX)) + return actions; + else { + return recursion(walletInDTO, offset + EosConstant.RPCConstant.ADD_OFFSET); + } + } + return null; + } + + /** + * 分析账户历史记录 + * + * @param actions + * @param tokenName + * @param walletInDTO + */ + public void handTransaction(JSONArray actions, String tokenName, WalletInDTO walletInDTO) { + if (actions.size() > 0) { + for (int i = 0; i < actions.size(); i++) { + JSONObject job = actions.getJSONObject(i); + JSONObject action_trace = job.getJSONObject("action_trace"); + JSONObject act = action_trace.getJSONObject("act"); + String token = act.getObject("account", String.class); + // 判断币种地址 + if (!token.equalsIgnoreCase(tokenName)) continue; + JSONObject data = act.getJSONObject("data"); + String to = data.getObject("to", String.class); + // 判断资金账户 + if (to == null || !walletInDTO.getAccountName().equalsIgnoreCase(to)) continue; + // 判断是否为本地钱包 + String memo = data.getObject("memo", String.class); + Integer walletId = null; + try { + walletId = Integer.parseInt(memo.trim()); + } catch (Exception e) { + continue; + } + Wallet wallet = eosWalletService.selectWalletById(walletId.toString()); + if (wallet == null) continue; + // 获取充值金额和币种符号 + String quantity = data.getObject("quantity", String.class); + String[] quantitys = quantity.split(" "); + String amount = quantitys[0]; + String symbol = quantitys[1]; + if (!wallet.getTokenSymbol().equalsIgnoreCase(symbol)) continue; + + String hash = action_trace.getString("trx_id"); + // 判断数据库是否存在该记录 + Integer row = eosWalletTransferService.countByHashAndTransferType(hash, EosConstant.TransferType.TRANSFER_IN); + if (row != 0) continue; + String from = data.getObject("from", String.class); + BigInteger blockNum = action_trace.getBigInteger("block_num"); + // 修改钱包插入充值记录 + eosWalletTransferService.handleWalletAndWalletTransfer(hash, + wallet.getId(), + null, + EosConstant.TransferType.TRANSFER_IN, + new BigDecimal(quantitys[0]), + tokenName, + quantitys[1], + memo, + blockNum); + LOG.info("************************充值成功: hash值 ==》 " + hash + " + 币种名称 ==》 " + tokenName + " + 钱包地址 ==》 " + wallet.getId() + " + 金额 ==》" + quantity + "************************"); + + // System.out.println("hashId ===>" + hash); + // System.out.println("blockNum ===>" + blockNum); + // System.out.println("amount ==> " + amount); + // System.out.println("symbol ==> " + symbol); + // System.out.println("from ==> " + from); + // System.out.println("to ==> " + to); + // System.out.println("quantity ==> " + quantity); + // System.out.println("memo ==> " + memo); + } + } + } + + /** + * 解析eos查询交易信息状态 + * + * @param blockNum + * @param hashId + * @param walletTransfer + * @return + */ + public Boolean getTransactions(String blockNum, String hashId, WalletTransfer walletTransfer) { + JSONObject body = eosWalletUtilService.getTransaction(blockNum, hashId); + if (body == null || "".equalsIgnoreCase(body.toString())) { + LOG.info("************************出币失败:区块高度" + blockNum + "交易hash:" + hashId + "************************"); + return false; + } + JSONObject action = null; + JSONObject data = null; + try { + // 返回结果分析出币信息 + JSONObject trx = body.getJSONObject("trx"); + JSONObject trx1 = trx.getJSONObject("trx"); + JSONArray actions = trx1.getJSONArray("actions"); + action = actions.getJSONObject(0); + data = action.getJSONObject("data"); + if (null == data || "".equalsIgnoreCase(data.toString())) { + LOG.info("************************出币失败:区块高度" + blockNum + "交易hash:" + hashId + "************************"); + return false; + } else { + // 支付人地址: + // String from = data.getObject("from", String.class); + // 收款人地址: + String to = data.getObject("to", String.class); + // 提现由系统账户转账到用户提供账户 + // 判断to是否为用户提供的地址名称 + if (to.equalsIgnoreCase(walletTransfer.getAccountName())) return true; + else { + LOG.info("************************出币失败:区块高度" + blockNum + "交易hash:" + hashId + "************************"); + throw new EosWalletException(EosWalletEnums.CURRENCY_FAILURE_ERROR); + } + } + } catch (Exception e) { + LOG.info("************************出币失败:区块高度" + blockNum + "交易hash:" + hashId + "************************"); + // TODO 出币失败是否抛出错误 + // throw new EosWalletException(EosWalletEnums.CURRENCY_FAILURE_ERROR); + return false; + } + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/util/RedisUtil.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/util/RedisUtil.java new file mode 100644 index 0000000..f506936 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/common/util/RedisUtil.java @@ -0,0 +1,88 @@ +package com.blockchain.server.eos.common.util; + +import com.blockchain.server.eos.common.constant.EosConstant; +import org.springframework.data.redis.core.RedisTemplate; + +import java.math.BigInteger; + +/** + * @author Harvey + * @date 2019/2/23 10:52 + * @user WIN10 + * redis工具类 + */ +public class RedisUtil { + + /** + * 获取当前区块 + * + * @param redisTemplate + * @return + */ + public static BigInteger getCurrentBlock(RedisTemplate redisTemplate) { + Object current = redisTemplate.opsForValue().get(EosConstant.RedisKey.EOS_BLOCKCHAIN_CURRENT); + if (!(current instanceof BigInteger)) return null; + return (BigInteger) current; + } + + /** + * 设置redis + * + * @param key + * @param value + * @param redisTemplate + */ + public static void setOpsForValue(String key, Object value, RedisTemplate redisTemplate) { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 获取存储处理次数 + * + * @param mapKey + * @param key + * @param redisTemplate + * @return + */ + public static Integer getOpsForHash(String mapKey, BigInteger key, RedisTemplate redisTemplate) { + Object number = redisTemplate.opsForHash().get(mapKey, key); + if (!(number instanceof Integer)) return null; + return (Integer) number; + } + + /** + * 判断是否存在该处理次数数据 + * + * @param mapKey + * @param key + * @param redisTemplate + * @return + */ + public static boolean getKeyIsExist(String mapKey, BigInteger key, RedisTemplate redisTemplate) { + return redisTemplate.opsForHash().hasKey(mapKey, key); + } + + /** + * 保存处理次数 + * + * @param mapKey + * @param key + * @param time + * @param redisTemplate + */ + public static void putOpsForHash(String mapKey, BigInteger key, Integer time, RedisTemplate redisTemplate) { + redisTemplate.opsForHash().put(mapKey, key, time); + } + + /** + * 删除处理次数 + * + * @param mapKey + * @param key + * @param redisTemplate + */ + public static void delOpsForHash(String mapKey, BigInteger key, RedisTemplate redisTemplate) { + redisTemplate.opsForHash().delete(mapKey, key); + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/ConfigWalletParamController.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/ConfigWalletParamController.java new file mode 100644 index 0000000..171b5cb --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/ConfigWalletParamController.java @@ -0,0 +1,36 @@ +package com.blockchain.server.eos.controller; + +import com.blockchain.common.base.dto.GasDTO; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.eos.controller.api.ConfigWalletParamApi; +import com.blockchain.server.eos.controller.api.EosTokenApi; +import com.blockchain.server.eos.entity.ConfigWalletParam; +import com.blockchain.server.eos.service.ConfigWalletParamService; +import com.blockchain.server.eos.service.EosTokenService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * @author Harvey + * @date 2019/2/19 18:09 + * @user WIN10 + */ +@Api(ConfigWalletParamApi.CONFIG_WALLET_PARAM_API) +@RestController +@RequestMapping("/configWalletParam") +public class ConfigWalletParamController { + + @Autowired + private ConfigWalletParamService configWalletParamService; + + @ApiOperation(value = ConfigWalletParamApi.SelectConfigWalletParam.MATHOD_API_NAME, notes = ConfigWalletParamApi.SelectConfigWalletParam.MATHOD_API_NOTE) + @GetMapping("/selectConfigWalletParam") + public ResultDTO selectConfigWalletParam(@ApiParam(ConfigWalletParamApi.SelectConfigWalletParam.MATHOD_API_TOKEN_TYPE) @RequestParam("tokenSymbol") String tokenType) { + GasDTO gasDTO = configWalletParamService.selectConfigWalletParam(configWalletParamService.selectOne(tokenType.toLowerCase())); + return ResultDTO.requstSuccess(gasDTO); + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosTokenController.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosTokenController.java new file mode 100644 index 0000000..b92f47a --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosTokenController.java @@ -0,0 +1,36 @@ +package com.blockchain.server.eos.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.eos.controller.api.EosTokenApi; +import com.blockchain.server.eos.controller.api.EosWalletInApi; +import com.blockchain.server.eos.service.EosTokenService; +import com.blockchain.server.eos.service.EosWalletInService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author Harvey + * @date 2019/2/19 18:09 + * @user WIN10 + */ +@Api(EosTokenApi.EOS_TOKEN_API) +@RestController +@RequestMapping("/walletIn") +public class EosTokenController { + + @Autowired + private EosTokenService eosTokenService; + + @ApiOperation(value = EosTokenApi.SelectEosToken.MATHOD_API_NAME, notes = EosTokenApi.SelectEosToken.MATHOD_API_NOTE) + @PostMapping("/selectEosToken") + public ResultDTO selectEosToken(@ApiParam(EosTokenApi.SelectEosToken.MATHOD_API_TOKEN_NAME) @RequestParam("tokenName") String tokenName) { + return ResultDTO.requstSuccess(eosTokenService.selectEosTokenByTokenName(tokenName)); + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosWalletController.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosWalletController.java new file mode 100644 index 0000000..a3938d6 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosWalletController.java @@ -0,0 +1,77 @@ +package com.blockchain.server.eos.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.eos.controller.api.EosWalletApi; +import com.blockchain.server.eos.dto.WalletDTO; +import com.blockchain.server.eos.service.EosWalletService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.math.BigDecimal; +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/19 18:09 + * @user WIN10 + */ +@Api(EosWalletApi.EOS_WALLET_API) +@RestController +@RequestMapping("/wallet") +public class EosWalletController { + + @Autowired + private EosWalletService eosWalletService; + @Autowired + private RedisTemplate redisTemplate; + + @ApiOperation(value = EosWalletApi.WithdrawDeposit.MATHOD_API_NAME, notes = EosWalletApi.WithdrawDeposit.MATHOD_API_NOTE) + @PostMapping("/withdrawDeposit") + public ResultDTO withdrawDeposit( + @ApiParam(EosWalletApi.WithdrawDeposit.MATHOD_API_PASSWORD) @RequestParam("password") String password, + @ApiParam(EosWalletApi.WithdrawDeposit.MATHOD_API_ACCOUNT) @RequestParam("toAddr") String account, + @ApiParam(EosWalletApi.WithdrawDeposit.MATHOD_API_AMOUNT) @RequestParam("amount") BigDecimal amount, + @ApiParam(EosWalletApi.WithdrawDeposit.MATHOD_API_TOKEN_NAME) @RequestParam("tokenName") String tokenName, + @ApiParam(EosWalletApi.WithdrawDeposit.MATHOD_API_WALLET_TYPE) @RequestParam("walletType") String walletType, + HttpServletRequest request) { + String userOpenId = SSOHelper.getUserId(redisTemplate, request); + eosWalletService.handleWithdrawDeposit(userOpenId, password, account, amount, tokenName, walletType); + + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = EosWalletApi.SelectWalletByWalletType.MATHOD_API_NAME, notes = EosWalletApi.SelectWalletByWalletType.MATHOD_API_NOTE) + @GetMapping("/selectWalletByWalletType") + public ResultDTO selectWalletByWalletType(@ApiParam(EosWalletApi.SelectWalletByWalletType.MATHOD_API_WALLET_TYPE) @RequestParam("walletType") String walletType, HttpServletRequest request) { + String userOpenId = SSOHelper.getUserId(redisTemplate, request); + List walletDTOS = eosWalletService.listWalletByWalletType(walletType, userOpenId); + return ResultDTO.requstSuccess(walletDTOS); + } + + @ApiOperation(value = EosWalletApi.SelectWalletByTokenName.MATHOD_API_NAME, notes = EosWalletApi.SelectWalletByTokenName.MATHOD_API_NOTE) + @GetMapping("/selectWalletByTokenName") + public ResultDTO selectWalletByTokenName(@ApiParam(EosWalletApi.SelectWalletByTokenName.MATHOD_API_TOKEN_NAME) @RequestParam("tokenName") String tokenName, @ApiParam(EosWalletApi.SelectWalletByTokenName.MATHOD_API_WALLET_TYPE) @RequestParam("walletType") String walletType, HttpServletRequest request) { + String userOpenId = SSOHelper.getUserId(redisTemplate, request); + WalletDTO walletDTO = eosWalletService.selectWallet(userOpenId, tokenName, walletType); + return ResultDTO.requstSuccess(walletDTO); + } + + @ApiOperation(value = EosWalletApi.Transfer.METHOD_API_NAME, notes = EosWalletApi.Transfer.METHOD_API_NOTE) + @PostMapping("/transfer") + public ResultDTO handleTransfer(HttpServletRequest request, + @ApiParam(EosWalletApi.Transfer.METHOD_API_FROMTYPE) @RequestParam("fromType") String fromType, + @ApiParam(EosWalletApi.Transfer.METHOD_API_TOTYPE) @RequestParam("toType") String toType, + @ApiParam(EosWalletApi.Transfer.METHOD_API_COINNAME) @RequestParam("coinName") String coinName, + @ApiParam(EosWalletApi.Transfer.METHOD_API_AMOUNT) @RequestParam("amount") BigDecimal amount) { + String userOpenId = SSOHelper.getUserId(redisTemplate, request); + return ResultDTO.requstSuccess(eosWalletService.handleTransfer(userOpenId,fromType, toType, coinName, amount)); + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosWalletInController.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosWalletInController.java new file mode 100644 index 0000000..83c717f --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosWalletInController.java @@ -0,0 +1,42 @@ +package com.blockchain.server.eos.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.eos.controller.api.EosWalletApi; +import com.blockchain.server.eos.controller.api.EosWalletInApi; +import com.blockchain.server.eos.dto.WalletDTO; +import com.blockchain.server.eos.service.EosWalletInService; +import com.blockchain.server.eos.service.EosWalletService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.math.BigDecimal; +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/19 18:09 + * @user WIN10 + */ +@Api(EosWalletInApi.EOS_WALLET_IN_API) +@RestController +@RequestMapping("/walletIn") +public class EosWalletInController { + + @Autowired + private EosWalletInService eosWalletInService; + + @ApiOperation(value = EosWalletInApi.ListWalletInAccount.MATHOD_API_NAME, notes = EosWalletInApi.ListWalletInAccount.MATHOD_API_NOTE) + @PostMapping("/listWalletInAccount") + public ResultDTO listWalletInAccount(@ApiParam(EosWalletInApi.ListWalletInAccount.MATHOD_API_TOKEN_NAME) @RequestParam("tokenName") String tokenName) { + return ResultDTO.requstSuccess(eosWalletInService.selectWalletInAccount(tokenName)); + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosWalletTransferController.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosWalletTransferController.java new file mode 100644 index 0000000..723646c --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/EosWalletTransferController.java @@ -0,0 +1,68 @@ +package com.blockchain.server.eos.controller; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.base.controller.BaseController; +import com.blockchain.server.eos.common.constant.EosConstant; +import com.blockchain.server.eos.controller.api.EosWalletTransferApi; +import com.blockchain.server.eos.dto.WalletTransferDTO; +import com.blockchain.server.eos.service.EosWalletTransferService; +import com.github.pagehelper.PageHelper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/25 11:07 + * @user WIN10 + */ +@Api(EosWalletTransferApi.EOS_WALLET_TRANSFER_API) +@RestController +@RequestMapping("/walletTransfer") +public class EosWalletTransferController extends BaseController { + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private EosWalletTransferService eosWalletTransferService; + + + @ApiOperation(value = EosWalletTransferApi.GetTransfer.METHOD_API_NAME, notes = EosWalletTransferApi.GetTransfer.METHOD_API_NOTE) + @GetMapping("/getTransfer") + public ResultDTO getTransfer(HttpServletRequest request, + @ApiParam(EosWalletTransferApi.PAGENUM) @RequestParam(name = "pageNum", required = false, defaultValue = BaseConstant.PAGE_DEFAULT_INDEX) Integer pageNum, + @ApiParam(EosWalletTransferApi.PAGESIZE) @RequestParam(name = "pageSize", required = false, defaultValue = BaseConstant.PAGE_DEFAULT_SIZE) Integer pageSize, + @ApiParam(EosWalletTransferApi.GetTransfer.METHOD_API_TOKEN_NAME) @RequestParam(value = "tokenName", required = false, defaultValue = EosConstant.EOS_TOKEN_NAME) String tokenName, + @ApiParam(EosWalletTransferApi.GetTransfer.METHOD_API_WALLET_TYPE) @RequestParam(value = "walletType", required = false, defaultValue = EosConstant.TransferType.TRANSFER_CCT) String walletType) { + String userOpenId = SSOHelper.getUserId(redisTemplate, request); + //分页查询记录 + PageHelper.startPage(pageNum, pageSize); + return ResultDTO.requstSuccess(eosWalletTransferService.listWalletTransfer(userOpenId, tokenName, walletType)); + } + + @ApiOperation(value = EosWalletTransferApi.pcGetTransfer.METHOD_API_NAME, notes = EosWalletTransferApi.pcGetTransfer.METHOD_API_NOTE) + @GetMapping("/pcGetTransfer") + public ResultDTO pcGetTransfer(HttpServletRequest request, + @ApiParam(EosWalletTransferApi.PAGENUM) @RequestParam(name = "pageNum", required = false, defaultValue = BaseConstant.PAGE_DEFAULT_INDEX) Integer pageNum, + @ApiParam(EosWalletTransferApi.PAGESIZE) @RequestParam(name = "pageSize", required = false, defaultValue = BaseConstant.PAGE_DEFAULT_SIZE) Integer pageSize, + @ApiParam(EosWalletTransferApi.pcGetTransfer.METHOD_API_TOKEN_NAME) @RequestParam(value = "tokenName", required = false, defaultValue = EosConstant.EOS_TOKEN_NAME) String tokenName, + @ApiParam(EosWalletTransferApi.pcGetTransfer.METHOD_API_WALLET_TYPE) @RequestParam(value = "walletType", required = false, defaultValue = EosConstant.TransferType.TRANSFER_CCT) String walletType) { + String userOpenId = SSOHelper.getUserId(redisTemplate, request); + //分页查询记录 + PageHelper.startPage(pageNum, pageSize); + List list = eosWalletTransferService.listWalletTransfer(userOpenId, tokenName, walletType); + return generatePage(list); + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/ConfigWalletParamApi.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/ConfigWalletParamApi.java new file mode 100644 index 0000000..ffe58f7 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/ConfigWalletParamApi.java @@ -0,0 +1,17 @@ +package com.blockchain.server.eos.controller.api; + +/** + * @author Harvey + * @date 2019/2/19 18:10 + * @user WIN10 + */ +public class ConfigWalletParamApi { + public static final String CONFIG_WALLET_PARAM_API = "配置手续费控制器"; + + public static class SelectConfigWalletParam { + public static final String MATHOD_API_NAME = "查询手续费"; + public static final String MATHOD_API_NOTE = "通过paramName查询手续费"; + public static final String MATHOD_API_TOKEN_TYPE = "token类型"; + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosTokenApi.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosTokenApi.java new file mode 100644 index 0000000..8847742 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosTokenApi.java @@ -0,0 +1,17 @@ +package com.blockchain.server.eos.controller.api; + +/** + * @author Harvey + * @date 2019/2/19 18:10 + * @user WIN10 + */ +public class EosTokenApi { + public static final String EOS_TOKEN_API = "充值钱包账户控制器"; + + public static class SelectEosToken { + public static final String MATHOD_API_NAME = "查询币种信息"; + public static final String MATHOD_API_NOTE = "查询币种信息"; + public static final String MATHOD_API_TOKEN_NAME = "币种名称"; + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosWalletApi.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosWalletApi.java new file mode 100644 index 0000000..ffe323f --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosWalletApi.java @@ -0,0 +1,44 @@ +package com.blockchain.server.eos.controller.api; + +/** + * @author Harvey + * @date 2019/2/19 18:10 + * @user WIN10 + */ +public class EosWalletApi { + public static final String EOS_WALLET_API = "钱包控制器"; + + public static class WithdrawDeposit { + public static final String MATHOD_API_NAME = "提现"; + public static final String MATHOD_API_NOTE = "提现"; + + public static final String MATHOD_API_PASSWORD = "提现密码"; + public static final String MATHOD_API_ACCOUNT = "提现账号"; + public static final String MATHOD_API_PRIVATE_KEY = "提现账户私钥"; + public static final String MATHOD_API_AMOUNT = "提现金额"; + public static final String MATHOD_API_TOKEN_NAME = "代币地址"; + public static final String MATHOD_API_WALLET_TYPE = "钱包标识"; + } + + public static class SelectWalletByWalletType { + public static final String MATHOD_API_NAME = "查询钱包"; + public static final String MATHOD_API_NOTE = "根据钱包标识查询钱包"; + public static final String MATHOD_API_WALLET_TYPE = "钱包标识"; + } + + public static class SelectWalletByTokenName { + public static final String MATHOD_API_NAME = "查询钱包"; + public static final String MATHOD_API_NOTE = "根据钱包标识和代币名称查询钱包"; + public static final String MATHOD_API_WALLET_TYPE = "查询标识"; + public static final String MATHOD_API_TOKEN_NAME = "币种名称"; + } + + public static class Transfer { + public static final String METHOD_API_NAME = "钱包划转"; + public static final String METHOD_API_NOTE = "钱包划转"; + public static final String METHOD_API_FROMTYPE = "支付方钱包类型"; + public static final String METHOD_API_TOTYPE = "收款钱包类型"; + public static final String METHOD_API_COINNAME = "划转币种的名称"; + public static final String METHOD_API_AMOUNT = "金额"; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosWalletInApi.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosWalletInApi.java new file mode 100644 index 0000000..5f5d7d1 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosWalletInApi.java @@ -0,0 +1,17 @@ +package com.blockchain.server.eos.controller.api; + +/** + * @author Harvey + * @date 2019/2/19 18:10 + * @user WIN10 + */ +public class EosWalletInApi { + public static final String EOS_WALLET_IN_API = "充值钱包账户控制器"; + + public static class ListWalletInAccount { + public static final String MATHOD_API_NAME = "查询钱包充值账户"; + public static final String MATHOD_API_NOTE = "查询钱包充值账户"; + public static final String MATHOD_API_TOKEN_NAME = "币种名称"; + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosWalletTransferApi.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosWalletTransferApi.java new file mode 100644 index 0000000..2fdf774 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/controller/api/EosWalletTransferApi.java @@ -0,0 +1,28 @@ +package com.blockchain.server.eos.controller.api; + +/** + * @author Harvey + * @date 2019/2/19 18:10 + * @user WIN10 + */ +public class EosWalletTransferApi { + public static final String EOS_WALLET_TRANSFER_API = "EOS钱包交易记录控制器"; + + public static final String PAGENUM = "当前页数"; + public static final String PAGESIZE = "页数展示条数"; + + + public static class GetTransfer { + public static final String METHOD_API_NAME = "app端查询所有交易记录"; + public static final String METHOD_API_NOTE = "app端查询所有交易记录"; + public static final String METHOD_API_TOKEN_NAME = "币种名称"; + public static final String METHOD_API_WALLET_TYPE = "钱包标识"; + } + + public static class pcGetTransfer { + public static final String METHOD_API_NAME = "pc端查询所有交易记录"; + public static final String METHOD_API_NOTE = "pc端查询所有交易记录"; + public static final String METHOD_API_TOKEN_NAME = "币种名称"; + public static final String METHOD_API_WALLET_TYPE = "钱包标识"; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/BlockNumberDTO.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/BlockNumberDTO.java new file mode 100644 index 0000000..451b759 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/BlockNumberDTO.java @@ -0,0 +1,28 @@ +package com.blockchain.server.eos.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigInteger; +import java.util.Date; + +/** + * \*

Desciption:

+ * \* CreateTime: 2018/12/7 17:09 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BlockNumberDTO { + + private BigInteger currentBlock; + + private BigInteger newBlock; + + private Date createTime; + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/BlockchainNumDTO.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/BlockchainNumDTO.java new file mode 100644 index 0000000..d711433 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/BlockchainNumDTO.java @@ -0,0 +1,23 @@ +package com.blockchain.server.eos.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigInteger; + +/** + * @author Harvey + * @date 2019/2/21 15:57 + * @user WIN10 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BlockchainNumDTO { + + private BigInteger begin; + private BigInteger current; + private BigInteger newBlockNum; + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/TokenDTO.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/TokenDTO.java new file mode 100644 index 0000000..7e88dd0 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/TokenDTO.java @@ -0,0 +1,26 @@ +package com.blockchain.server.eos.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigInteger; +import java.util.Date; + +/** + * TokentDTO 数据传输类(以太坊代币配置表) + * @date 2018-11-05 15:10:47 + * @version 1.0 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TokenDTO { + private String tokenName; + private String tokenSymbol; + private Date issueTime; + private String totalSupply; + private String totalCirculation; + private String descr; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/WalletDTO.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/WalletDTO.java new file mode 100644 index 0000000..f62bba9 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/WalletDTO.java @@ -0,0 +1,29 @@ +package com.blockchain.server.eos.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * Wallet 数据传输类 + * @date 2018-11-05 15:10:47 + * @version 1.0 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WalletDTO { + private Integer id; + private String tokenName; + private String userOpenId; + private String tokenSymbol; + private BigDecimal balance; + private BigDecimal freeBalance; + private BigDecimal freezeBalance; + private java.util.Date createTime; + private java.util.Date updateTime; + private String walletType; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/WalletInDTO.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/WalletInDTO.java new file mode 100644 index 0000000..12bf61b --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/WalletInDTO.java @@ -0,0 +1,25 @@ +package com.blockchain.server.eos.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigInteger; + +/** + * WalletIn 充值资金钱包 + * @date 2018-11-05 15:10:47 + * @version 1.0 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WalletInDTO { + private String id; + private String accountName; + private String tokenName; + private String tokenSymbol; + private String remark; + private String status; + private BigInteger blockNumber; +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/WalletTransferDTO.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/WalletTransferDTO.java new file mode 100644 index 0000000..881efd9 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/dto/WalletTransferDTO.java @@ -0,0 +1,37 @@ +package com.blockchain.server.eos.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * WalletTransfert 用户钱包历史记录 + * + * @version 1.0 + * @date 2018-11-05 15:10:47 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WalletTransferDTO { + private String id; + private String hash; + private Integer fromId; + private Integer toId; + private String accountName; + private BigDecimal amount; + private String tokenName; + private String tokenSymbol; + private BigDecimal gasPrice; + private String gasTokenType; + private String gasTokenName; + private String gasTokenSymbol; + private String transferType; + private Integer status; + private String remark; + private Date timestamp; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/Application.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/Application.java new file mode 100644 index 0000000..7969b22 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/Application.java @@ -0,0 +1,27 @@ +package com.blockchain.server.eos.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * Apply 数据传输类 + * @date 2018年12月5日10:05:13 + * @version 1.0 + */ +@Table(name = "dapp_eos_application") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Application extends BaseModel { + @Id + @Column(name = "app_id") + private String appId; + @Column(name = "app_name") + private String appName; +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/BlockNumber.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/BlockNumber.java new file mode 100644 index 0000000..d3b18a0 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/BlockNumber.java @@ -0,0 +1,34 @@ +package com.blockchain.server.eos.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigInteger; +import java.util.Date; + +/** + * \*

Desciption:

+ * \* CreateTime: 2018/12/7 17:09 + * \* User: XianChaoWei + * \* Version: V1.0 + * \ + */ +@Table(name = "dapp_eos_block_number") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BlockNumber { + + @Id + @Column(name = "block_number") + private BigInteger blockNumber; + @Column(name = "status") + private Character status; + @Column(name = "create_time") + private Date createTime; + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/ConfigWalletParam.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/ConfigWalletParam.java new file mode 100644 index 0000000..d171d9d --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/ConfigWalletParam.java @@ -0,0 +1,35 @@ +package com.blockchain.server.eos.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * Apply 数据传输类 + * @date 2018年12月5日10:05:13 + * @version 1.0 + */ +@Table(name = "config_wallet_param") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ConfigWalletParam extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "module_type") + private String moduleType; + @Column(name = "param_name") + private String paramName; + @Column(name = "param_value") + private String paramValue; + @Column(name = "param_descr") + private String paramDescr; + @Column(name = "status") + private String status; +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/Token.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/Token.java new file mode 100644 index 0000000..fefb21f --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/Token.java @@ -0,0 +1,39 @@ +package com.blockchain.server.eos.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigInteger; +import java.util.Date; + +/** + * Token 数据传输类 + * @date 2018-11-05 15:10:47 + * @version 1.0 + */ +@Table(name = "dapp_eos_token") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Token extends BaseModel { + @Id + @Column(name = "token_name") + private String tokenName; + @Column(name = "token_symbol") + private String tokenSymbol; + @Column(name = "issue_time") + private Date issueTime; + @Column(name = "total_supply") + private BigInteger totalSupply; + @Column(name = "total_circulation") + private String totalCirculation; + @Column(name = "descr") + private String descr; + + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/TransferAuditing.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/TransferAuditing.java new file mode 100644 index 0000000..da197f9 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/TransferAuditing.java @@ -0,0 +1,39 @@ +package com.blockchain.server.eos.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigInteger; +import java.util.Date; + +/** + * Token 数据传输类 + * @date 2018-11-05 15:10:47 + * @version 1.0 + */ +@Table(name = "dapp_eos_transfer_auditing") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TransferAuditing extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "sys_user_id") + private String sysUserId; + @Column(name = "ip_addr") + private String ipAddr; + @Column(name = "transfer_id") + private String transferId; + @Column(name = "transfer_status") + private Integer transferStatus; + @Column(name = "create_time") + private Date createTime; + + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/Wallet.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/Wallet.java new file mode 100644 index 0000000..69eef91 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/Wallet.java @@ -0,0 +1,44 @@ +package com.blockchain.server.eos.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * Wallet 数据传输类 + * @date 2018-11-05 15:10:47 + * @version 1.0 + */ +@Table(name = "dapp_eos_wallet") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Wallet extends BaseModel { + @Id + @Column(name = "id") + private Integer id; + @Column(name = "token_name") + private String tokenName; + @Column(name = "user_open_id") + private String userOpenId; + @Column(name = "token_symbol") + private String tokenSymbol; + @Column(name = "balance") + private String balance; + @Column(name = "free_balance") + private String freeBalance; + @Column(name = "freeze_balance") + private String freezeBalance; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "update_time") + private java.util.Date updateTime; + @Column(name = "wallet_type") + private String walletType; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/WalletIn.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/WalletIn.java new file mode 100644 index 0000000..6bbfc78 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/WalletIn.java @@ -0,0 +1,36 @@ +package com.blockchain.server.eos.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * WalletIn 充值资金钱包 + * @date 2018-11-05 15:10:47 + * @version 1.0 + */ +@Table(name = "dapp_eos_wallet_in") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WalletIn extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "account_name") + private String accountName; + @Column(name = "token_name") + private String tokenName; + @Column(name = "token_symbol") + private String tokenSymbol; + @Column(name = "remark") + private String remark; + @Column(name = "status") + private String status; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/WalletOut.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/WalletOut.java new file mode 100644 index 0000000..fb70606 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/WalletOut.java @@ -0,0 +1,40 @@ +package com.blockchain.server.eos.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * WalletOut 提现资金钱包 + * @date 2018-11-05 15:10:47 + * @version 1.0 + */ +@Table(name = "dapp_eos_wallet_out") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WalletOut extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "account_name") + private String accountName; + @Column(name = "token_name") + private String tokenName; + @Column(name = "token_symbol") + private String tokenSymbol; + @Column(name = "private_key") + private String privateKey; + @Column(name = "password") + private String password; + @Column(name = "remark") + private String remark; + @Column(name = "status") + private String status; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/WalletTransfer.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/WalletTransfer.java new file mode 100644 index 0000000..5572e67 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/entity/WalletTransfer.java @@ -0,0 +1,61 @@ +package com.blockchain.server.eos.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; +import java.util.Date; + +/** + * WalletTransfert 用户钱包历史记录 + * + * @version 1.0 + * @date 2018-11-05 15:10:47 + */ +@Table(name = "dapp_eos_wallet_transfer") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WalletTransfer extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "hash") + private String hash; + @Column(name = "from_id") + private Integer fromId; + @Column(name = "to_id") + private Integer toId; + @Column(name = "account_name") + private String accountName; + @Column(name = "amount") + private BigDecimal amount; + @Column(name = "token_name") + private String tokenName; + @Column(name = "token_symbol") + private String tokenSymbol; + @Column(name = "gas_price") + private BigDecimal gasPrice; + @Column(name = "gas_token_type") + private String gasTokenType; + @Column(name = "gas_token_name") + private String gasTokenName; + @Column(name = "gas_token_symbol") + private String gasTokenSymbol; + @Column(name = "transfer_type") + private String transferType; + @Column(name = "status") + private Integer status; + @Column(name = "remark") + private String remark; + @Column(name = "timestamp") + private Date timestamp; + @Column(name = "block_number") + private String blockNumber; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/Ecc.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/Ecc.java new file mode 100644 index 0000000..997267d --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/Ecc.java @@ -0,0 +1,154 @@ +package com.blockchain.server.eos.eos4j; + +import com.blockchain.server.eos.eos4j.api.vo.transaction.push.TxSign; +import com.blockchain.server.eos.eos4j.ecc.EccTool; +import com.blockchain.server.eos.eos4j.ese.Ese; + +import java.util.List; + +/** + * Ecc,用户生成公私钥,签名,数据序列化 + * + * @author espritblock http://eblock.io + * + */ +public class Ecc { + + /** + * 通过种子生成私钥 + * + * @param seed + * 种子 + * @return + */ + public static String seedPrivate(String seed) { + return EccTool.seedPrivate(seed); + } + + /** + * 通过私钥生成公钥 + * + * @param privateKey + * 私钥 + * @return + */ + public static String privateToPublic(String privateKey) { + return EccTool.privateToPublic(privateKey); + } + + /** + * 普通数据签名 + * + * @param privateKey + * 私钥 + * @param data + * 需要签名的数据 + * @return + */ + public static String sign(String privateKey, String data) { + return EccTool.sign(privateKey, data); + } + + /** + * 交易签名 + * + * @param privateKey + * 私钥 + * @param data + * 需要签名的对象 + * @return + */ + public static String signTransaction(String privateKey, TxSign sign) { + return EccTool.signTransaction(privateKey, sign); + } + + /** + * 转账数据序列化 + * + * @param from + * 从 + * @param to + * 到 + * @param quantity + * 转账金额和币种 + * @param memo + * 备注留言 + * @return + */ + public static String parseTransferData(String from, String to, String quantity, String memo) { + return Ese.parseTransferData(from, to, quantity, memo); + } + + /** + * + * @param voter + * @param proxy + * @param producers + * @return + */ + public static String parseVoteProducerData(String voter, String proxy, List producers) { + return Ese.parseVoteProducerData(voter, proxy, producers); + } + + /** + * 创建账户数据序列化 + * + * @param creator + * 创建者 + * @param name + * 账户名 + * @param onwe + * onwer公钥 + * @param active + * active公钥 + * @return + */ + public static String parseAccountData(String creator, String name, String onwer, String active) { + return Ese.parseAccountData(creator, name, onwer, active); + } + + /** + * 购买ram数据序列化 + * + * @param payer + * 付款账户 + * @param receiver + * 接收账户 + * @param bytes + * 购买字节数量 + * @return + */ + public static String parseBuyRamData(String payer, String receiver, Long bytes) { + return Ese.parseBuyRamData(payer, receiver, bytes); + } + + /** + * 抵押数据序列化 + * + * @param from + * 抵押账户 + * @param receiver + * 接受账户 + * @param stakeNetQuantity + * 网络抵押数量和币种 + * @param stakeCpuQuantity + * CPU抵押数量和币种 + * @param transfer + * 是否讲抵押资产转送给对方,0自己所有,1对方所有 + * @return + */ + public static String parseBuyRamData(String from, String receiver, String stakeNetQuantity, String stakeCpuQuantity, + int transfer) { + return Ese.parseDelegateData(from, receiver, stakeNetQuantity, stakeCpuQuantity, transfer); + } + + /** + * 关闭token + * @param owner + * @param symble + * @return + */ + public static String parseCloseData(String owner, String symbol) { + return Ese.parseCloseData(owner, symbol); + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/EosTest.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/EosTest.java new file mode 100644 index 0000000..9e2f08a --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/EosTest.java @@ -0,0 +1,417 @@ +package com.blockchain.server.eos.eos4j; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.blockchain.server.eos.eos4j.api.exception.ApiException; +import com.blockchain.server.eos.eos4j.api.vo.Block; +import com.blockchain.server.eos.eos4j.api.vo.ChainInfo; +import com.blockchain.server.eos.eos4j.api.vo.SignParam; +import com.blockchain.server.eos.eos4j.api.vo.account.Account; +import com.blockchain.server.eos.eos4j.api.vo.transaction.Processed; +import com.blockchain.server.eos.eos4j.api.vo.transaction.Transaction; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; + + +/** + * @author Harvey + * @date 2019/2/14 16:38 + * @user WIN10 + */ +public class EosTest { + + private static Rpc rpc = null; // new Rpc("https://api-kylin.eosasia.one"); + private static final RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory()); + + private static final String EOS_RPC_URI = null; // "http://api-kylin.eosasia.one/"; + private static final String GET_TRANSACTION_SUFFIX_URI = "v1/history/get_transaction"; + private static RedisTemplate redistemplate; + + // public static void main(String[] args) { + + + // 创建密钥对 + /*for (int i = 0; i < 2; i++) { + String pk = Ecc.seedPrivate(UUID.randomUUID().toString()); + String pu = Ecc.privateToPublic(pk); + System.out.println(pu + " : " + pk); + // EOS5i7ScCUx85khc3gBWKjBcdBRQdimkPJ3BLtVpArTML71ZEoSoR : 5KbbsfKUd4YshNqkE67AMyvQjEAmiomN66rfcyabx2v4U75yB6p + // EOS4wLzc4Gfr39nNJfVCT2eLqv1xhxEWwsEj6mVAEiY4o4YcEx2o7 : 5JVDVECcg4WQQF4GYYMqWofwyBLXAWfjpnP22Hw72TnTA6wrC6w + }*/ + + // 创建者 + String creator = "aaasssxxxccc"; + // 创建者密钥 + String privateKeys = "5KBR1zJT1VYxcHDAB1MgDqmXufvFYJDmWtwZGVMUDGottR5k5gJ"; + // 创建的账户 + String newAccount = "55vbdcvfg553"; + String ownerPublic = "EOS5i7ScCUx85khc3gBWKjBcdBRQdimkPJ3BLtVpArTML71ZEoSoR"; + String activePublic = "EOS4wLzc4Gfr39nNJfVCT2eLqv1xhxEWwsEj6mVAEiY4o4YcEx2o7"; + String stakeNetQuantity = new BigDecimal(1.0100).setScale(4, BigDecimal.ROUND_HALF_UP) + " EOS"; + String stakeCpuQuantity = new BigDecimal(10.0100).setScale(4, BigDecimal.ROUND_HALF_UP) + " EOS"; + + + // 不抵押创建账户 需要提供两个公钥, + // testOfflineCreate(creator, privateKeys, ownerPublic, activePublic, newAccount); + // 押创建账户 需要提供两个公钥, + // testOfflineCreatePledge(creator, privateKeys, ownerPublic, activePublic, newAccount); + + // 提现 + // eosWithdrawDeposit(privateKeys, creator, newAccount, stakeCpuQuantity, "aaasssxxxccc transfer 55vbdcvfg553"); + + // 查询账户 + // Account account = getAccount(creator); + // System.out.println(account); + + // ChainInfo chainInfo = getChainInfo(); + // System.out.println("-----------------------------------------------------------------"); + // getBlock(chainInfo.getHeadBlockNum()); + + // postVisitEos(); + + // 获取区块交易信息 + // blockPay(); + + // eosWithdrawDeposit(privateKeys, creator, newAccount, stakeCpuQuantity, "aaasssxxxccc transfer 55vbdcvfg553"); + // } + + private static void eosWithdrawDeposit(String privateKeys, String creator, String newAccount, String stakeCpuQuantity, String memo) { + /** + * 业务参数判断 + */ + GetTransactionParamentDto transfer = testOfflineTransfer(privateKeys, creator, newAccount, stakeCpuQuantity, memo); +// GetTransactionParamentDto transfer = new GetTransactionParamentDto(); +// transfer.setId("4f70d57f10f305db9b2378d73734bb51b96ca858172fe089d40aa9be1fcdeac9"); +// transfer.setBlock_num_hint(BigInteger.valueOf(34808748)); + //String json = JsonUtils.objectToJson(transfer); + System.out.println("---------------------------------------"); + getTransaction(transfer); + } + + + // 获取区块高度 + public static BigInteger get_info() { + String url = EOS_RPC_URI + "/v1/chain/get_info"; + ResponseEntity body = postVisitEos(url, null); + Map innerMap = body.getBody().getInnerMap(); + System.out.println(body); + // System.out.println(blockNumber); + return (BigInteger) innerMap.get("head_block_num"); + + } + + // 获取区块交易记录 + public static void blockPay() { + // 获取区块信息 + String url = EOS_RPC_URI + "v1/chain/get_block"; + long blcokNum = 34681329; + + ResponseEntity body = get_block(blcokNum); + + // System.out.println("boby: " + body); + JSONObject bodyBody = body.getBody(); + JSONArray transactions = bodyBody.getJSONArray("transactions"); + + if (transactions.size() > 0) { + for (int i = 0; i < transactions.size(); i++) { + // 遍历 jsonarray 数组,把每一个对象转成 json 对象 + JSONObject job = transactions.getJSONObject(i); + + JSONObject trx = null; + try { + trx = job.getJSONObject("trx"); + } catch (Exception e) { + continue; + } + JSONObject transaction = trx.getJSONObject("transaction"); + JSONArray actions = transaction.getJSONArray("actions"); + JSONObject action = actions.getJSONObject(0); + // System.out.println("action: " + action); + JSONObject data = action.getJSONObject("data"); + // System.out.println("data: " + data); + + if (null != data && !"".equals(data)) { + if (data.containsKey("to")) { + // 解析区块信息,获取充值记录 + + // 交易地址:account: eosio.token + System.out.println("account: " + action.get("account")); + // 交易类型:transfer type: transfer + System.out.println("transfer type: " + action.get("name")); + // 交易方:from: aaasssxxxccc + System.out.println("from: " + data.get("from")); + // 接收方:to: 55vbdcvfg553 + System.out.println("to: " + data.get("to")); + // 交易数额:quantity: 1.0100 EOS + System.out.println("quantity: " + data.get("quantity")); + // 交易备注:memo: aaasssxxxccc transfer 55vbdcvfg553 + System.out.println("memo: " + data.get("memo")); + // 交易哈希值:hex_data: 8010eabd638c8d31304a616ba3747629742700000000000004454f530000000022616161737373787878636363207472616e7366657220353576626463766667353533 + System.out.println("hex_data: " + action.get("hex_data")); + } + } + } + } + } + + // 获取区块信息 + public static ResponseEntity get_block(long blockNumber) { + String url = EOS_RPC_URI + "v1/chain/get_block"; + Map map = new HashMap<>(); + map.put("block_num_or_id", blockNumber); + + String para = JsonUtils.objectToJson(map); + ResponseEntity body = postVisitEos(url, para); + // System.out.println(body); + return body; + } + + /** + * 提现 离线签名 + * + * @param privateKeys + * @param from + * @param to 用户指定账户提现 + * @param quantity + * @param memo + * @return + */ + public static GetTransactionParamentDto testOfflineTransfer(String privateKeys, String from, String to, String quantity, String memo) { + System.out.println("================= 开始提现 ================="); +// Rpc rpc = new Rpc("http://47.106.221.171:8888"); + // Rpc rpc = new Rpc("https://api-kylin.eosasia.one"); + // 获取离线签名参数 + SignParam params = rpc.getOfflineSignParams(60l); + // 离线签名 + OfflineSign sign = new OfflineSign(); + // 交易信息 + String content = ""; + try { + // 转账需要active的私钥 + + content = sign.transfer(params, privateKeys, "eosio.token", + from, to, quantity, memo); + System.out.println(content); + } catch (Exception e) { + e.printStackTrace(); + } + GetTransactionParamentDto getTransactionParamentDto = new GetTransactionParamentDto(); + // 广播交易 + try { + Transaction tx = rpc.pushTransaction(content); + Processed processed = tx.getProcessed(); + String transactionId = tx.getTransactionId(); + getTransactionParamentDto.setId(transactionId); + getTransactionParamentDto.setBlock_num_hint(new BigInteger(processed.getBlockNum())); + System.out.println("id: " + getTransactionParamentDto.getId()); + System.out.println("blockNum: " + getTransactionParamentDto.getBlock_num_hint()); + System.out.println("================= 提现成功 ================="); + return getTransactionParamentDto; + } catch (ApiException ex) { + System.out.println(ex.getError().getCode()); + System.out.println("================= 提现失败 ================="); + return null; + } catch (Exception e) { + e.printStackTrace(); + System.out.println("================= 提现失败 ================="); + return null; + } + + } + + /** + * 出币查询 + * + * @param transactionPara + */ + public static void getTransaction(GetTransactionParamentDto transactionPara) { + System.out.println("================= 开始出币 ================="); + if (null == transactionPara) { + // 参数异常 + System.out.println("参数异常错误"); + } + JSONObject result = getTransactionPost(transactionPara); + JSONObject trx = result.getJSONObject("trx"); + JSONObject trx1 = trx.getJSONObject("trx"); + JSONArray actions = trx1.getJSONArray("actions"); + JSONObject action = actions.getJSONObject(0); + JSONObject data = action.getJSONObject("data"); + + // token_name:account: eosio.token + String account = action.getObject("account", String.class); + System.out.println("account: " + account); + // 转账类型:transfer: transfer + String name = action.getObject("name", String.class); + System.out.println("transfer: " + name); + // 支付人地址: + String from = data.getObject("from", String.class); + System.out.println("from: " + from); + // 收款人地址: + String to = data.getObject("to", String.class); + System.out.println("to: " + to); + // 数额: + String quantity = data.getObject("quantity", String.class); + System.out.println("quantity: " + quantity); + // 备注: + String memo = data.getObject("memo", String.class); + System.out.println("memo: " + memo); + // 交易哈希值: + String hexData = action.getObject("hex_data", String.class); + System.out.println("hax_data: " + hexData); + // 区块高度 + BigInteger blockNum = result.getObject("block_num", BigInteger.class); + System.out.println("block_num: " + blockNum); + + System.out.println("================= 出币成功 ================="); + } + + public static JSONObject getTransactionPost(GetTransactionParamentDto para) { + String url = EOS_RPC_URI + GET_TRANSACTION_SUFFIX_URI; + String jsonPara = JsonUtils.objectToJson(para); + ResponseEntity jsonObjectResponseEntity = postVisitEos(url, jsonPara); + JSONObject body = jsonObjectResponseEntity.getBody(); + return body; + } + + /** + * 获得账户信息 + * + * @param account 账户名称 + * @return + */ + public static Account getAccount(String account) { + return rpc.getAccount(account); + } + + /** + * 获得链信息 + * + * @return + */ + public static ChainInfo getChainInfo() { + ChainInfo chainInfo = rpc.getChainInfo(); + System.out.println("chainInfo: " + chainInfo); + return chainInfo; + } + + /** + * 获得区块信息 + * + * @param blockNumberOrId 区块ID或者高度 + * @return + */ + public static Block getBlock(String blockNumberOrId) { + Block block = rpc.getBlock(blockNumberOrId); + System.out.println("block: " + block); + return block; + } + + + // 创建账户 + public static void testOfflineCreate(String creator, String privateKeys, String owner, String active, String newAccount) { + // Rpc rpc = new Rpc("https://api-kylin.eosasia.one"); + // 获取离线签名参数 + SignParam params = rpc.getOfflineSignParams(60l); + // 离线签名 + OfflineSign sign = new OfflineSign(); + // 交易信息 + String content = ""; + try { + content = sign.createAccount(params, privateKeys, creator, newAccount, owner, active, 8000l); + System.out.println("content: " + content); + } catch (Exception e) { + e.printStackTrace(); + } + // 广播交易 + try { + Transaction tx = rpc.pushTransaction(content); + System.out.println(tx.toString()); + } catch (ApiException ex) { + System.out.println("error: " + ex); + System.out.println(ex.getError().getCode()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // 创建账户并且抵押 + public static void testOfflineCreatePledge(String creator, String privateKeys, String owner, String active, String newAccount) { + // Rpc rpc = new Rpc("https://api-kylin.eosasia.one"); + // 获取离线签名参数 + SignParam params = rpc.getOfflineSignParams(60l); + // 离线签名 + OfflineSign sign = new OfflineSign(); + // 交易信息 + String content = ""; + try { + Transaction t2 = rpc.createAccount(privateKeys, creator, newAccount, owner, active, 8192l, "1.0100 EOS", "1.0100 EOS", 0l); + System.out.println("创建成功 = " + t2.getTransactionId() + " \n "); + } catch (Exception ex) { + ex.printStackTrace(); + } + // 广播交易 + /*try { + Transaction tx = rpc.pushTransaction(content); + System.out.println(tx.toString()); + } catch (ApiException ex) { + System.out.println("error: " + ex); + System.out.println(ex.getError().getCode()); + } catch (Exception e) { + e.printStackTrace(); + }*/ + } + + + /** + * eos get请求 + * + * @param url + * @return + */ + public static ResponseEntity getVisitEos(String url) { + ResponseEntity responseEntity = restTemplate.getForEntity(url, JSONObject.class); + // Map body = responseEntity.getBody().getInnerMap(); + return responseEntity; + } + + /** + * eos post请求 + * + * @param url + * @param parameterJson + * @return + */ + public static ResponseEntity postVisitEos(String url, String parameterJson) { + ResponseEntity responseEntity = restTemplate.postForEntity(url, parameterJson, JSONObject.class); + // System.out.println("responseEntity: " + responseEntity); + // System.out.println("-----------------"); + + // Map body = responseEntity.getBody().getInnerMap(); + // System.out.println(responseEntity); + return responseEntity; + } + + /** + * 配置HttpClient超时时间 + * + * @return + */ + private static ClientHttpRequestFactory getClientHttpRequestFactory() { + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(10000) + .setConnectTimeout(10000).build(); + CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build(); + return new HttpComponentsClientHttpRequestFactory(client); + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/GetTransactionParamentDto.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/GetTransactionParamentDto.java new file mode 100644 index 0000000..9da5a1f --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/GetTransactionParamentDto.java @@ -0,0 +1,30 @@ +package com.blockchain.server.eos.eos4j; + +import java.math.BigInteger; + +/** + * @author Harvey + * @date 2019/2/16 14:08 + * @user WIN10 + */ +public class GetTransactionParamentDto { + + private String id; + private BigInteger block_num_hint; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public BigInteger getBlock_num_hint() { + return block_num_hint; + } + + public void setBlock_num_hint(BigInteger block_num_hint) { + this.block_num_hint = block_num_hint; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/JsonUtils.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/JsonUtils.java new file mode 100644 index 0000000..e800948 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/JsonUtils.java @@ -0,0 +1,273 @@ +package com.blockchain.server.eos.eos4j; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.*; + +public class JsonUtils { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + /** + * 将对象转换成json字符串。 + * + * @param data + * @return + */ + public static String objectToJson(Object data) { + try { + String string = MAPPER.writeValueAsString(data); + return string; + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 将json结果集转化为对象 + * + * @param jsonData json数据 + * @param beanType 对象中的object类型 + * @param + * @return + */ + public static T jsonToPojo(String jsonData, Class beanType) { + try { + T t = MAPPER.readValue(jsonData, beanType); + return t; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 将json数据转换成pojo对象list + * + * @param jsonData + * @param beanType + * @param + * @return + */ + public static List jsonToList(String jsonData, Class beanType) { + JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType); + try { + List list = MAPPER.readValue(jsonData, javaType); + return list; + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + /** + * set属性的值到Bean + * + * @param bean + * @param valMap + */ + public synchronized static void setFieldValue(Object bean, Map valMap) { + Class cls = bean.getClass(); + // 取出bean里的所有方法 + Method[] methods = cls.getDeclaredMethods(); + Field[] fields = cls.getDeclaredFields(); + + for (Field field : fields) { + try { + String fieldSetName = parSetName(field.getName(), false); + if (!checkSetMet(methods, fieldSetName)) { + fieldSetName = parSetName(field.getName(), true); + if (!checkSetMet(methods, fieldSetName)) { + continue; + } + } + Method fieldSetMet = cls.getMethod(fieldSetName, field.getType()); + String value = valMap.get(field.getName()); + if (null != value && !"".equals(value)) { + String fieldType = field.getType().getSimpleName(); + if ("String".equals(fieldType)) { + fieldSetMet.invoke(bean, value); + } else if ("Date".equals(fieldType)) { + Date temp = parseDate(value); + fieldSetMet.invoke(bean, temp); + } else if ("Integer".equals(fieldType) || "int".equals(fieldType)) { + Integer intval = Integer.parseInt(value); + fieldSetMet.invoke(bean, intval); + } else if ("Long".equalsIgnoreCase(fieldType)) { + Long temp = Long.parseLong(value); + fieldSetMet.invoke(bean, temp); + } else if ("Double".equalsIgnoreCase(fieldType)) { + Double temp = Double.parseDouble(value); + fieldSetMet.invoke(bean, temp); + } else if ("Boolean".equalsIgnoreCase(fieldType)) { + Boolean temp = Boolean.parseBoolean(value); + fieldSetMet.invoke(bean, temp); + } else if ("Date".equalsIgnoreCase(fieldType)) { + Date temp = parseDate(value); + fieldSetMet.invoke(bean, temp); + } else if ("BigDecimal".equals(fieldType)) { + BigDecimal temp = new BigDecimal(value); + fieldSetMet.invoke(bean, temp); + } else { + // throw new Jc("500","not supper type" + fieldType); + } + } + } catch (Exception e) { + continue; + } + } + + } + + /** + * 拼接某属性的 get方法 + * + * @param fieldName + * @return String + */ + private static String parGetName(String fieldName, boolean firstLow) { + if (null == fieldName || "".equals(fieldName)) { + return null; + } + if (firstLow && fieldName.length() > 1) { //判断是否为 第一个字母小写,第二个字母大写 + if (Character.isLowerCase(fieldName.charAt(0)) && Character.isUpperCase(fieldName.charAt(1))) { + return "get" + fieldName; + } + } + return "get" + fieldName.substring(0, 1).toUpperCase() + + fieldName.substring(1); + } + + /** + * 拼接在某属性的 set方法 + * + * @param fieldName + * @return String + */ + private static String parSetName(String fieldName, boolean firstLow) { + if (null == fieldName || "".equals(fieldName)) { + return null; + } + if (firstLow && fieldName.length() > 1) { //判断是否为 第一个字母小写,第二个字母大写 + if (Character.isLowerCase(fieldName.charAt(0)) && Character.isUpperCase(fieldName.charAt(1))) { + return "set" + fieldName; + } + } + return "set" + fieldName.substring(0, 1).toUpperCase() + + fieldName.substring(1); + } + + /** + * 格式化string为Date + * + * @param datestr + * @return date + */ + private static Date parseDate(String datestr) { + if (null == datestr || "".equals(datestr)) { + return null; + } + try { + String fmtstr = null; + if (datestr.indexOf(':') > 0) { + fmtstr = "yyyy-MM-dd HH:mm:ss"; + } else { + fmtstr = "yyyy-MM-dd"; + } + SimpleDateFormat sdf = new SimpleDateFormat(fmtstr, Locale.UK); + return sdf.parse(datestr); + } catch (Exception e) { + return null; + } + } + + /** + * 日期转化为String + * + * @param date + * @return date string + */ + private static String fmtDate(Date date) { + if (null == date) { + return null; + } + try { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); + return sdf.format(date); + } catch (Exception e) { + return null; + } + } + + /** + * 判断是否存在某属性的 set方法 + * + * @param methods + * @param fieldSetMet + * @return boolean + */ + private static boolean checkSetMet(Method[] methods, String fieldSetMet) { + for (Method met : methods) { + if (fieldSetMet.equals(met.getName())) { + return true; + } + } + return false; + } + + /** + * 判断是否存在某属性的 get方法 + * + * @param methods + * @param fieldGetMet + * @return boolean + */ + private static boolean checkGetMet(Method[] methods, String fieldGetMet) { + for (Method met : methods) { + if (fieldGetMet.equals(met.getName())) { + return true; + } + } + return false; + } + + /** + * 使用json的方式转换两个pojo类(源是包含下划线属性的) + * + * @param source 源对象 + * @param destCls 目标类 + * @param + * @return 目标对象 + */ + public static D mapLowerCaseWithUnderscoresSource(Object source, Class destCls) { + String sourceJson = objectToJson(source); + ObjectMapper objectMapper = new ObjectMapper(); + /** 设置映射下划线 */ + objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + try { + return objectMapper.readValue(sourceJson, destCls); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public static Map jsonToHashMap(String sourceJson, Class sourceClazz, Class destClazz) { + try { + return MAPPER.readValue(sourceJson, MAPPER.getTypeFactory().constructMapType(HashMap.class, sourceClazz, destClazz)); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/OfflineSign.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/OfflineSign.java new file mode 100644 index 0000000..1fc5d0a --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/OfflineSign.java @@ -0,0 +1,142 @@ +package com.blockchain.server.eos.eos4j; + +import com.blockchain.server.eos.eos4j.api.vo.SignParam; +import com.blockchain.server.eos.eos4j.api.vo.transaction.push.Tx; +import com.blockchain.server.eos.eos4j.api.vo.transaction.push.TxAction; +import com.blockchain.server.eos.eos4j.api.vo.transaction.push.TxRequest; +import com.blockchain.server.eos.eos4j.api.vo.transaction.push.TxSign; +import com.blockchain.server.eos.eos4j.ese.Action; +import com.blockchain.server.eos.eos4j.ese.DataParam; +import com.blockchain.server.eos.eos4j.ese.DataType; +import com.blockchain.server.eos.eos4j.ese.Ese; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * + * @author espritblock http://eblock.io + * + */ +public class OfflineSign { + + SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + + public OfflineSign() { + dateFormatter.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + /** + * + * @param compression + * @param pushTransaction + * @param signatures + * @return + * @throws Exception + */ + public String pushTransaction(String compression, Tx pushTransaction, String[] signatures) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + String mapJakcson = mapper.writeValueAsString(new TxRequest(compression, pushTransaction, signatures)); + return mapJakcson; + } + + /** + * 离线签名转账 + * + * @param signParam + * @param pk + * @param contractAccount + * @param from + * @param to + * @param quantity + * @param memo + * @return + * @throws Exception + */ + public String transfer(SignParam signParam, String pk, String contractAccount, String from, String to, + String quantity, String memo) throws Exception { + Tx tx = new Tx(); + tx.setExpiration(signParam.getHeadBlockTime().getTime() / 1000 + signParam.getExp()); + tx.setRef_block_num(signParam.getLastIrreversibleBlockNum()); + tx.setRef_block_prefix(signParam.getRefBlockPrefix()); + tx.setNet_usage_words(0l); + tx.setMax_cpu_usage_ms(0l); + tx.setDelay_sec(0l); + // actions + List actions = new ArrayList<>(); + // data + Map dataMap = new LinkedHashMap<>(); + dataMap.put("from", from); + dataMap.put("to", to); + dataMap.put("quantity", new DataParam(quantity, DataType.asset, Action.transfer).getValue()); + dataMap.put("memo", memo); + // action + TxAction action = new TxAction(from, contractAccount, "transfer", dataMap); + actions.add(action); + tx.setActions(actions); + // sgin + String sign = Ecc.signTransaction(pk, new TxSign(signParam.getChainId(), tx)); + // data parse + String data = Ecc.parseTransferData(from, to, quantity, memo); + // reset data + action.setData(data); + // reset expiration + tx.setExpiration(dateFormatter.format(new Date(1000 * Long.parseLong(tx.getExpiration().toString())))); + return pushTransaction("none", tx, new String[] { sign }); + } + + /** + * 离线签名创建账户 + * + * @param signParam + * @param pk + * @param creator + * @param newAccount + * @param owner + * @param active + * @param buyRam + * @return + * @throws Exception + */ + public String createAccount(SignParam signParam, String pk, String creator, String newAccount, String owner, + String active, Long buyRam) throws Exception { + Tx tx = new Tx(); + tx.setExpiration(signParam.getHeadBlockTime().getTime() / 1000 + signParam.getExp()); + tx.setRef_block_num(signParam.getLastIrreversibleBlockNum()); + tx.setRef_block_prefix(signParam.getRefBlockPrefix()); + tx.setNet_usage_words(0l); + tx.setMax_cpu_usage_ms(0l); + tx.setDelay_sec(0l); + // actions + List actions = new ArrayList<>(); + tx.setActions(actions); + // create + Map createMap = new LinkedHashMap<>(); + createMap.put("creator", creator); + createMap.put("name", newAccount); + createMap.put("owner", owner); + createMap.put("active", active); + TxAction createAction = new TxAction(creator, "eosio", "newaccount", createMap); + actions.add(createAction); + // buyrap + Map buyMap = new LinkedHashMap<>(); + buyMap.put("payer", creator); + buyMap.put("receiver", newAccount); + buyMap.put("bytes", buyRam); + TxAction buyAction = new TxAction(creator, "eosio", "buyrambytes", buyMap); + actions.add(buyAction); + // sgin + String sign = Ecc.signTransaction(pk, new TxSign(signParam.getChainId(), tx)); + // System.out.println("sign: " + sign); + // data parse + String accountData = Ese.parseAccountData(creator, newAccount, owner, active); + createAction.setData(accountData); + // data parse + String ramData = Ese.parseBuyRamData(creator, newAccount, buyRam); + buyAction.setData(ramData); + // reset expiration + tx.setExpiration(dateFormatter.format(new Date(1000 * Long.parseLong(tx.getExpiration().toString())))); + return pushTransaction("none", tx, new String[] { sign }); + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/OfflineTest.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/OfflineTest.java new file mode 100644 index 0000000..150338f --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/OfflineTest.java @@ -0,0 +1,82 @@ +package com.blockchain.server.eos.eos4j; + + +import com.blockchain.server.eos.eos4j.api.exception.ApiException; +import com.blockchain.server.eos.eos4j.api.vo.SignParam; +import com.blockchain.server.eos.eos4j.api.vo.transaction.Transaction; + +/** + * + * @author espritblock http://eblock.io + * + */ +public class OfflineTest { + + /*public static void main(String[] args) { + testOfflineCreate(); +// testOfflineTransfer(); + }*/ + + public static void testOfflineCreate() { +// Rpc rpc = new Rpc("http://47.106.221.171:8888"); + Rpc rpc = null; // new Rpc("https://api-kylin.eosasia.one"); + // 获取离线签名参数 + SignParam params = rpc.getOfflineSignParams(60l); + // 离线签名 + OfflineSign sign = new OfflineSign(); + // 交易信息 + String content = ""; + try { + /*content = sign.createAccount(params, "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3", "eeeeeeeeeeee", + "555555555551", "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", + "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV", 8000l);*/ + content = sign.createAccount(params, "5KBR1zJT1VYxcHDAB1MgDqmXufvFYJDmWtwZGVMUDGottR5k5gJ", "aaasssxxxccc", + "5555dcvfg551", "EOS8FDspV2gWzQgMJNWj8ZbMG9fTcLVJbNPTNFEtNxCAvJH4kfKKd", + "EOS8FDspV2gWzQgMJNWj8ZbMG9fTcLVJbNPTNFEtNxCAvJH4kfKKd", 8000l); + System.out.println(content); + } catch (Exception e) { + e.printStackTrace(); + } + // 广播交易 + try { + Transaction tx = rpc.pushTransaction(content); + System.out.println(tx.toString()); + System.out.println(tx.getTransactionId()); + } catch (ApiException ex) { + System.out.println(ex.getError().getCode()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void testOfflineTransfer() { +// Rpc rpc = new Rpc("http://47.106.221.171:8888"); + Rpc rpc = null; // new Rpc("https://api-kylin.eosasia.one"); + // 获取离线签名参数 + SignParam params = rpc.getOfflineSignParams(60l); + // 离线签名 + OfflineSign sign = new OfflineSign(); + // 交易信息 + String content = ""; + try { + // 转账需要active的私钥 + /*content = sign.transfer(params, "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3", "eosio.token", + "eeeeeeeeeeee", "555555555551", "372.0993 EOS", "test");*/ + content = sign.transfer(params, "5KBR1zJT1VYxcHDAB1MgDqmXufvFYJDmWtwZGVMUDGottR5k5gJ", "eosio.token", + "aaasssxxxccc", "scfpolkiolkm", "10.0000 EOS", "test123"); + System.out.println(content); + } catch (Exception e) { + e.printStackTrace(); + } + // 广播交易 + try { + Transaction tx = rpc.pushTransaction(content); + System.out.println(tx.getTransactionId()); + System.out.println(tx.toString()); + } catch (ApiException ex) { + System.out.println(ex.getError().getCode()); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/Rpc.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/Rpc.java new file mode 100644 index 0000000..5df1025 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/Rpc.java @@ -0,0 +1,513 @@ +package com.blockchain.server.eos.eos4j; + +import com.alibaba.fastjson.JSONObject; +import com.blockchain.server.eos.common.enums.EosWalletEnums; +import com.blockchain.server.eos.common.exception.EosWalletException; +import com.blockchain.server.eos.eos4j.api.service.RpcService; +import com.blockchain.server.eos.eos4j.api.utils.Generator; +import com.blockchain.server.eos.eos4j.api.vo.*; +import com.blockchain.server.eos.eos4j.api.vo.account.Account; +import com.blockchain.server.eos.eos4j.api.vo.transaction.Transaction; +import com.blockchain.server.eos.eos4j.api.vo.transaction.push.Tx; +import com.blockchain.server.eos.eos4j.api.vo.transaction.push.TxAction; +import com.blockchain.server.eos.eos4j.api.vo.transaction.push.TxRequest; +import com.blockchain.server.eos.eos4j.api.vo.transaction.push.TxSign; +import com.blockchain.server.eos.eos4j.ese.Action; +import com.blockchain.server.eos.eos4j.ese.DataParam; +import com.blockchain.server.eos.eos4j.ese.DataType; +import com.blockchain.server.eos.eos4j.ese.Ese; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; +import retrofit2.Call; + +import java.math.BigInteger; +import java.text.SimpleDateFormat; +import java.util.*; + + +public class Rpc { + + private final RpcService rpcService; + private static final RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory()); + + + SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + + public Rpc(String baseUrl) { + dateFormatter.setTimeZone(TimeZone.getTimeZone("UTC")); + rpcService = Generator.createService(RpcService.class, baseUrl); + } + + /** + * 获得链信息 + * + * @return + */ + public ChainInfo getChainInfo() { + return Generator.executeSync(rpcService.getChainInfo()); + } + + /** + * 获得区块信息 + * + * @param blockNumberOrId + * 区块ID或者高度 + * @return + */ + public Block getBlock(String blockNumberOrId) { + return Generator.executeSync(rpcService.getBlock(Collections.singletonMap("block_num_or_id", blockNumberOrId))); + } + + /** + * 获得账户信息 + * + * @param account + * 账户名称 + * @return + */ + public Account getAccount(String account) { + return Generator.executeSync(rpcService.getAccount(Collections.singletonMap("account_name", account))); + } + + + /*public Transaction getTransaction(String id, String blockNum){ + LinkedHashMap requestParameters = new LinkedHashMap<>(1); + + requestParameters.put("id", id); + requestParameters.put("block_num_hint", blockNum); + + return Generator.executeSync(rpcService.getTransaction(requestParameters)); + }*/ + + /** + * 获得table数据 + * + * @param req + * @return + */ + public TableRows getTableRows(TableRowsReq req) { + return Generator.executeSync(rpcService.getTableRows(req)); + } + + /** + * 发送请求 + * + * @param compression + * 压缩 + * @param pushTransaction + * 交易 + * @param signatures + * 签名 + * @return + * @throws Exception + */ + public Transaction pushTransaction(String compression, Tx pushTransaction, String[] signatures) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + String mapJakcson = mapper.writeValueAsString(new TxRequest(compression, pushTransaction, signatures)); + System.out.println(mapJakcson); + return Generator + .executeSync(rpcService.pushTransaction(new TxRequest(compression, pushTransaction, signatures))); + } + + /** + * 发送交易 + * + * @param tx + * @return + * @throws Exception + */ + public Transaction pushTransaction(String tx) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + TxRequest txObj = mapper.readValue(tx, TxRequest.class); + // TODO: 2019/2/14 + Call transactionCall = rpcService.pushTransaction(txObj); + System.out.println("transactionCall: " + transactionCall); + return Generator.executeSync(transactionCall); + } + + /** + * 获取离线签名参数 + * + * @param exp + * 过期时间秒 + * @return + */ + public SignParam getOfflineSignParams(Long exp) { + SignParam params = new SignParam(); + ChainInfo info = getChainInfo(); + Block block = getBlock(info.getLastIrreversibleBlockNum().toString()); + params.setChainId(info.getChainId()); + params.setHeadBlockTime(info.getHeadBlockTime()); + params.setLastIrreversibleBlockNum(info.getLastIrreversibleBlockNum()); + params.setRefBlockPrefix(block.getRefBlockPrefix()); + params.setExp(exp); + return params; + } + + /** + * 转账 + * + * @param pk + * 私钥 + * @param contractAccount + * 合约账户 + * @param from + * 从 + * @param to + * 到 + * @param quantity + * 币种金额 + * @param memo + * 留言 + * @return + * @throws Exception + */ + public Transaction transfer(String pk, String contractAccount, String from, String to, String quantity, String memo) + throws Exception { + // get chain info + ChainInfo info = getChainInfo(); +// info.setChainId("cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f"); +// info.setLastIrreversibleBlockNum(826366l); +// info.setHeadBlockTime(dateFormatter.parse("2018-08-22T09:19:01.000")); + // get block info + Block block = getBlock(info.getLastIrreversibleBlockNum().toString()); +// block.setRefBlockPrefix(2919590658l); + // tx + Tx tx = new Tx(); + tx.setExpiration(info.getHeadBlockTime().getTime() / 1000 + 60); + tx.setRef_block_num(info.getLastIrreversibleBlockNum()); + tx.setRef_block_prefix(block.getRefBlockPrefix()); + tx.setNet_usage_words(0l); + tx.setMax_cpu_usage_ms(0l); + tx.setDelay_sec(0l); + // actions + List actions = new ArrayList<>(); + // data + Map dataMap = new LinkedHashMap<>(); + dataMap.put("from", from); + dataMap.put("to", to); + dataMap.put("quantity", new DataParam(quantity, DataType.asset, Action.transfer).getValue()); + dataMap.put("memo", memo); + // action + TxAction action = new TxAction(from, contractAccount, "transfer", dataMap); + actions.add(action); + tx.setActions(actions); + // sgin + String sign = Ecc.signTransaction(pk, new TxSign(info.getChainId(), tx)); + // data parse + String data = Ecc.parseTransferData(from, to, quantity, memo); + // reset data + action.setData(data); + // reset expiration + tx.setExpiration(dateFormatter.format(new Date(1000 * Long.parseLong(tx.getExpiration().toString())))); + return pushTransaction("none", tx, new String[] { sign }); + } + + /** + * 创建账户 + * + * @param pk + * 私钥 + * @param creator + * 创建者 + * @param newAccount + * 新账户 + * @param owner + * 公钥 + * @param active + * 公钥 + * @param buyRam + * ram + * @return + * @throws Exception + */ + public Transaction createAccount(String pk, String creator, String newAccount, String owner, String active, + Long buyRam) throws Exception { + // get chain info + ChainInfo info = getChainInfo(); + // get block info + Block block = getBlock(info.getLastIrreversibleBlockNum().toString()); + // tx + Tx tx = new Tx(); + tx.setExpiration(info.getHeadBlockTime().getTime() / 1000 + 60); + tx.setRef_block_num(info.getLastIrreversibleBlockNum()); + tx.setRef_block_prefix(block.getRefBlockPrefix()); + tx.setNet_usage_words(0l); + tx.setMax_cpu_usage_ms(0l); + tx.setDelay_sec(0l); + // actions + List actions = new ArrayList<>(); + tx.setActions(actions); + // create + Map createMap = new LinkedHashMap<>(); + createMap.put("creator", creator); + createMap.put("name", newAccount); + createMap.put("owner", owner); + createMap.put("active", active); + TxAction createAction = new TxAction(creator, "eosio", "newaccount", createMap); + actions.add(createAction); + // buyrap + Map buyMap = new LinkedHashMap<>(); + buyMap.put("payer", creator); + buyMap.put("receiver", newAccount); + buyMap.put("bytes", buyRam); + TxAction buyAction = new TxAction(creator, "eosio", "buyrambytes", buyMap); + actions.add(buyAction); + // sgin + String sign = Ecc.signTransaction(pk, new TxSign(info.getChainId(), tx)); + // data parse + String accountData = Ese.parseAccountData(creator, newAccount, owner, active); + createAction.setData(accountData); + // data parse + String ramData = Ese.parseBuyRamData(creator, newAccount, buyRam); + buyAction.setData(ramData); + // reset expiration + tx.setExpiration(dateFormatter.format(new Date(1000 * Long.parseLong(tx.getExpiration().toString())))); + return pushTransaction("none", tx, new String[] { sign }); + } + + /** + * 创建账户 + * + * @param pk + * 私钥 + * @param creator + * 创建者 + * @param newAccount + * 新账户 + * @param owner + * 公钥 + * @param active + * 公钥 + * @param buyRam + * 购买空间数量 + * @param stakeNetQuantity + * 网络抵押 + * @param stakeCpuQuantity + * cpu抵押 + * @param transfer + * 抵押资产是否转送给对方,0自己所有,1对方所有 + * @return + * @throws Exception + */ + public Transaction createAccount(String pk, String creator, String newAccount, String owner, String active, + Long buyRam, String stakeNetQuantity, String stakeCpuQuantity, Long transfer) throws Exception { + // get chain info + ChainInfo info = getChainInfo(); + // info.setChainId("cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f"); + // info.setLastIrreversibleBlockNum(22117l); + // get block info + Block block = getBlock(info.getLastIrreversibleBlockNum().toString()); + // block.setRefBlockPrefix(3920078619l); + // tx + Tx tx = new Tx(); + tx.setExpiration(info.getHeadBlockTime().getTime() / 1000 + 60); + // tx.setExpiration(1528436078); + tx.setRef_block_num(info.getLastIrreversibleBlockNum()); + tx.setRef_block_prefix(block.getRefBlockPrefix()); + tx.setNet_usage_words(0l); + tx.setMax_cpu_usage_ms(0l); + tx.setDelay_sec(0l); + // actions + List actions = new ArrayList<>(); + tx.setActions(actions); + // create + Map createMap = new LinkedHashMap<>(); + createMap.put("creator", creator); + createMap.put("name", newAccount); + createMap.put("owner", owner); + createMap.put("active", active); + TxAction createAction = new TxAction(creator, "eosio", "newaccount", createMap); + actions.add(createAction); + // buyrap + Map buyMap = new LinkedHashMap<>(); + buyMap.put("payer", creator); + buyMap.put("receiver", newAccount); + buyMap.put("bytes", buyRam); + TxAction buyAction = new TxAction(creator, "eosio", "buyrambytes", buyMap); + actions.add(buyAction); + // buyrap + Map delMap = new LinkedHashMap<>(); + delMap.put("from", creator); + delMap.put("receiver", newAccount); + delMap.put("stake_net_quantity", new DataParam(stakeNetQuantity, DataType.asset, Action.delegate).getValue()); + delMap.put("stake_cpu_quantity", new DataParam(stakeCpuQuantity, DataType.asset, Action.delegate).getValue()); + delMap.put("transfer", transfer); + TxAction delAction = new TxAction(creator, "eosio", "delegatebw", delMap); + actions.add(delAction); + // // sgin + String sign = Ecc.signTransaction(pk, new TxSign(info.getChainId(), tx)); + // data parse + String accountData = Ese.parseAccountData(creator, newAccount, owner, active); + createAction.setData(accountData); + // data parse + String ramData = Ese.parseBuyRamData(creator, newAccount, buyRam); + buyAction.setData(ramData); + // data parse + String delData = Ese.parseDelegateData(creator, newAccount, stakeNetQuantity, stakeCpuQuantity, + transfer.intValue()); + delAction.setData(delData); + // reset expiration + tx.setExpiration(dateFormatter.format(new Date(1000 * Long.parseLong(tx.getExpiration().toString())))); + return pushTransaction("none", tx, new String[] { sign }); + } + + /** + * + * @param pk + * @param voter + * @param proxy + * @param producers + * @return + * @throws Exception + */ + public Transaction voteproducer(String pk,String voter,String proxy,List producers) throws Exception { + Comparator comparator = (h1, h2) -> h2.compareTo(h1); + producers.sort(comparator.reversed()); + // get chain info + ChainInfo info = getChainInfo(); + // get block info + Block block = getBlock(info.getLastIrreversibleBlockNum().toString()); + // tx + Tx tx = new Tx(); + tx.setExpiration(info.getHeadBlockTime().getTime() / 1000 + 60); + tx.setRef_block_num(info.getLastIrreversibleBlockNum()); + tx.setRef_block_prefix(block.getRefBlockPrefix()); + tx.setNet_usage_words(0l); + tx.setMax_cpu_usage_ms(0l); + tx.setDelay_sec(0l); + // actions + List actions = new ArrayList<>(); + // data + Map dataMap = new LinkedHashMap<>(); + dataMap.put("voter", voter); + dataMap.put("proxy", proxy); + dataMap.put("producers",producers); + // action + TxAction action = new TxAction(voter, "eosio", "voteproducer", dataMap); + actions.add(action); + tx.setActions(actions); + // sgin + String sign = Ecc.signTransaction(pk, new TxSign(info.getChainId(), tx)); + // data parse + String data = Ecc.parseVoteProducerData(voter, proxy, producers); + // reset data + action.setData(data); + // reset expiration + tx.setExpiration(dateFormatter.format(new Date(1000 * Long.parseLong(tx.getExpiration().toString())))); + return pushTransaction("none", tx, new String[] { sign }); + } + + /** + * 根据账户获取用户历史交易信息 + * + * @param url + * @param accountName + * @return + */ + public ResponseEntity getActions(String url, String accountName, String offset) { + Map map = new HashMap<>(); + map.put("pos", "-1"); + map.put("offset", offset); + map.put("account_name", accountName); + String para = JsonUtils.objectToJson(map); + return postVisitEos(url, para); + } + + /** + * token close + * @param owner + * @param symbol + * @return + * @throws Exception + */ + public Transaction close(String pk,String contract,String owner, String symbol)throws Exception { + ChainInfo info = getChainInfo(); + Block block = getBlock(info.getLastIrreversibleBlockNum().toString()); + Tx tx = new Tx(); + tx.setExpiration(info.getHeadBlockTime().getTime() / 1000 + 60); + tx.setRef_block_num(info.getLastIrreversibleBlockNum()); + tx.setRef_block_prefix(block.getRefBlockPrefix()); + tx.setNet_usage_words(0l); + tx.setMax_cpu_usage_ms(0l); + tx.setDelay_sec(0l); + // actions + List actions = new ArrayList<>(); + // data + Map dataMap = new LinkedHashMap<>(); + dataMap.put("close-owner", owner); + dataMap.put("close-symbol", new DataParam(symbol, DataType.symbol, Action.close).getValue()); + // action + TxAction action = new TxAction(owner,contract,"close",dataMap); + actions.add(action); + tx.setActions(actions); + // sgin + String sign = Ecc.signTransaction(pk, new TxSign(info.getChainId(), tx)); + // data parse + String data = Ecc.parseCloseData(owner, symbol); + // reset data + action.setData(data); + // reset expiration + tx.setExpiration(dateFormatter.format(new Date(1000 * Long.parseLong(tx.getExpiration().toString())))); + return pushTransaction("none", tx, new String[] { sign }); + } + + + // 获取区块信息 + public ResponseEntity get_block(String url, BigInteger blockNumber) { + Map map = new HashMap<>(); + map.put("block_num_or_id", blockNumber); + + String para = JsonUtils.objectToJson(map); + ResponseEntity body = postVisitEos(url, para); + // System.out.println(body); + return body; + } + + public ResponseEntity getTransaction(String url, String blockNum, String hashId) { + Map map = new HashMap<>(); + map.put("id", hashId); + map.put("block_num_hint", blockNum); + String para = JsonUtils.objectToJson(map); + ResponseEntity body = postVisitEos(url, para); + // System.out.println(body); + return body; + } + + /** + * eos post请求 + * + * @param url + * @param parameterJson + * @return + */ + public ResponseEntity postVisitEos(String url, String parameterJson) { + ResponseEntity responseEntity = null; + try { + responseEntity = restTemplate.postForEntity(url, parameterJson, JSONObject.class); + } catch (Exception e) { + e.printStackTrace(); + throw new EosWalletException(EosWalletEnums.EOS_RPC_ERROR); + } + return responseEntity; + } + + + /** + * 配置HttpClient超时时间 + * + * @return + */ + private static ClientHttpRequestFactory getClientHttpRequestFactory() { + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(10000) + .setConnectTimeout(10000).build(); + CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).build(); + return new HttpComponentsClientHttpRequestFactory(client); + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/Test.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/Test.java new file mode 100644 index 0000000..bdcd58b --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/Test.java @@ -0,0 +1,95 @@ +package com.blockchain.server.eos.eos4j; + +import com.blockchain.server.eos.eos4j.api.exception.ApiException; +import com.blockchain.server.eos.eos4j.api.vo.transaction.Transaction; + +import java.util.ArrayList; +import java.util.List; + +public class Test { + + static final String eosjs_transfer_seriz = "00f2d4142123e95d0000c85353840ccdb486010000000000045359530000000019e6b58be8af95313233616263646f2e2f2c2e2f214023232425"; + + static final String eosjs_account_seriz = "0000000000ea30550002a2f164772b5601000000010003ee4221c9c3f4f62646e3c758dbb8abaae506a559f67148a76968fa6b0f0868140100000001000000010003ba8de2f029cae85e7ca5c9f591bb17b86d750c5116cec30d94100e16e446d41501000000"; + + /*public static void main(String[] args){ + System.out.println("******************* Ecc Start *******************\n"); + + + System.out.println("============= 通过种子生成私钥 ==============="); + String pk = Ecc.seedPrivate("!@#$%^&*(lajdlkjaksjdlkjaskldM<>?87126162kajsdjlaksd kajdlkaslkd heiuheijpe f[a- si0ausd9asd ahsdvcyasdcasdc ajhsdg8ca" + + "we asds JHDKAHDKKASDKJALSKDKA ooidjajsdua09sid0asdo[paksdajsdlklasdmlk FJKLIKNLK;B/;LP[P'NC;PO'; OOPO;L0[" + + "XP'C'[FG[" + + "19218728909107328972309289832098012"); + System.out.println("private key :" + pk +"\n"); + + System.out.println("============= 通过私钥生成公钥 ==============="); + String pu = Ecc.privateToPublic(pk); + System.out.println("public key :" + pu + " \n "); + + System.out.println("============= 自定义数据签名 ==============="); + String sign = Ecc.sign(pk,"is京東價as看到可可是是是@#¥%……&*(CVBNM《d "); + System.out.println("sign :" + sign + " \n "); + + System.out.println("============= 转账数据序列化 ==============="); + String data = Ecc.parseTransferData("fromaccount", "toaccount", "10.0020 SYS", "测试123abcdo./,./!@##$%"); + System.out.println("seriz data :" + data); + System.out.println("transfer eq eosjs seriz " + data.equals(eosjs_transfer_seriz)+" \n "); + + System.out.println("============= 创建账户数据序列化 ==============="); + String data1 = Ecc.parseAccountData("eosio", "espritbloc1.","EOS8eAX54cJtAngV2V22WZhRCW7e4sTAZz1mC5U22vp8mAGuFdMXx","EOS8FPooohZiiCAYXahWCQRxgXXzUbS2gNELAeYCUgGdDMbd2FHQT"); + System.out.println("seriz data :" + data1); + System.out.println("account eq eosjs seriz " + data1.equals(eosjs_account_seriz)); + + + System.out.println("\n******************* Ecc End *******************\n\n\n"); + + System.out.println("******************* Rpc Start *******************\n"); + + Rpc rpc = new Rpc("http://47.106.221.171:8888"); + + System.out.println("============= 转账 ==============="); + try { + Transaction t1 = rpc.transfer("5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3","espritblocke", "inita","initb", "12.2821 MSP", ""); + System.out.println("转账成功 = " + t1.getTransactionId()+" \n "); + }catch(Exception ex) { + ex.printStackTrace(); + } + + System.out.println("============= 创建账户并且抵押 ==============="); + try { + Transaction t2 = rpc.createAccount("5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3","eosio","ccccc..bbbbb", "EOS8eAX54cJtAngV2V22WZhRCW7e4sTAZz1mC5U22vp8mAGuFdMXx","EOS8eAX54cJtAngV2V22WZhRCW7e4sTAZz1mC5U22vp8mAGuFdMXx", 8192l, "0.01 SYS","0.01 SYS", 0l); + System.out.println("创建成功 = " + t2.getTransactionId()+" \n "); + }catch(Exception ex) { + ex.printStackTrace(); + } + System.out.println("============= 创建账户不抵押 ==============="); + try { + Transaction t3 = rpc.createAccount("5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3","eosio","bbbb..54321", "EOS8eAX54cJtAngV2V22WZhRCW7e4sTAZz1mC5U22vp8mAGuFdMXx","EOS8eAX54cJtAngV2V22WZhRCW7e4sTAZz1mC5U22vp8mAGuFdMXx", 8192l); + System.out.println("创建成功 = " + t3.getTransactionId()+" \n "); + }catch(Exception ex) { + ex.printStackTrace(); + } + + System.out.println("============= 代理投票 ==============="); + try { + List produces = new ArrayList<>(); + produces.add("pppppeeeeooo"); + produces.add("mdddssssddds"); + produces.add("mdjddjddddds"); + rpc.voteproducer("5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3", "epskdkdsddss","iuewjdkslsdc",produces); + } catch (Exception e) { + e.printStackTrace(); + } + + System.out.println("============= 关闭余额为0的token ==============="); + try { + rpc.close("5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3", "合约账户", "关闭账户", "0.0000 IPOS"); + }catch(ApiException e) { + e.printStackTrace(); + }catch(Exception ex) { + ex.printStackTrace(); + } + System.out.println("\n******************* Rpc End *******************"); + }*/ +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/ApiError.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/ApiError.java new file mode 100644 index 0000000..ead0894 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/ApiError.java @@ -0,0 +1,39 @@ +package com.blockchain.server.eos.eos4j.api.exception; + +/** + * + * @author espritblock http://eblock.io + * + */ +public class ApiError { + + private String message; + + private String code; + + private Error error; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public Error getError() { + return error; + } + + public void setError(Error error) { + this.error = error; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/ApiException.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/ApiException.java new file mode 100644 index 0000000..713d9a1 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/ApiException.java @@ -0,0 +1,40 @@ +package com.blockchain.server.eos.eos4j.api.exception; + +/** + * + * @author espritblock http://eblock.io + * + */ +public class ApiException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private ApiError error; + + public ApiException(ApiError apiError) { + this.error = apiError; + } + + public ApiException(Throwable cause) { + super(cause); + } + + public ApiError getError() { + return error; + } + + public void setError(ApiError error) { + this.error = error; + } + + @Override + public String getMessage() { + if (error != null) { + return error.getMessage(); + } + return super.getMessage(); + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/Error.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/Error.java new file mode 100644 index 0000000..bbc1aa6 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/Error.java @@ -0,0 +1,54 @@ +package com.blockchain.server.eos.eos4j.api.exception; + +/** + * + * @author espritblock http://eblock.io + * + */ +public class Error { + + private String code; + + private String name; + + private String what; + + private ErrorDetails[] details; + + private Error() { + + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getWhat() { + return what; + } + + public void setWhat(String what) { + this.what = what; + } + + public ErrorDetails[] getDetails() { + return details; + } + + public void setDetails(ErrorDetails[] details) { + this.details = details; + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/ErrorDetails.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/ErrorDetails.java new file mode 100644 index 0000000..c27afde --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/exception/ErrorDetails.java @@ -0,0 +1,57 @@ +package com.blockchain.server.eos.eos4j.api.exception; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * @author espritblock http://eblock.io + * + */ +public class ErrorDetails { + + private String message; + + private String file; + + private Integer lineNumber; + + private String method; + + private ErrorDetails() { + + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = file; + } + + public Integer getLineNumber() { + return lineNumber; + } + + @JsonProperty("line_number") + public void setLineNumber(Integer lineNumber) { + this.lineNumber = lineNumber; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/service/RpcService.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/service/RpcService.java new file mode 100644 index 0000000..4c66e19 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/service/RpcService.java @@ -0,0 +1,42 @@ +package com.blockchain.server.eos.eos4j.api.service; + +import com.blockchain.server.eos.eos4j.api.vo.Block; +import com.blockchain.server.eos.eos4j.api.vo.ChainInfo; +import com.blockchain.server.eos.eos4j.api.vo.TableRows; +import com.blockchain.server.eos.eos4j.api.vo.TableRowsReq; +import com.blockchain.server.eos.eos4j.api.vo.account.Account; +import com.blockchain.server.eos.eos4j.api.vo.transaction.Transaction; +import com.blockchain.server.eos.eos4j.api.vo.transaction.push.TxRequest; +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.GET; +import retrofit2.http.POST; + +import java.util.Map; + +/** + * + * @author espritblock http://eblock.io + * + */ +public interface RpcService { + + @GET("/v1/chain/get_info") + Call getChainInfo(); + + @POST("/v1/chain/get_block") + Call getBlock(@Body Map requestFields); + + @POST("/v1/chain/get_account") + Call getAccount(@Body Map requestFields); + + @POST("/v1/chain/push_transaction") + Call pushTransaction(@Body TxRequest request); + + @POST("/v1/chain/get_table_rows") + Call getTableRows(@Body TableRowsReq request); + + // @POST("/v1/history/get_transaction") + // Call getTransaction(@Body Map requestFields); + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/utils/Generator.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/utils/Generator.java new file mode 100644 index 0000000..004e955 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/utils/Generator.java @@ -0,0 +1,48 @@ +package com.blockchain.server.eos.eos4j.api.utils; + +import com.blockchain.server.eos.eos4j.api.exception.ApiError; +import com.blockchain.server.eos.eos4j.api.exception.ApiException; +import okhttp3.OkHttpClient; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.jackson.JacksonConverterFactory; + +import java.io.IOException; +import java.lang.annotation.Annotation; + +public class Generator { + + private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder(); + + private static Retrofit.Builder builder = new Retrofit.Builder() + .addConverterFactory(JacksonConverterFactory.create()); + + private static Retrofit retrofit; + + public static S createService(Class serviceClass, String baseUrl) { + builder.baseUrl(baseUrl); + builder.client(httpClient.build()); + builder.addConverterFactory(JacksonConverterFactory.create()); + retrofit = builder.build(); + return retrofit.create(serviceClass); + } + + public static T executeSync(Call call) { + try { + Response response = call.execute(); + if (response.isSuccessful()) { + return response.body(); + } else { + ApiError apiError = getApiError(response); + throw new ApiException(apiError); + } + } catch (IOException e) { + throw new ApiException(e); + } + } + + private static ApiError getApiError(Response response) throws IOException, ApiException { + return (ApiError) retrofit.responseBodyConverter(ApiError.class, new Annotation[0]).convert(response.errorBody()); + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/BaseVo.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/BaseVo.java new file mode 100644 index 0000000..db35384 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/BaseVo.java @@ -0,0 +1,5 @@ +package com.blockchain.server.eos.eos4j.api.vo; + +public class BaseVo { + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/Block.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/Block.java new file mode 100644 index 0000000..3381462 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/Block.java @@ -0,0 +1,145 @@ +package com.blockchain.server.eos.eos4j.api.vo; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Date; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Block { + + @JsonProperty("timestamp") + private Date timestamp; + + @JsonProperty("producer") + private String producer; + + @JsonProperty("confirmed") + private Long confirmed; + + @JsonProperty("previous") + private String previous; + + @JsonProperty("transaction_mroot") + private String transactionMroot; + + @JsonProperty("action_mroot") + private String actionMroot; + + @JsonProperty("schedule_version") + private String scheduleVersion; + + @JsonProperty("id") + private String id; + + @JsonProperty("block_num") + private Long blockNum; + + @JsonProperty("ref_block_prefix") + private Long refBlockPrefix; + + public Block() { + + } + + public Date getTimestamp() { + return timestamp; + } + + public void setTimestamp(Date timestamp) { + this.timestamp = timestamp; + } + + public String getProducer() { + return producer; + } + + public void setProducer(String producer) { + this.producer = producer; + } + + public Long getConfirmed() { + return confirmed; + } + + public void setConfirmed(Long confirmed) { + this.confirmed = confirmed; + } + + public String getPrevious() { + return previous; + } + + public void setPrevious(String previous) { + this.previous = previous; + } + + public String getTransactionMroot() { + return transactionMroot; + } + + public void setTransactionMroot(String transactionMroot) { + this.transactionMroot = transactionMroot; + } + + public String getActionMroot() { + return actionMroot; + } + + public void setActionMroot(String actionMroot) { + this.actionMroot = actionMroot; + } + + public String getScheduleVersion() { + return scheduleVersion; + } + + public void setScheduleVersion(String scheduleVersion) { + this.scheduleVersion = scheduleVersion; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Long getBlockNum() { + return blockNum; + } + + public void setBlockNum(Long blockNum) { + this.blockNum = blockNum; + } + + public Long getRefBlockPrefix() { + return refBlockPrefix; + } + + public void setRefBlockPrefix(Long refBlockPrefix) { + this.refBlockPrefix = refBlockPrefix; + } + + @Override + public String toString() { + return "Block{" + + "timestamp=" + timestamp + + ", producer='" + producer + '\'' + + ", confirmed=" + confirmed + + ", previous='" + previous + '\'' + + ", transactionMroot='" + transactionMroot + '\'' + + ", actionMroot='" + actionMroot + '\'' + + ", scheduleVersion='" + scheduleVersion + '\'' + + ", id='" + id + '\'' + + ", blockNum=" + blockNum + + ", refBlockPrefix=" + refBlockPrefix + + '}'; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/ChainInfo.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/ChainInfo.java new file mode 100644 index 0000000..b65518a --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/ChainInfo.java @@ -0,0 +1,170 @@ +package com.blockchain.server.eos.eos4j.api.vo; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Date; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class ChainInfo { + + public ChainInfo() { + + } + + @JsonProperty("server_version") + private String serverVersion; + + @JsonProperty("chain_id") + private String chainId; + + @JsonProperty("head_block_num") + private String headBlockNum; + + @JsonProperty("last_irreversible_block_num") + private Long lastIrreversibleBlockNum; + + @JsonProperty("last_irreversible_block_id") + private String lastIrreversibleBlockId; + + @JsonProperty("head_block_id") + private String headBlockId; + + @JsonProperty("head_block_time") + private Date headBlockTime; + + @JsonProperty("head_block_producer") + private String headBlockProducer; + + @JsonProperty("virtual_block_cpu_limit") + private String virtualBlockCpuLimit; + + @JsonProperty("virtual_block_net_limit") + private String virtualBlockNetLimit; + + @JsonProperty("block_cpu_limit") + private String blockCpuLimit; + + @JsonProperty("block_net_limit") + private String blockNetLimit; + + public String getServerVersion() { + return serverVersion; + } + + public void setServerVersion(String serverVersion) { + this.serverVersion = serverVersion; + } + + public String getChainId() { + return chainId; + } + + public void setChainId(String chainId) { + this.chainId = chainId; + } + + public String getHeadBlockNum() { + return headBlockNum; + } + + public void setHeadBlockNum(String headBlockNum) { + this.headBlockNum = headBlockNum; + } + + public Long getLastIrreversibleBlockNum() { + return lastIrreversibleBlockNum; + } + + public void setLastIrreversibleBlockNum(Long lastIrreversibleBlockNum) { + this.lastIrreversibleBlockNum = lastIrreversibleBlockNum; + } + + public String getLastIrreversibleBlockId() { + return lastIrreversibleBlockId; + } + + public void setLastIrreversibleBlockId(String lastIrreversibleBlockId) { + this.lastIrreversibleBlockId = lastIrreversibleBlockId; + } + + public Date getHeadBlockTime() { + return headBlockTime; + } + + public void setHeadBlockTime(Date headBlockTime) { + this.headBlockTime = headBlockTime; + } + + public String getHeadBlockProducer() { + return headBlockProducer; + } + + public void setHeadBlockProducer(String headBlockProducer) { + this.headBlockProducer = headBlockProducer; + } + + public String getVirtualBlockCpuLimit() { + return virtualBlockCpuLimit; + } + + public void setVirtualBlockCpuLimit(String virtualBlockCpuLimit) { + this.virtualBlockCpuLimit = virtualBlockCpuLimit; + } + + public String getVirtualBlockNetLimit() { + return virtualBlockNetLimit; + } + + public void setVirtualBlockNetLimit(String virtualBlockNetLimit) { + this.virtualBlockNetLimit = virtualBlockNetLimit; + } + + public String getBlockCpuLimit() { + return blockCpuLimit; + } + + public void setBlockCpuLimit(String blockCpuLimit) { + this.blockCpuLimit = blockCpuLimit; + } + + public String getBlockNetLimit() { + return blockNetLimit; + } + + public void setBlockNetLimit(String blockNetLimit) { + this.blockNetLimit = blockNetLimit; + } + + public String getHeadBlockId() { + return headBlockId; + } + + public void setHeadBlockId(String headBlockId) { + + this.headBlockId = headBlockId; + } + + @Override + public String toString() { + return "ChainInfo{" + + "serverVersion='" + serverVersion + '\'' + + ", chainId='" + chainId + '\'' + + ", headBlockNum='" + headBlockNum + '\'' + + ", lastIrreversibleBlockNum=" + lastIrreversibleBlockNum + + ", lastIrreversibleBlockId='" + lastIrreversibleBlockId + '\'' + + ", headBlockId='" + headBlockId + '\'' + + ", headBlockTime=" + headBlockTime + + ", headBlockProducer='" + headBlockProducer + '\'' + + ", virtualBlockCpuLimit='" + virtualBlockCpuLimit + '\'' + + ", virtualBlockNetLimit='" + virtualBlockNetLimit + '\'' + + ", blockCpuLimit='" + blockCpuLimit + '\'' + + ", blockNetLimit='" + blockNetLimit + '\'' + + '}'; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/SignParam.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/SignParam.java new file mode 100644 index 0000000..475839c --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/SignParam.java @@ -0,0 +1,72 @@ +package com.blockchain.server.eos.eos4j.api.vo; + +import java.util.Date; + +/** + * + * @author espritblock http://eblock.io + * + */ +public class SignParam { + /** + * 最新区块时间 + */ + private Date headBlockTime; + /** + * 链ID + */ + private String chainId; + /** + * 不可逆区块 + */ + private Long lastIrreversibleBlockNum; + /** + * 上一个区块hash前缀 + */ + private Long refBlockPrefix; + /** + * 过期时间 + */ + private Long exp; + + public Date getHeadBlockTime() { + return headBlockTime; + } + + public void setHeadBlockTime(Date headBlockTime) { + this.headBlockTime = headBlockTime; + } + + public String getChainId() { + return chainId; + } + + public void setChainId(String chainId) { + this.chainId = chainId; + } + + public Long getLastIrreversibleBlockNum() { + return lastIrreversibleBlockNum; + } + + public void setLastIrreversibleBlockNum(Long lastIrreversibleBlockNum) { + this.lastIrreversibleBlockNum = lastIrreversibleBlockNum; + } + + public Long getRefBlockPrefix() { + return refBlockPrefix; + } + + public void setRefBlockPrefix(Long refBlockPrefix) { + this.refBlockPrefix = refBlockPrefix; + } + + public Long getExp() { + return exp; + } + + public void setExp(Long exp) { + this.exp = exp; + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/TableRows.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/TableRows.java new file mode 100644 index 0000000..9a7ba26 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/TableRows.java @@ -0,0 +1,30 @@ +package com.blockchain.server.eos.eos4j.api.vo; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import java.util.List; +import java.util.Map; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class TableRows { + + private Boolean more; + + private List rows; + + public Boolean getMore() { + return more; + } + + public void setMore(Boolean more) { + this.more = more; + } + + public List getRows() { + return rows; + } + + public void setRows(List rows) { + this.rows = rows; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/TableRowsReq.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/TableRowsReq.java new file mode 100644 index 0000000..21bea43 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/TableRowsReq.java @@ -0,0 +1,58 @@ +package com.blockchain.server.eos.eos4j.api.vo; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class TableRowsReq { + + private String code = "eosio"; + + private String scope; + + private String table; + + private Boolean json=true; + + private int limit = 10; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + + public String getTable() { + return table; + } + + public void setTable(String table) { + this.table = table; + } + + public int getLimit() { + return limit; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public Boolean getJson() { + return json; + } + + public void setJson(Boolean json) { + this.json = json; + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/Account.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/Account.java new file mode 100644 index 0000000..d2ab8dc --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/Account.java @@ -0,0 +1,158 @@ +package com.blockchain.server.eos.eos4j.api.vo.account; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Date; +import java.util.List; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Account { + + @JsonProperty("account_name") + private String accountName; + + @JsonProperty("privileged") + private String privileged; + + @JsonProperty("last_code_update") + private Date lastCodeUpdate; + + @JsonProperty("created") + private Date created; + + @JsonProperty("ram_quota") + private Long ramQuota; + + @JsonProperty("net_weight") + private Long netWeight; + + @JsonProperty("cpu_weight") + private Long cpuWeight; + + @JsonProperty("net_limit") + private NetLimit netLimit; + + @JsonProperty("cpu_limit") + private CpuLimit cpuLimit; + + @JsonProperty("ram_usage") + private Long ramUsage; + + @JsonProperty("permissions") + private List permissions; + + public Account() { + + } + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public String getPrivileged() { + return privileged; + } + + public void setPrivileged(String privileged) { + this.privileged = privileged; + } + + public Date getLastCodeUpdate() { + return lastCodeUpdate; + } + + public void setLastCodeUpdate(Date lastCodeUpdate) { + this.lastCodeUpdate = lastCodeUpdate; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Long getRamQuota() { + return ramQuota; + } + + public void setRamQuota(Long ramQuota) { + this.ramQuota = ramQuota; + } + + public Long getNetWeight() { + return netWeight; + } + + public void setNetWeight(Long netWeight) { + this.netWeight = netWeight; + } + + public Long getCpuWeight() { + return cpuWeight; + } + + public void setCpuWeight(Long cpuWeight) { + this.cpuWeight = cpuWeight; + } + + public NetLimit getNetLimit() { + return netLimit; + } + + public void setNetLimit(NetLimit netLimit) { + this.netLimit = netLimit; + } + + public CpuLimit getCpuLimit() { + return cpuLimit; + } + + public void setCpuLimit(CpuLimit cpuLimit) { + this.cpuLimit = cpuLimit; + } + + public Long getRamUsage() { + return ramUsage; + } + + public void setRamUsage(Long ramUsage) { + this.ramUsage = ramUsage; + } + + public List getPermissions() { + return permissions; + } + + public void setPermissions(List permissions) { + this.permissions = permissions; + } + + @Override + public String toString() { + return "Account{" + + "accountName='" + accountName + '\'' + + ", privileged='" + privileged + '\'' + + ", lastCodeUpdate=" + lastCodeUpdate + + ", created=" + created + + ", ramQuota=" + ramQuota + + ", netWeight=" + netWeight + + ", cpuWeight=" + cpuWeight + + ", netLimit=" + netLimit + + ", cpuLimit=" + cpuLimit + + ", ramUsage=" + ramUsage + + ", permissions=" + permissions + + '}'; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/CpuLimit.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/CpuLimit.java new file mode 100644 index 0000000..2f6d4ca --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/CpuLimit.java @@ -0,0 +1,55 @@ +package com.blockchain.server.eos.eos4j.api.vo.account; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class CpuLimit { + + private Long used; + + private Long available; + + private Long max; + + public CpuLimit() { + + } + + public Long getUsed() { + return used; + } + + public void setUsed(Long used) { + this.used = used; + } + + public Long getAvailable() { + return available; + } + + public void setAvailable(Long available) { + this.available = available; + } + + public Long getMax() { + return max; + } + + public void setMax(Long max) { + this.max = max; + } + + @Override + public String toString() { + return "CpuLimit{" + + "used=" + used + + ", available=" + available + + ", max=" + max + + '}'; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/Key.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/Key.java new file mode 100644 index 0000000..274af2d --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/Key.java @@ -0,0 +1,44 @@ +package com.blockchain.server.eos.eos4j.api.vo.account; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Key { + + private String key; + + private Long weight; + + public Key() { + + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public Long getWeight() { + return weight; + } + + public void setWeight(Long weight) { + this.weight = weight; + } + + @Override + public String toString() { + return "Key{" + + "key='" + key + '\'' + + ", weight=" + weight + + '}'; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/NetLimit.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/NetLimit.java new file mode 100644 index 0000000..e285f84 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/NetLimit.java @@ -0,0 +1,55 @@ +package com.blockchain.server.eos.eos4j.api.vo.account; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class NetLimit { + + private Long used; + + private Long available; + + private Long max; + + public NetLimit() { + + } + + public Long getUsed() { + return used; + } + + public void setUsed(Long used) { + this.used = used; + } + + public Long getAvailable() { + return available; + } + + public void setAvailable(Long available) { + this.available = available; + } + + public Long getMax() { + return max; + } + + public void setMax(Long max) { + this.max = max; + } + + @Override + public String toString() { + return "NetLimit{" + + "used=" + used + + ", available=" + available + + ", max=" + max + + '}'; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/Permission.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/Permission.java new file mode 100644 index 0000000..598782b --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/Permission.java @@ -0,0 +1,59 @@ +package com.blockchain.server.eos.eos4j.api.vo.account; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Permission { + + public Permission() { + + } + + @JsonProperty("perm_name") + private String permName; + + @JsonProperty("parent") + private String parent; + + @JsonProperty("required_auth") + private RequiredAuth requiredAuth; + + public String getPermName() { + return permName; + } + + public void setPermName(String permName) { + this.permName = permName; + } + + public String getParent() { + return parent; + } + + public void setParent(String parent) { + this.parent = parent; + } + + public RequiredAuth getRequiredAuth() { + return requiredAuth; + } + + public void setRequiredAuth(RequiredAuth requiredAuth) { + this.requiredAuth = requiredAuth; + } + + @Override + public String toString() { + return "Permission{" + + "permName='" + permName + '\'' + + ", parent='" + parent + '\'' + + ", requiredAuth=" + requiredAuth + + '}'; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/RequiredAuth.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/RequiredAuth.java new file mode 100644 index 0000000..b948339 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/account/RequiredAuth.java @@ -0,0 +1,54 @@ +package com.blockchain.server.eos.eos4j.api.vo.account; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import java.util.List; +import java.util.Map; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class RequiredAuth { + + private List accounts; + + private List keys; + + private Long threshold; + + public List getAccounts() { + return accounts; + } + + public void setAccounts(List accounts) { + this.accounts = accounts; + } + + public List getKeys() { + return keys; + } + + public void setKeys(List keys) { + this.keys = keys; + } + + public Long getThreshold() { + return threshold; + } + + public void setThreshold(Long threshold) { + this.threshold = threshold; + } + + @Override + public String toString() { + return "RequiredAuth{" + + "accounts=" + accounts + + ", keys=" + keys + + ", threshold=" + threshold + + '}'; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Act.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Act.java new file mode 100644 index 0000000..f68332f --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Act.java @@ -0,0 +1,67 @@ +package com.blockchain.server.eos.eos4j.api.vo.transaction; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Act { + + @JsonProperty("account") + private String account; + + @JsonProperty("name") + private String name; + + @JsonProperty("data") + private Data data; + + @JsonProperty("hex_data") + private String hexData; + + @Override + public String toString() { + return "Actions{" + + "account='" + account + '\'' + + ", name='" + name + '\'' + + ", data=" + data + + ", hexData='" + hexData + '\'' + + '}'; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Data getData() { + return data; + } + + public void setData(Data data) { + this.data = data; + } + + public String getHexData() { + return hexData; + } + + public void setHexData(String hexData) { + this.hexData = hexData; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/ActionTraces.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/ActionTraces.java new file mode 100644 index 0000000..c273994 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/ActionTraces.java @@ -0,0 +1,31 @@ +package com.blockchain.server.eos.eos4j.api.vo.transaction; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class ActionTraces { + + @JsonProperty("act") + private Act act; + + public Act getAct() { + return act; + } + + public void setAct(Act act) { + this.act = act; + } + + @Override + public String toString() { + return "ActionTraces{" + + "act=" + act + + '}'; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Data.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Data.java new file mode 100644 index 0000000..4c8a273 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Data.java @@ -0,0 +1,67 @@ +package com.blockchain.server.eos.eos4j.api.vo.transaction; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Data { + + @JsonProperty("from") + private String from; + + @JsonProperty("to") + private String to; + + @JsonProperty("quantity") + private String quantity; + + @JsonProperty("memo") + private String memo; + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + public String getQuantity() { + return quantity; + } + + public void setQuantity(String quantity) { + this.quantity = quantity; + } + + public String getMemo() { + return memo; + } + + public void setMemo(String memo) { + this.memo = memo; + } + + @Override + public String toString() { + return "Data{" + + "from='" + from + '\'' + + ", to='" + to + '\'' + + ", quantity='" + quantity + '\'' + + ", memo='" + memo + '\'' + + '}'; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Processed.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Processed.java new file mode 100644 index 0000000..28aa71b --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Processed.java @@ -0,0 +1,118 @@ +package com.blockchain.server.eos.eos4j.api.vo.transaction; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Processed { + + @JsonProperty("id") + private String id; + + @JsonProperty("receipt") + private Receipt receipt; + + @JsonProperty("elapsed") + private Long elapsed; + + @JsonProperty("net_usage") + private Long netUsage; + + @JsonProperty("scheduled") + private Boolean scheduled; + + + @JsonProperty("block_time") + private String blockTime; + + @JsonProperty("block_num") + private String blockNum; + + @JsonProperty("action_traces") + private List actionTraces; + + public List getActionTraces() { + return actionTraces; + } + + public void setActionTraces(List actionTraces) { + this.actionTraces = actionTraces; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Receipt getReceipt() { + return receipt; + } + + public void setReceipt(Receipt receipt) { + this.receipt = receipt; + } + + public Long getElapsed() { + return elapsed; + } + + public void setElapsed(Long elapsed) { + this.elapsed = elapsed; + } + + public Long getNetUsage() { + return netUsage; + } + + public void setNetUsage(Long netUsage) { + this.netUsage = netUsage; + } + + public Boolean getScheduled() { + return scheduled; + } + + public void setScheduled(Boolean scheduled) { + this.scheduled = scheduled; + } + + public String getBlockTime() { + return blockTime; + } + + public void setBlockTime(String blockTime) { + this.blockTime = blockTime; + } + + public String getBlockNum() { + return blockNum; + } + + public void setBlockNum(String blockNum) { + this.blockNum = blockNum; + } + + @Override + public String toString() { + return "Processed{" + + "id='" + id + '\'' + + ", receipt=" + receipt + + ", elapsed=" + elapsed + + ", netUsage=" + netUsage + + ", scheduled=" + scheduled + + ", blockTime='" + blockTime + '\'' + + ", blockNum='" + blockNum + '\'' + + ", actionTraces=" + actionTraces + + '}'; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Receipt.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Receipt.java new file mode 100644 index 0000000..a66565f --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Receipt.java @@ -0,0 +1,55 @@ +package com.blockchain.server.eos.eos4j.api.vo.transaction; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Receipt { + + @JsonProperty("status") + private String status; + + @JsonProperty("cpu_usage_us") + private Long cpuUsageUs; + + @JsonProperty("net_usage_words") + private Long netUsageWords; + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Long getCpuUsageUs() { + return cpuUsageUs; + } + + public void setCpuUsageUs(Long cpuUsageUs) { + this.cpuUsageUs = cpuUsageUs; + } + + public Long getNetUsageWords() { + return netUsageWords; + } + + public void setNetUsageWords(Long netUsageWords) { + this.netUsageWords = netUsageWords; + } + + @Override + public String toString() { + return "Receipt{" + + "status='" + status + '\'' + + ", cpuUsageUs=" + cpuUsageUs + + ", netUsageWords=" + netUsageWords + + '}'; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Transaction.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Transaction.java new file mode 100644 index 0000000..8e246d5 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/Transaction.java @@ -0,0 +1,47 @@ +package com.blockchain.server.eos.eos4j.api.vo.transaction; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.deser.Deserializers.Base; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Transaction extends Base { + + @JsonProperty("transaction_id") + private String transactionId; + + @JsonProperty("processed") + private Processed processed; + + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public Processed getProcessed() { + return processed; + } + + public void setProcessed(Processed processed) { + this.processed = processed; + } + + + + @Override + public String toString() { + return "Transaction{" + + "transactionId='" + transactionId + '\'' + + ", processed=" + processed + + '}'; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/Tx.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/Tx.java new file mode 100644 index 0000000..290c3dc --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/Tx.java @@ -0,0 +1,106 @@ +package com.blockchain.server.eos.eos4j.api.vo.transaction.push; + +import com.blockchain.server.eos.eos4j.api.vo.BaseVo; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Tx extends BaseVo { + + private Object expiration; + + private Long ref_block_num; + + private Long ref_block_prefix; + + private Long net_usage_words; + + private Long max_cpu_usage_ms; + + private Long delay_sec; + + private List context_free_actions = new ArrayList<>(); + + private List actions; + + private List transaction_extensions = new ArrayList<>(); + + public Object getExpiration() { + return expiration; + } + + public void setExpiration(Object expiration) { + this.expiration = expiration; + } + + public Long getRef_block_num() { + return ref_block_num; + } + + public void setRef_block_num(Long ref_block_num) { + this.ref_block_num = ref_block_num; + } + + public Long getRef_block_prefix() { + return ref_block_prefix; + } + + public void setRef_block_prefix(Long ref_block_prefix) { + this.ref_block_prefix = ref_block_prefix; + } + + public Long getNet_usage_words() { + return net_usage_words; + } + + public void setNet_usage_words(Long net_usage_words) { + this.net_usage_words = net_usage_words; + } + + public Long getMax_cpu_usage_ms() { + return max_cpu_usage_ms; + } + + public void setMax_cpu_usage_ms(Long max_cpu_usage_ms) { + this.max_cpu_usage_ms = max_cpu_usage_ms; + } + + public Long getDelay_sec() { + return delay_sec; + } + + public void setDelay_sec(Long delay_sec) { + this.delay_sec = delay_sec; + } + + public List getContext_free_actions() { + return context_free_actions; + } + + public void setContext_free_actions(List context_free_actions) { + this.context_free_actions = context_free_actions; + } + + public List getActions() { + return actions; + } + + public void setActions(List actions) { + this.actions = actions; + } + + public List getTransaction_extensions() { + return transaction_extensions; + } + + public void setTransaction_extensions(List transaction_extensions) { + this.transaction_extensions = transaction_extensions; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxAction.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxAction.java new file mode 100644 index 0000000..b1b5b9a --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxAction.java @@ -0,0 +1,68 @@ +package com.blockchain.server.eos.eos4j.api.vo.transaction.push; + + +import com.blockchain.server.eos.eos4j.api.vo.BaseVo; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author espritblock http://eblock.io + * + */ +public class TxAction extends BaseVo { + + public TxAction() { + + } + + public TxAction(String actor, String account, String name, Object data) { + this.account = account; + this.name = name; + this.data = data; + this.authorization = new ArrayList<>(); + this.authorization.add(new TxActionAuth(actor, "active")); + } + + private String account; + + private String name; + + private List authorization; + + private Object data; + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getAuthorization() { + return authorization; + } + + public void setAuthorization(List authorization) { + this.authorization = authorization; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxActionAuth.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxActionAuth.java new file mode 100644 index 0000000..3c8bb62 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxActionAuth.java @@ -0,0 +1,42 @@ +package com.blockchain.server.eos.eos4j.api.vo.transaction.push; + + +import com.blockchain.server.eos.eos4j.api.vo.BaseVo; + +/** + * + * @author espritblock http://eblock.io + * + */ +public class TxActionAuth extends BaseVo { + + public TxActionAuth() { + + } + + public TxActionAuth(String actor, String permission) { + this.actor = actor; + this.permission = permission; + } + + private String actor; + + private String permission; + + public String getActor() { + return actor; + } + + public void setActor(String actor) { + this.actor = actor; + } + + public String getPermission() { + return permission; + } + + public void setPermission(String permission) { + this.permission = permission; + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxExtenstions.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxExtenstions.java new file mode 100644 index 0000000..532fc63 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxExtenstions.java @@ -0,0 +1,15 @@ +package com.blockchain.server.eos.eos4j.api.vo.transaction.push; + + +import com.blockchain.server.eos.eos4j.api.vo.BaseVo; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * + * @author espritblock http://eblock.io + * + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class TxExtenstions extends BaseVo { + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxRequest.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxRequest.java new file mode 100644 index 0000000..dc72204 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxRequest.java @@ -0,0 +1,53 @@ +package com.blockchain.server.eos.eos4j.api.vo.transaction.push; + + +import com.blockchain.server.eos.eos4j.api.vo.BaseVo; + +/** + * + * @author espritblock http://eblock.io + * + */ +public class TxRequest extends BaseVo { + + public TxRequest() { + + } + + public TxRequest(String compression, Tx transaction, String[] signatures) { + this.compression = compression; + this.transaction = transaction; + this.signatures = signatures; + } + + private String compression; + + private Tx transaction; + + private String[] signatures; + + public String getCompression() { + return compression; + } + + public void setCompression(String compression) { + this.compression = compression; + } + + public Tx getTransaction() { + return transaction; + } + + public void setTransaction(Tx transaction) { + this.transaction = transaction; + } + + public String[] getSignatures() { + return signatures; + } + + public void setSignatures(String[] signatures) { + this.signatures = signatures; + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxSign.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxSign.java new file mode 100644 index 0000000..de4973f --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/api/vo/transaction/push/TxSign.java @@ -0,0 +1,41 @@ +package com.blockchain.server.eos.eos4j.api.vo.transaction.push; + +import com.blockchain.server.eos.eos4j.api.vo.BaseVo; + +/** + * + * @author espritblock http://eblock.io + * + */ +public class TxSign extends BaseVo { + + public TxSign() { + + } + + public TxSign(String chain_id, Tx transaction) { + this.chain_id = chain_id; + this.transaction = transaction; + } + + private String chain_id; + + private Tx transaction; + + public String getChain_id() { + return chain_id; + } + + public void setChain_id(String chain_id) { + this.chain_id = chain_id; + } + + public Tx getTransaction() { + return transaction; + } + + public void setTransaction(Tx transaction) { + this.transaction = transaction; + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Curve.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Curve.java new file mode 100644 index 0000000..32ff578 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Curve.java @@ -0,0 +1,105 @@ +package com.blockchain.server.eos.eos4j.ecc; + +import java.math.BigInteger; + +/** + * Curve + * + * @author espritblock http://eblock.io + * + */ +public class Curve { + + private FieldElement a; + + private FieldElement b; + + private BigInteger q; + + private Point infinity; + + private BigInteger pOverFour; + + public Curve(BigInteger q, BigInteger a, BigInteger b) { + this.q = q; + this.a = fromBigInteger(a); + this.b = fromBigInteger(b); + this.infinity = new Point(this, null, null); + this.pOverFour = q.add(BigInteger.ONE).shiftRight(2); + } + + public FieldElement getA() { + return a; + } + + public FieldElement getB() { + return b; + } + + public BigInteger getQ() { + return q; + } + + public Point getInfinity() { + return infinity; + } + + public int getFieldSize() { + return q.bitLength(); + } + + public FieldElement fromBigInteger(BigInteger x) { + return new FieldElement(this.q, x); + } + + public Point pointFromX(int isOdd, BigInteger x) { + FieldElement f = this.a.multiply(fromBigInteger(x)); + BigInteger alpha = x.pow(3).add(f.toBigInteger()).add(this.b.toBigInteger()).mod(this.q); + BigInteger beta = alpha.modPow(this.pOverFour, this.q); + BigInteger y = beta; + if (beta.intValue() % 2 == 0 ^ isOdd == 0) { + y = this.q.subtract(y); + } + return new Point(this, new FieldElement(this.q, x), new FieldElement(this.q, y), false); + } + + public Point decodePoint(byte[] encodedPoint) { + Point p = null; + switch (encodedPoint[0]) { + case 0x00: + p = getInfinity(); + break; + case 0x02: + case 0x03: + int ytilde = encodedPoint[0] & 1; + byte[] i = new byte[encodedPoint.length - 1]; + System.arraycopy(encodedPoint, 1, i, 0, i.length); + FieldElement x = new FieldElement(this.q, new BigInteger(1, i)); + FieldElement alpha = x.multiply(x.square().add(a)).add(b); + FieldElement beta = alpha.sqrt(); + if (beta == null) { + throw new RuntimeException("Invalid compression"); + } + int bit0 = (beta.toBigInteger().testBit(0) ? 1 : 0); + if (bit0 == ytilde) { + p = new Point(this, x, beta, true); + } else { + p = new Point(this, x, new FieldElement(this.q, q.subtract(beta.toBigInteger())), true); + } + break; + case 0x04: + case 0x06: + case 0x07: + byte[] xEnc = new byte[(encodedPoint.length - 1) / 2]; + byte[] yEnc = new byte[(encodedPoint.length - 1) / 2]; + System.arraycopy(encodedPoint, 1, xEnc, 0, xEnc.length); + System.arraycopy(encodedPoint, xEnc.length + 1, yEnc, 0, yEnc.length); + p = new Point(this, new FieldElement(this.q, new BigInteger(1, xEnc)), + new FieldElement(this.q, new BigInteger(1, yEnc))); + break; + default: + throw new RuntimeException("Invalid encoding 0x" + Integer.toString(encodedPoint[0], 16)); + } + return p; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/EccTool.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/EccTool.java new file mode 100644 index 0000000..f82c322 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/EccTool.java @@ -0,0 +1,189 @@ +package com.blockchain.server.eos.eos4j.ecc; + + +import com.blockchain.server.eos.eos4j.api.vo.transaction.push.TxSign; +import com.blockchain.server.eos.eos4j.utils.*; + +import java.math.BigInteger; + +/** + * Ecc + * + * @author espritblock http://eblock.io + * + */ +public class EccTool { + + public static final String address_prefix = "EOS"; + + public static final Secp256k secp = new Secp256k(); + + /** + * seedPrivate + * + * @param seed + * @return + */ + public static String seedPrivate(String seed) { + if (seed == null || seed.length() == 0) { + throw new EException("args_empty", "args is empty"); + } + byte[] a = { (byte) 0x80 }; + byte[] b = new BigInteger(Sha.SHA256(seed)).toByteArray(); + byte[] private_key = ByteUtils.concat(a, b); + byte[] checksum = Sha.SHA256(private_key); + checksum = Sha.SHA256(checksum); + byte[] check = ByteUtils.copy(checksum, 0, 4); + byte[] pk = ByteUtils.concat(private_key, check); + return Base58.encode(pk); + } + + /** + * privateKey + * + * @param pk + * @return + */ + private static BigInteger privateKey(String pk) { + byte[] private_wif = Base58.decode(pk); + byte version = (byte) 0x80; + if (private_wif[0] != version) { + throw new EException("version_error", "Expected version " + 0x80 + ", instead got " + version); + } + byte[] private_key = ByteUtils.copy(private_wif, 0, private_wif.length - 4); + byte[] new_checksum = Sha.SHA256(private_key); + new_checksum = Sha.SHA256(new_checksum); + new_checksum = ByteUtils.copy(new_checksum, 0, 4); + byte[] last_private_key = ByteUtils.copy(private_key, 1, private_key.length - 1); + BigInteger d = new BigInteger(Hex.bytesToHexString(last_private_key), 16); + return d; + } + + /** + * privateToPublic + * + * @param pk + * @return + */ + public static String privateToPublic(String pk) { + if (pk == null || pk.length() == 0) { + throw new EException("args_empty", "args is empty"); + } + // private key + BigInteger d = privateKey(pk); + // publick key + Point ep = secp.G().multiply(d); + byte[] pub_buf = ep.getEncoded(); + byte[] csum = Ripemd160.from(pub_buf).bytes(); + csum = ByteUtils.copy(csum, 0, 4); + byte[] addy = ByteUtils.concat(pub_buf, csum); + StringBuffer bf = new StringBuffer(address_prefix); + bf.append(Base58.encode(addy)); + return bf.toString(); + } + + /** + * signHash + * @param pk + * @param b + * @return + */ + public static String signHash(String pk, byte[] b) { + String dataSha256 = Hex.bytesToHexString(Sha.SHA256(b)); + BigInteger e = new BigInteger(dataSha256, 16); + int nonce = 0; + int i = 0; + BigInteger d = privateKey(pk); + Point Q = secp.G().multiply(d); + nonce = 0; + Ecdsa ecd = new Ecdsa(secp); + Ecdsa.SignBigInt sign; + while (true) { + sign = ecd.sign(dataSha256, d, nonce++); + byte der[] = sign.getDer(); + byte lenR = der[3]; + byte lenS = der[5 + lenR]; + if (lenR == 32 && lenS == 32) { + i = ecd.calcPubKeyRecoveryParam(e, sign, Q); + i += 4; // compressed + i += 27; // compact // 24 or 27 :( forcing odd-y 2nd key candidate) + break; + } + } + byte[] pub_buf = new byte[65]; + pub_buf[0] = (byte) i; + ByteUtils.copy(sign.getR().toByteArray(), 0, pub_buf, 1, sign.getR().toByteArray().length); + ByteUtils.copy(sign.getS().toByteArray(), 0, pub_buf, sign.getR().toByteArray().length + 1, + sign.getS().toByteArray().length); + + byte[] checksum = Ripemd160.from(ByteUtils.concat(pub_buf, "K1".getBytes())).bytes(); + + byte[] signatureString = ByteUtils.concat(pub_buf, ByteUtils.copy(checksum, 0, 4)); + + return "SIG_K1_" + Base58.encode(signatureString); + } + + /** + * sign string + * @param pk + * @param data + * @return + */ + public static String sign(String pk, String data) { + String dataSha256 = Hex.bytesToHexString(Sha.SHA256(data)); + BigInteger e = new BigInteger(dataSha256, 16); + int nonce = 0; + int i = 0; + BigInteger d = privateKey(pk); + Point Q = secp.G().multiply(d); + nonce = 0; + Ecdsa ecd = new Ecdsa(secp); + Ecdsa.SignBigInt sign; + while (true) { + sign = ecd.sign(dataSha256, d, nonce++); + byte der[] = sign.getDer(); + byte lenR = der[3]; + byte lenS = der[5 + lenR]; + if (lenR == 32 && lenS == 32) { + i = ecd.calcPubKeyRecoveryParam(e, sign, Q); + i += 4; // compressed + i += 27; // compact // 24 or 27 :( forcing odd-y 2nd key candidate) + break; + } + } + byte[] pub_buf = new byte[65]; + pub_buf[0] = (byte) i; + ByteUtils.copy(sign.getR().toByteArray(), 0, pub_buf, 1, sign.getR().toByteArray().length); + ByteUtils.copy(sign.getS().toByteArray(), 0, pub_buf, sign.getR().toByteArray().length + 1, + sign.getS().toByteArray().length); + + byte[] checksum = Ripemd160.from(ByteUtils.concat(pub_buf, "K1".getBytes())).bytes(); + + byte[] signatureString = ByteUtils.concat(pub_buf, ByteUtils.copy(checksum, 0, 4)); + + return "SIG_K1_" + Base58.encode(signatureString); + } + + /** + * signTransaction + * @param privateKey + * @param push + * @return + */ + public static String signTransaction(String privateKey, TxSign push) { + // tx + ByteBuffer bf = new ByteBuffer(); + ObjectUtils.writeBytes(push, bf); + byte[] real = bf.getBuffer(); + // append + real = ByteUtils.concat(real, java.nio.ByteBuffer.allocate(33).array()); + + // final byte [] b = real.clone(); + // int[] a = IntStream.range(0, b.length).map(i -> b[i] & 0xff).toArray(); + // for(int i=1;i<=a.length;i++) { + // System.out.print(a[i-1]+","+((i%8==0)?"\n":"")); + // } + return signHash(privateKey, real); + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Ecdsa.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Ecdsa.java new file mode 100644 index 0000000..b6b05d8 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Ecdsa.java @@ -0,0 +1,270 @@ +package com.blockchain.server.eos.eos4j.ecc; + + +import com.blockchain.server.eos.eos4j.utils.ByteUtils; +import com.blockchain.server.eos.eos4j.utils.EException; +import com.blockchain.server.eos.eos4j.utils.Hex; +import com.blockchain.server.eos.eos4j.utils.Sha; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Ecdsa + * + * @author espritblock http://eblock.io + * + */ +public class Ecdsa { + + Secp256k curve = null; + + public Ecdsa(Secp256k curve) { + this.curve = curve; + } + + /** + * sign + * + * @param dataHash + * @param d + * @param nonce + * @return + */ + public SignBigInt sign(String dataHash, BigInteger d, int nonce) { + BigInteger n = curve.n(); + SignBigInt big = new SignBigInt(); + deterministicGenerateK(curve, dataHash, d, nonce, big); + BigInteger N_OVER_TWO = n.shiftRight(1); + if (big.getS().compareTo(N_OVER_TWO) > 0) { + big.setS(n.subtract(big.getS())); + } + big.setDer(toDER(big)); + return big; + } + + /** + * toDER + * + * @param big + * @return + */ + private byte[] toDER(SignBigInt big) { + byte[] rBa = big.getR().toByteArray(); + byte[] sBa = big.getS().toByteArray(); + ArrayList sequence = new ArrayList(); + sequence.add(new Byte(((byte) 0x02))); + sequence.add(new Byte(((byte) rBa.length))); + for (int i = 0; i < rBa.length; i++) { + sequence.add(rBa[i]); + } + sequence.add(new Byte(((byte) 0x02))); + sequence.add(new Byte(((byte) sBa.length))); + for (int i = 0; i < sBa.length; i++) { + sequence.add(sBa[i]); + } + int len = sequence.size(); + sequence.add(0, (byte) 0x30); + sequence.add(1, (byte) len); + byte[] bf = new byte[sequence.size()]; + for (int i = 0; i < bf.length; i++) { + bf[i] = sequence.get(i).byteValue(); + } + return bf; + } + + private BigInteger deterministicGenerateK(Secp256k curve, String dataHash, BigInteger d, int nonce, + SignBigInt big) { + byte[] hash = Hex.hexStringToBytes(dataHash); + if (nonce > 0) { + hash = Sha.SHA256(ByteUtils.concat(hash, new byte[nonce])); + } + byte[] x = null; + if (d.toByteArray()[0] == 0) { + x = ByteUtils.copy(d.toByteArray(), 1, d.toByteArray().length - 1); + } else { + x = d.toByteArray(); + } + // int padding = d.toByteArray().length-tmp.length; + // byte[] zeros = null; + // if(padding>0) { + // zeros = new byte[padding]; + // for(int i =0;i= 0 || !check) { + k = Sha.HmacSHA256(ByteUtils.concat(v, new byte[] { 0 }), k); + v = Sha.HmacSHA256(v, k); + v = Sha.HmacSHA256(v, k); + T = new BigInteger(v); + } + + big.setK(T); + return T; + } + + /** + * checkSig + * + * @param k + * @param d + * @param e + * @param big + * @return + */ + public Boolean checkSig(BigInteger k, BigInteger d, BigInteger e, SignBigInt big) { + Point Q = curve.G().multiply(k); + if (Q.isInfinity()) + return false; + BigInteger r = Q.getX().toBigInteger().mod(curve.n()); + big.setR(r); + if (r.signum() == 0) + return false; + BigInteger s = k.modInverse(curve.n()).multiply(e.add(d.multiply(r))).mod(curve.n()); + big.setS(s); + if (s.signum() == 0) + return false; + return true; + } + + /** + * + * @author espritblock http://eblock.io + * + */ + public static class SignBigInt { + private BigInteger k; + private BigInteger r; + private BigInteger s; + private byte[] der; + + public BigInteger getK() { + return k; + } + + public void setK(BigInteger k) { + this.k = k; + } + + public BigInteger getR() { + return r; + } + + public void setR(BigInteger r) { + this.r = r; + } + + public BigInteger getS() { + return s; + } + + public void setS(BigInteger s) { + this.s = s; + } + + public byte[] getDer() { + return der; + } + + public void setDer(byte[] der) { + this.der = der; + } + } + + public int calcPubKeyRecoveryParam(BigInteger e, SignBigInt sign, Point Q) { + for (int i = 0; i < 4; i++) { + Point Qprime = recoverPubKey(e, sign, i); + if (Qprime.equals(Q)) { + return i; + } + } + throw new EException("sign_error", "Unable to find valid recovery factor"); + } + + public Point recoverPubKey(BigInteger e, SignBigInt big, int i) { + + BigInteger n = curve.n(); + Point G = curve.G(); + + BigInteger r = big.getR(); + BigInteger s = big.getS(); + + if (!(r.signum() > 0 && r.compareTo(n) < 0)) { + throw new EException("recover_pubkey_error", "Invalid r value"); + } + if (!(s.signum() > 0 && s.compareTo(n) < 0)) { + throw new EException("recover_pubkey_error", "Invalid r value"); + } + + // A set LSB signifies that the y-coordinate is odd + int isYOdd = i & 1; + + // The more significant bit specifies whether we should use the + // first or second candidate key. + int isSecondKey = i >> 1; + + // 1.1 Let x = r + jn + BigInteger x = isSecondKey == 1 ? r.add(n) : r; + + Point R = curve.getCurve().pointFromX(isYOdd, x); + + // // 1.4 Check that nR is at infinity + Point nR = R.multiply(n); + + if (!nR.isInfinity()) { + throw new EException("sign_error", "nR is not a valid curve point"); + } + + BigInteger eNeg = e.negate().mod(n); + + BigInteger rInv = r.modInverse(n); + + Point Q = R.multiplyTwo(s, G, eNeg).multiply(rInv); + + if (Q.isInfinity()) { + throw new EException("sign_error", "Point is at infinity"); + } + + return Q; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/FieldElement.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/FieldElement.java new file mode 100644 index 0000000..89a0da6 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/FieldElement.java @@ -0,0 +1,166 @@ +package com.blockchain.server.eos.eos4j.ecc; + + +import com.blockchain.server.eos.eos4j.utils.EException; + +import java.math.BigInteger; +import java.util.Random; + +/** + * FieldElement + * + * @author espritblock http://eblock.io + * + */ +public class FieldElement { + + private BigInteger q; + + private BigInteger x; + + private static final BigInteger TWO = BigInteger.valueOf(2); + + public FieldElement(BigInteger q, BigInteger x) { + this.x = x; + if (x.compareTo(q) >= 0) { + throw new EException("error", "x value too large in field element"); + } + this.q = q; + } + + public FieldElement add(FieldElement b) { + return new FieldElement(q, x.add(b.toBigInteger()).mod(q)); + } + + public FieldElement subtract(FieldElement b) { + return new FieldElement(q, x.subtract(b.toBigInteger()).mod(q)); + } + + public FieldElement multiply(FieldElement b) { + return new FieldElement(q, x.multiply(b.toBigInteger()).mod(q)); + } + + public FieldElement divide(FieldElement b) { + return new FieldElement(q, x.multiply(b.toBigInteger().modInverse(q)).mod(q)); + } + + public FieldElement negate() { + return new FieldElement(q, x.negate().mod(q)); + } + + public FieldElement square() { + return new FieldElement(q, x.multiply(x).mod(q)); + } + + public FieldElement invert() { + return new FieldElement(q, x.modInverse(q)); + } + + @Override + public String toString() { + return this.toBigInteger().toString(16); + } + + public FieldElement sqrt() { + if (!q.testBit(0)) { + throw new RuntimeException("not done yet"); + } + + if (q.testBit(1)) { + FieldElement z = new FieldElement(q, x.modPow(q.shiftRight(2).add(BigInteger.ONE), q)); + return z.square().equals(this) ? z : null; + } + BigInteger qMinusOne = q.subtract(BigInteger.ONE); + BigInteger legendreExponent = qMinusOne.shiftRight(1); + if (!(x.modPow(legendreExponent, q).equals(BigInteger.ONE))) { + return null; + } + BigInteger u = qMinusOne.shiftRight(2); + BigInteger k = u.shiftLeft(1).add(BigInteger.ONE); + BigInteger Q = this.x; + BigInteger fourQ = Q.shiftLeft(2).mod(q); + BigInteger U, V; + Random rand = new Random(); + do { + BigInteger P; + do { + P = new BigInteger(q.bitLength(), rand); + } while (P.compareTo(q) >= 0 + || !(P.multiply(P).subtract(fourQ).modPow(legendreExponent, q).equals(qMinusOne))); + BigInteger[] result = lucasSequence(q, P, Q, k); + U = result[0]; + V = result[1]; + if (V.multiply(V).mod(q).equals(fourQ)) { + if (V.testBit(0)) { + V = V.add(q); + } + V = V.shiftRight(1); + return new FieldElement(q, V); + } + } while (U.equals(BigInteger.ONE) || U.equals(qMinusOne)); + return null; + } + + private static BigInteger[] lucasSequence(BigInteger p, BigInteger P, BigInteger Q, BigInteger k) { + int n = k.bitLength(); + int s = k.getLowestSetBit(); + BigInteger Uh = BigInteger.ONE; + BigInteger Vl = TWO; + BigInteger Vh = P; + BigInteger Ql = BigInteger.ONE; + BigInteger Qh = BigInteger.ONE; + for (int j = n - 1; j >= s + 1; --j) { + Ql = Ql.multiply(Qh).mod(p); + if (k.testBit(j)) { + Qh = Ql.multiply(Q).mod(p); + Uh = Uh.multiply(Vh).mod(p); + Vl = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p); + Vh = Vh.multiply(Vh).subtract(Qh.shiftLeft(1)).mod(p); + } else { + Qh = Ql; + Uh = Uh.multiply(Vl).subtract(Ql).mod(p); + Vh = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p); + Vl = Vl.multiply(Vl).subtract(Ql.shiftLeft(1)).mod(p); + } + } + Ql = Ql.multiply(Qh).mod(p); + Qh = Ql.multiply(Q).mod(p); + Uh = Uh.multiply(Vl).subtract(Ql).mod(p); + Vl = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p); + Ql = Ql.multiply(Qh).mod(p); + for (int j = 1; j <= s; ++j) { + Uh = Uh.multiply(Vl).mod(p); + Vl = Vl.multiply(Vl).subtract(Ql.shiftLeft(1)).mod(p); + Ql = Ql.multiply(Ql).mod(p); + } + return new BigInteger[] { Uh, Vl }; + } + + public BigInteger toBigInteger() { + return x; + } + + public int getFieldSize() { + return q.bitLength(); + } + + public BigInteger getQ() { + return q; + } + + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof FieldElement)) { + return false; + } + FieldElement o = (FieldElement) other; + return q.equals(o.q) && x.equals(o.x); + } + + public int hashCode() { + return q.hashCode() ^ x.hashCode(); + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Point.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Point.java new file mode 100644 index 0000000..2d9cc83 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Point.java @@ -0,0 +1,214 @@ +package com.blockchain.server.eos.eos4j.ecc; + +import java.math.BigInteger; + +/** + * Point + * + * @author espritblock http://eblock.io + * + */ +public class Point { + + private Curve curve; + + private FieldElement x; + + private FieldElement y; + + private boolean compressed; + + public Point(Curve curve, FieldElement x, FieldElement y, boolean compressed) { + this.curve = curve; + this.x = x; + this.y = y; + this.compressed = compressed; + } + + public Point(Curve curve, FieldElement x, FieldElement y) { + this(curve, x, y, true); + } + + public Curve getCurve() { + return curve; + } + + public FieldElement getX() { + return x; + } + + public FieldElement getY() { + return y; + } + + public boolean isInfinity() { + return x == null && y == null; + } + + public boolean isCompressed() { + return compressed; + } + + public byte[] getEncoded() { + + if (this.isInfinity()) { + return new byte[1]; + } + + int length = getByteLength(x.getFieldSize()); + + if (compressed) { + byte PC; + if (this.getY().toBigInteger().testBit(0)) { + PC = 0x03; + } else { + PC = 0x02; + } + byte[] X = integerToBytes(this.getX().toBigInteger(), length); + byte[] PO = new byte[X.length + 1]; + PO[0] = PC; + System.arraycopy(X, 0, PO, 1, X.length); + return PO; + } else { + byte[] X = integerToBytes(this.getX().toBigInteger(), length); + byte[] Y = integerToBytes(this.getY().toBigInteger(), length); + byte[] PO = new byte[X.length + Y.length + 1]; + PO[0] = 0x04; + System.arraycopy(X, 0, PO, 1, X.length); + System.arraycopy(Y, 0, PO, X.length + 1, Y.length); + return PO; + } + } + + public Point add(Point b) { + if (this.isInfinity()) { + return b; + } + if (b.isInfinity()) { + return this; + } + if (this.x.equals(b.x)) { + if (this.y.equals(b.y)) { + return this.twice(); + } + return this.curve.getInfinity(); + } + FieldElement gamma = b.y.subtract(this.y).divide(b.x.subtract(this.x)); + FieldElement x3 = gamma.square().subtract(this.x).subtract(b.x); + FieldElement y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); + return new Point(curve, x3, y3); + } + + public Point twice() { + if (this.isInfinity()) { + return this; + } + if (this.y.toBigInteger().signum() == 0) { + return this.curve.getInfinity(); + } + FieldElement TWO = this.curve.fromBigInteger(BigInteger.valueOf(2)); + FieldElement THREE = this.curve.fromBigInteger(BigInteger.valueOf(3)); + FieldElement gamma = this.x.square().multiply(THREE).add(curve.getA()).divide(y.multiply(TWO)); + FieldElement x3 = gamma.square().subtract(this.x.multiply(TWO)); + FieldElement y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); + return new Point(curve, x3, y3, this.compressed); + } + + public Point subtract(Point b) { + if (b.isInfinity()) { + return this; + } + return add(b.negate()); + } + + public Point negate() { + return new Point(curve, this.x, this.y.negate(), this.compressed); + } + + public Point multiply(BigInteger k) { + BigInteger e = k; + BigInteger h = e.multiply(BigInteger.valueOf(3)); + Point neg = this.negate(); + Point R = this; + for (int i = h.bitLength() - 2; i > 0; --i) { + R = R.twice(); + boolean hBit = h.testBit(i); + boolean eBit = e.testBit(i); + if (hBit != eBit) { + R = R.add(hBit ? this : neg); + } + } + return R; + } + + public Point multiplyTwo(BigInteger j, Point x, BigInteger k) { + int i = Math.max(j.bitLength(), k.bitLength()) - 1; + Point R = this.curve.getInfinity(); + Point both = this.add(x); + while (i >= 0) { + Boolean jBit = j.testBit(i); + Boolean kBit = k.testBit(i); + + R = R.twice(); + + if (jBit) { + if (kBit) { + R = R.add(both); + } else { + R = R.add(this); + } + } else if (kBit) { + R = R.add(x); + } + --i; + } + return R; + } + + public static int getByteLength(int fieldSize) { + return (fieldSize + 7) / 8; + } + + public static byte[] integerToBytes(BigInteger s, int length) { + byte[] bytes = s.toByteArray(); + if (length < bytes.length) { + byte[] tmp = new byte[length]; + System.arraycopy(bytes, bytes.length - tmp.length, tmp, 0, tmp.length); + return tmp; + } else if (length > bytes.length) { + byte[] tmp = new byte[length]; + System.arraycopy(bytes, 0, tmp, tmp.length - bytes.length, bytes.length); + return tmp; + } + return bytes; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Point)) { + return false; + } + + Point o = (Point) other; + + if (this.isInfinity()) { + return o.isInfinity(); + } + + return x.equals(o.x) && y.equals(o.y); + } + + @Override + public int hashCode() { + if (this.isInfinity()) { + return 0; + } + + return x.hashCode() ^ y.hashCode(); + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Secp256k.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Secp256k.java new file mode 100644 index 0000000..e95cf38 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ecc/Secp256k.java @@ -0,0 +1,53 @@ +package com.blockchain.server.eos.eos4j.ecc; + + +import com.blockchain.server.eos.eos4j.utils.Hex; + +import java.math.BigInteger; + +/** + * Curve + * + * @author espritblock http://eblock.io + * + */ +public class Secp256k { + + public static final String P = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"; + public static final String A = "0"; + public static final String B = "7"; + public static final String N = "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"; + public static final String GX = "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"; + public static final String GY = "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"; + + private final Curve curve; + + private final Point G; + + private final BigInteger n; + + private final BigInteger HALF_CURVE_ORDER; + + public Secp256k() { + n = new BigInteger(N, 16); + HALF_CURVE_ORDER = n.shiftRight(1); + curve = new Curve(new BigInteger(P, 16), new BigInteger(A, 16), new BigInteger(B, 16)); + G = curve.decodePoint(Hex.toBytes("04" + GX + GY)); + } + + public Point G() { + return this.G; + } + + public BigInteger n() { + return this.n; + } + + public BigInteger halfCurveOrder() { + return HALF_CURVE_ORDER; + } + + public Curve getCurve() { + return curve; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/Action.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/Action.java new file mode 100644 index 0000000..6cb6f87 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/Action.java @@ -0,0 +1,27 @@ +package com.blockchain.server.eos.eos4j.ese; + +/** + * Action + * + * @author espritblock http://eblock.io + * + */ +public enum Action { + + transfer("${precision},${quantity}@eosio.token"), account("account"), ram("ram"), delegate("${precision},${quantity}@eosio.token"), voteproducer("voteproducer"), + close("${precision},${quantity}@eosio.token"); + + private String code; + + private Action(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/DataParam.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/DataParam.java new file mode 100644 index 0000000..ab0abd9 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/DataParam.java @@ -0,0 +1,74 @@ +package com.blockchain.server.eos.eos4j.ese; + + +import com.blockchain.server.eos.eos4j.utils.ByteUtils; +import com.blockchain.server.eos.eos4j.utils.EException; + +/** + * DataParam + * + * @author espritblock http://eblock.io + * + */ +public class DataParam { + + public DataParam(String value, DataType type, Action action) { + this.value = value; + this.type = type; + if (type == DataType.asset || type == DataType.symbol ) { + if (action == action.transfer || action == action.delegate || action == action.close) { + String vs[] = value.split(" "); + if (vs.length < 2) {throw new EException("error", "quantity error");} + String ammount = vs[0]; + String ams [] = ammount.split("[.]"); + int precision = 0; + if(ams.length>1) {precision = ams[1].length();} + this.value = vs[0] + " " + action.getCode().replace("${precision}",String.valueOf(precision)).replace("${quantity}", vs[1]); + }else { + this.value = value; + } + } + } + + private String value; + + private DataType type; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public DataType getType() { + return type; + } + + public void setType(DataType type) { + this.type = type; + } + + public byte[] seria() { + if (this.type == DataType.name) { + return ByteUtils.writeName(this.value); + } else if (this.type == DataType.asset) { + return ByteUtils.writerAsset(this.value); + }else if (this.type == DataType.symbol) { + return ByteUtils.writerSymbol(this.value); + } else if (this.type == DataType.unit32) { + return ByteUtils.writerUnit32(this.value); + } else if (this.type == DataType.unit16) { + return ByteUtils.writerUnit16(this.value); + } else if (this.type == DataType.key) { + return ByteUtils.writerKey(this.value); + } else if (this.type == DataType.varint32) { + return ByteUtils.writerVarint32(this.value); + } else if (this.type == DataType.unit64) { + return ByteUtils.writeUint64(this.value); + } else { + return ByteUtils.writerString(this.value); + } + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/DataType.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/DataType.java new file mode 100644 index 0000000..9c50fea --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/DataType.java @@ -0,0 +1,27 @@ +package com.blockchain.server.eos.eos4j.ese; + +/** + * DataType + * + * @author espritblock http://eblock.io + * + */ +public enum DataType { + + name("name"), asset("asset"), string("string"), key("key"), unit16("unit16"), unit32("unit32"), varint32( + "varint32"),unit64("unit64"),symbol("symbol"); + + private DataType(String code) { + this.code = code; + } + + private String code; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/Ese.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/Ese.java new file mode 100644 index 0000000..ca3eb8f --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/ese/Ese.java @@ -0,0 +1,156 @@ +package com.blockchain.server.eos.eos4j.ese; + + +import com.blockchain.server.eos.eos4j.utils.ByteUtils; +import com.blockchain.server.eos.eos4j.utils.Hex; + +import java.util.ArrayList; +import java.util.List; + +/** + * Ese + * + * @author espritblock http://eblock.io + * + */ +public class Ese { + + /** + * parseTransferData + * + * @param datas + * @return + */ + public static String parseTransferData(String from, String to, String quantity, String memo) { + DataParam[] datas = new DataParam[] { new DataParam(from, DataType.name, Action.transfer), + new DataParam(to, DataType.name, Action.transfer), + new DataParam(quantity, DataType.asset, Action.transfer), + new DataParam(memo, DataType.string, Action.transfer), }; + byte[] allbyte = new byte[] {}; + for (DataParam value : datas) { + allbyte = ByteUtils.concat(allbyte, value.seria()); + } + // final byte [] b = allbyte.clone(); + // int[] a = IntStream.range(0, b.length).map(i -> b[i] & 0xff).toArray(); + // for(int i=1;i<=a.length;i++) { + // System.out.print(a[i-1]+","+((i%8==0)?"\n":"")); + // } + return Hex.bytesToHexString(allbyte); + } + + + /** + * parseTransferData + * + * @param datas + * @return + */ + public static String parseVoteProducerData(String voter, String proxy, List producers) { + List datas = new ArrayList(); + datas.add(new DataParam(voter, DataType.name, Action.voteproducer)); + datas.add(new DataParam(proxy, DataType.name, Action.voteproducer)); + datas.add(new DataParam(String.valueOf(producers.size()), DataType.varint32, Action.voteproducer)); + for(String producer:producers) { + datas.add(new DataParam(producer, DataType.name, Action.voteproducer)); + } + byte[] allbyte = new byte[] {}; + for (DataParam value : datas) { + allbyte = ByteUtils.concat(allbyte, value.seria()); + } +// final byte [] b = allbyte.clone(); +// int[] a = IntStream.range(0, b.length).map(i -> b[i] & 0xff).toArray(); +// for(int i=1;i<=a.length;i++) { +// System.out.print(a[i-1]+","+((i%8==0)?"\n":"")); +// } + return Hex.bytesToHexString(allbyte); + } + + + + /** + * parseTransferData + * + * @param datas + * @return + */ + public static String parseAccountData(String creator, String name, String onwer, String active) { + + DataParam[] datas = new DataParam[] { + // creator + new DataParam(creator, DataType.name, Action.account), + // name + new DataParam(name, DataType.name, Action.account), + // owner + new DataParam(onwer, DataType.key, Action.account), + // active + new DataParam(active, DataType.key, Action.account), + + }; + byte[] allbyte = new byte[] {}; + for (DataParam value : datas) { + allbyte = ByteUtils.concat(allbyte, value.seria()); + } + return Hex.bytesToHexString(allbyte); + } + + /** + * parseBuyRamData + * + * @param datas + * @return + */ + public static String parseDelegateData(String from, String receiver, String stakeNetQuantity, + String stakeCpuQuantity, int transfer) { + + DataParam[] datas = new DataParam[] { new DataParam(from, DataType.name, Action.delegate), + new DataParam(receiver, DataType.name, Action.delegate), + new DataParam(stakeNetQuantity, DataType.asset, Action.delegate), + new DataParam(stakeCpuQuantity, DataType.asset, Action.delegate), + new DataParam(String.valueOf(transfer), DataType.varint32, Action.delegate) + + }; + byte[] allbyte = new byte[] {}; + for (DataParam value : datas) { + allbyte = ByteUtils.concat(allbyte, value.seria()); + } + return Hex.bytesToHexString(allbyte); + } + + /** + * parseTransferData + * + * @param datas + * @return + */ + public static String parseBuyRamData(String payer, String receiver, Long bytes) { + + DataParam[] datas = new DataParam[] { new DataParam(payer, DataType.name, Action.ram), + new DataParam(receiver, DataType.name, Action.ram), + new DataParam(String.valueOf(bytes), DataType.unit32, Action.ram) + + }; + byte[] allbyte = new byte[] {}; + for (DataParam value : datas) { + allbyte = ByteUtils.concat(allbyte, value.seria()); + } + return Hex.bytesToHexString(allbyte); + } + + /** + * parseCloseData + * + * @param datas + * @return + */ + public static String parseCloseData(String owner, String symbol) { + DataParam[] datas = new DataParam[] { + new DataParam(owner, DataType.name, Action.close), + new DataParam(symbol, DataType.symbol, Action.close) + }; + byte[] allbyte = new byte[] {}; + for (DataParam value : datas) { + allbyte = ByteUtils.concat(allbyte, value.seria()); + } + return Hex.bytesToHexString(allbyte); + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Base58.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Base58.java new file mode 100644 index 0000000..b53ca9d --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Base58.java @@ -0,0 +1,163 @@ +package com.blockchain.server.eos.eos4j.utils; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; + +public class Base58 { + + public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); + + private static final int[] INDEXES = new int[128]; + + static { + for (int i = 0; i < INDEXES.length; i++) { + INDEXES[i] = -1; + } + for (int i = 0; i < ALPHABET.length; i++) { + INDEXES[ALPHABET[i]] = i; + } + } + + /** + * encode + * + * @param input + * @return + */ + public static String encode(byte[] input) { + if (input.length == 0) { + return ""; + } + input = copyOfRange(input, 0, input.length); + int zeroCount = 0; + while (zeroCount < input.length && input[zeroCount] == 0) { + ++zeroCount; + } + byte[] temp = new byte[input.length * 2]; + int j = temp.length; + int startAt = zeroCount; + while (startAt < input.length) { + byte mod = divmod58(input, startAt); + if (input[startAt] == 0) { + ++startAt; + } + temp[--j] = (byte) ALPHABET[mod]; + } + while (j < temp.length && temp[j] == ALPHABET[0]) { + ++j; + } + while (--zeroCount >= 0) { + temp[--j] = (byte) ALPHABET[0]; + } + byte[] output = copyOfRange(temp, j, temp.length); + try { + return new String(output, "US-ASCII"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); // Cannot happen. + } + } + + /** + * decode + * + * @param input + * @return + * @throws IllegalArgumentException + */ + public static byte[] decode(String input) throws IllegalArgumentException { + if (input.length() == 0) { + return new byte[0]; + } + byte[] input58 = new byte[input.length()]; + for (int i = 0; i < input.length(); ++i) { + char c = input.charAt(i); + int digit58 = -1; + if (c >= 0 && c < 128) { + digit58 = INDEXES[c]; + } + if (digit58 < 0) { + throw new IllegalArgumentException("Illegal character " + c + " at " + i); + } + input58[i] = (byte) digit58; + } + int zeroCount = 0; + while (zeroCount < input58.length && input58[zeroCount] == 0) { + ++zeroCount; + } + byte[] temp = new byte[input.length()]; + int j = temp.length; + int startAt = zeroCount; + while (startAt < input58.length) { + byte mod = divmod256(input58, startAt); + if (input58[startAt] == 0) { + ++startAt; + } + temp[--j] = mod; + } + while (j < temp.length && temp[j] == 0) { + ++j; + } + return copyOfRange(temp, j - zeroCount, temp.length); + } + + /** + * decodeToBigInteger + * + * @param input + * @return + * @throws IllegalArgumentException + */ + public static BigInteger decodeToBigInteger(String input) throws IllegalArgumentException { + return new BigInteger(1, decode(input)); + } + + /** + * divmod58 + * + * @param number + * @param startAt + * @return + */ + private static byte divmod58(byte[] number, int startAt) { + int remainder = 0; + for (int i = startAt; i < number.length; i++) { + int digit256 = (int) number[i] & 0xFF; + int temp = remainder * 256 + digit256; + number[i] = (byte) (temp / 58); + remainder = temp % 58; + } + return (byte) remainder; + } + + /** + * divmod256 + * + * @param number58 + * @param startAt + * @return + */ + private static byte divmod256(byte[] number58, int startAt) { + int remainder = 0; + for (int i = startAt; i < number58.length; i++) { + int digit58 = (int) number58[i] & 0xFF; + int temp = remainder * 58 + digit58; + number58[i] = (byte) (temp / 256); + remainder = temp % 256; + } + return (byte) remainder; + } + + /** + * copyOfRange + * + * @param source + * @param from + * @param to + * @return + */ + private static byte[] copyOfRange(byte[] source, int from, int to) { + byte[] range = new byte[to - from]; + System.arraycopy(source, from, range, 0, range.length); + return range; + } +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/ByteBuffer.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/ByteBuffer.java new file mode 100644 index 0000000..11aee1f --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/ByteBuffer.java @@ -0,0 +1,28 @@ +package com.blockchain.server.eos.eos4j.utils; + +/** + * + * @author espritblock http://eblock.io + * + */ +public class ByteBuffer { + + private byte[] buffer = new byte[] {}; + + public void concat(byte[] b) { + + // int[] a = IntStream.range(0, b.length).map(i -> b[i] & 0xff).toArray(); + // for(int i=1;i<=a.length;i++) { + // System.out.print(a[i-1]+","+((i%8==0)?"\n":"")); + // } + buffer = ByteUtils.concat(buffer, b); + } + + public byte[] getBuffer() { + return buffer; + } + + public void setBuffer(byte[] buffer) { + this.buffer = buffer; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/ByteUtils.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/ByteUtils.java new file mode 100644 index 0000000..323dff9 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/ByteUtils.java @@ -0,0 +1,399 @@ +package com.blockchain.server.eos.eos4j.utils; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.stream.IntStream; + +/** + * + * @author espritblock http://eblock.io + * + */ +public class ByteUtils { + + static String charmap = ".12345abcdefghijklmnopqrstuvwxyz"; + + /** + * charidx + * + * @param c + * @return + */ + public static int charidx(char c) { + return charmap.indexOf(c); + } + + /** + * concat + * + * @param a + * @param b + * @return + */ + public static byte[] concat(byte[] a, byte[] b) { + byte[] c = new byte[a.length + b.length]; + System.arraycopy(a, 0, c, 0, a.length); + System.arraycopy(b, 0, c, a.length, b.length); + return c; + } + + /** + * copy + * + * @param a + * @param b + * @return + */ + public static byte[] copy(byte[] src, int start, int length) { + byte[] c = new byte[length]; + System.arraycopy(src, start, c, 0, length); + return c; + } + + /** + * copy + * + * @param a + * @param b + * @return + */ + public static byte[] copy(byte[] src, int start, byte[] dest, int dstart, int length) { + System.arraycopy(src, start, dest, dstart, length); + return dest; + } + + /** + * LongToBytes + * + * @param values + * @return + */ + public static int[] LongToBytes(Long n) { + ByteBuffer hi = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.BIG_ENDIAN).putLong(n); + byte[] buf = hi.array(); + int[] a = IntStream.range(0, buf.length).map(i -> buf[i] & 0xff).toArray(); + return a; + } + + /** + * + * @param value + * @return + */ + public static String stringToAscii(String value) { + StringBuffer sbu = new StringBuffer(); + char[] chars = value.toCharArray(); + for (int i = 0; i < chars.length; i++) { + if (i != chars.length - 1) { + sbu.append((int) chars[i]); + } else { + sbu.append((int) chars[i]); + } + } + return sbu.toString(); + } + + /** + * writerUnit32 + * + * @param value + * @return + */ + public static byte[] writerUnit32(String value) { + + Long l = Long.parseLong(value); + if (l > Integer.MAX_VALUE) { + byte[] b = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.LITTLE_ENDIAN).putLong(l).array(); + int j = 0; + for (int i = b.length - 1; i >= 0; i--) { + if (b[i] == (byte) 0) { + j++; + } else { + break; + } + } + return ByteUtils.copy(b, 0, b.length - j); + } else { + return ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN).putInt(Integer.parseInt(value)) + .array(); + } + } + + /** + * writerUnit16 + * + * @param value + * @return + */ + public static byte[] writerUnit16(String value) { + long vl = Long.parseLong(value); + return new byte[] { (byte) (vl & 0x00FF), (byte) ((vl & 0xFF00) >>> 8) }; + } + + /** + * writerUnit8 + * + * @param value + * @return + */ + public static byte[] writerUnit8(String value) { + long vl = Long.parseLong(value); + return new byte[] { (byte) (vl & 0x00FF) }; + } + + /** + * writerVarint32 + * + * @param v + * @return + */ + public static byte[] writerVarint32(String v) { + long value = Long.parseLong(v); + byte[] a = new byte[] {}; + value >>>= 0; + while (value >= 0x80) { + long b = (value & 0x7f) | 0x80; + a = ByteUtils.concat(a, new byte[] { (byte) b }); + value >>>= 7; + } + a = ByteUtils.concat(a, new byte[] { (byte) value }); + return a; + } + + /** + * writerAsset + * + * @param v + * @return + */ + public static byte[] writerAsset(String v) { + String _value[] = v.split(" "); + String amount = _value[0]; + if(amount==null || !amount.matches("^[0-9]+(.[0-9]+)?$")){ + throw new EException("amount_error", "amount error"); + } + String sym = _value[1]; + String precision = sym.split(",")[0]; + String symbol = sym.split(",")[1].split("@")[0]; + String[] part = amount.split("[.]"); + + int pad = Integer.parseInt(precision); + StringBuffer bf = new StringBuffer(part[0] + "."); + if (part.length > 1) { + if(part[1].length()>pad) { + throw new EException("precision_error", "precision max "+pad); + } + pad = Integer.parseInt(precision) - part[1].length(); + bf.append(part[1]); + } + // ���Ȳ�0 + for (int i = 0; i < pad; i++) { + bf.append("0"); + } + String asset = precision + "," + symbol; + // amount + amount = bf.toString().replace(".", ""); + ByteBuffer ammount = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.LITTLE_ENDIAN) + .putLong(Long.parseLong(amount)); + + // asset + StringBuffer padStr = new StringBuffer(); + for (int i = 0; i < (7 - symbol.length()); i++) { + padStr.append("\0"); + } + char c = (char) Integer.parseInt(precision); + asset = c + symbol + padStr; + ByteBuffer ba = ByteBuffer.wrap(asset.getBytes()); + return ByteUtils.concat(ammount.array(), ba.array()); + } + + /** + * writerAsset + * + * @param v + * @return + */ + public static byte[] writerSymbol(String v) { + String _value[] = v.split(" "); + String amount = _value[0]; + if(amount==null || !amount.matches("^[0-9]+(.[0-9]+)?$")){ + throw new EException("amount_error", "amount error"); + } + String sym = _value[1]; + String precision = sym.split(",")[0]; + String symbol = sym.split(",")[1].split("@")[0]; + String[] part = amount.split("[.]"); + + int pad = Integer.parseInt(precision); + StringBuffer bf = new StringBuffer(part[0] + "."); + if (part.length > 1) { + if(part[1].length()>pad) { + throw new EException("precision_error", "precision max "+pad); + } + pad = Integer.parseInt(precision) - part[1].length(); + bf.append(part[1]); + } + // ���Ȳ�0 + for (int i = 0; i < pad; i++) { + bf.append("0"); + } + String asset = precision + "," + symbol; + // amount +// amount = bf.toString().replace(".", ""); +// ByteBuffer ammount = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.LITTLE_ENDIAN) +// .putLong(Long.parseLong(amount)); + + // asset + StringBuffer padStr = new StringBuffer(); + for (int i = 0; i < (7 - symbol.length()); i++) { + padStr.append("\0"); + } + char c = (char) Integer.parseInt(precision); + asset = c + symbol + padStr; + ByteBuffer ba = ByteBuffer.wrap(asset.getBytes()); + return ba.array(); + } + + /** + * writerAccount + * + * @param v + * @return + */ + public static byte[] writeName(String v) { + StringBuffer bitstr = new StringBuffer(); + for (int i = 0; i <= 12; i++) { + int c = i < v.length() ? ByteUtils.charidx(v.charAt(i)) : 0; + int bitlen = i < 12 ? 5 : 4; + String bits = Integer.toBinaryString(c); + if (bits.length() > bitlen) { + throw new EException("", "Invalid name " + v); + } + StringBuffer sb = new StringBuffer(""); + for (int j = 0; j < bitlen - bits.length(); j++) { + sb.append("0"); + } + bits = sb + bits; + bitstr.append(bits); + } + BigInteger lv = new BigInteger(bitstr.toString(), 2); + StringBuffer leHex = new StringBuffer(); + int bytes[] = ByteUtils.LongToBytes(lv.longValue()); + for (int i = 0; i < bytes.length; i++) { + int b = bytes[i]; + String n = Integer.toHexString(b); + leHex.append(n.length() == 1 ? "0" : "").append(n); + } + BigInteger ulName = new BigInteger(leHex.toString(), 16); + return ByteBuffer.allocate(Long.BYTES).order(ByteOrder.LITTLE_ENDIAN).putLong(ulName.longValue()).array(); + } + + /** + * charCount + * + * @return + */ + private static long charCount(String v) { + long c = 0; + for (char cp : v.toCharArray()) { + if (cp < 0x80) { + c += 1; + } else if (cp < 0x800) { + c += 2; + } else if (cp < 0x10000) { + c += 3; + } else { + c += 4; + } + } + return c; + } + + /** + * writerString + * + * @param v + * @return + */ + public static byte[] writerString(String v) { + long value = charCount(v); + byte[] a = new byte[] {}; + value >>>= 0; + while (value >= 0x80) { + long b = (value & 0x7f) | 0x80; + a = ByteUtils.concat(a, new byte[] { (byte) b }); + value >>>= 7; + } + a = ByteUtils.concat(a, new byte[] { (byte) value }); + for (char c : v.toCharArray()) { + a = ByteUtils.concat(a, decodeChar(c)); + } + return a; + } + + /** + * decodeChar + * + * @param ca + * @return + */ + private static byte[] decodeChar(char ca) { + long cp = (long) ca; + if (cp < 0x80) { + long a = cp & 0x7F; + return new byte[] { (byte) a }; + } else if (cp < 0x800) { + long a = ((cp >> 6) & 0x1F) | 0xC0; + long b = (cp & 0x3F) | 0x80; + return new byte[] { (byte) a, (byte) b }; + } else if (cp < 0x10000) { + long a = ((cp >> 12) & 0x0F) | 0xE0; + long b = ((cp >> 6) & 0x3F) | 0x80; + long c = (cp & 0x3F) | 0x80; + return new byte[] { (byte) a, (byte) b, (byte) c }; + } else { + long a = ((cp >> 18) & 0x07) | 0xF0; + long b = ((cp >> 12) & 0x3F) | 0x80; + long c = ((cp >> 6) & 0x3F) | 0x80; + long d = (cp & 0x3F) | 0x80; + return new byte[] { (byte) a, (byte) b, (byte) c, (byte) d }; + } + } + + /** + * writerKey + * + * @param v + * @return + */ + private static byte[] writerKeyStr(String v) { + v = v.replace("EOS", ""); + byte[] b = Base58.decode(v); + b = ByteBuffer.allocate(b.length).order(ByteOrder.BIG_ENDIAN).put(b).array(); + byte[] key = ByteUtils.copy(b, 0, b.length - 4); + return key; + } + + /** + * writerKey + * + * @param key + */ + public static byte[] writerKey(String key) { + com.blockchain.server.eos.eos4j.utils.ByteBuffer bf = new com.blockchain.server.eos.eos4j.utils.ByteBuffer (); + bf.concat(writerUnit32("1")); + bf.concat(writerVarint32("1")); + bf.concat(writerVarint32("0")); + bf.concat(writerKeyStr(key)); + bf.concat(writerUnit16("1")); + bf.concat(writerVarint32("0")); + bf.concat(writerVarint32("0")); + return bf.getBuffer(); + } + + public static byte[] writeUint64(String v) { + return ByteBuffer.allocate(Long.BYTES).order(ByteOrder.LITTLE_ENDIAN).putLong(Long.parseLong(v)).array(); + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/EException.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/EException.java new file mode 100644 index 0000000..acc1b67 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/EException.java @@ -0,0 +1,38 @@ +package com.blockchain.server.eos.eos4j.utils; + +/** + * exception + * + * @author espritblock http://eblock.io + * + */ +public class EException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public EException(String code, String msg) { + this.code = code; + this.msg = msg; + } + + private String code; + + private String msg; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/GeneralDigest.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/GeneralDigest.java new file mode 100644 index 0000000..25c8b08 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/GeneralDigest.java @@ -0,0 +1,109 @@ +package com.blockchain.server.eos.eos4j.utils; + +public abstract class GeneralDigest { + private static final int BYTE_LENGTH = 64; + private byte[] xBuf; + private int xBufOff; + + private long byteCount; + + /** + * Standard constructor + */ + protected GeneralDigest() { + xBuf = new byte[4]; + xBufOff = 0; + } + + /** + * Copy constructor. We are using copy constructors in place of the + * Object.clone() interface as this interface is not supported by J2ME. + */ + protected GeneralDigest(GeneralDigest t) { + xBuf = new byte[t.xBuf.length]; + System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length); + + xBufOff = t.xBufOff; + byteCount = t.byteCount; + } + + public void update(byte in) { + xBuf[xBufOff++] = in; + + if (xBufOff == xBuf.length) { + processWord(xBuf, 0); + xBufOff = 0; + } + + byteCount++; + } + + public void update(byte[] in, int inOff, int len) { + // + // fill the current word + // + while ((xBufOff != 0) && (len > 0)) { + update(in[inOff]); + + inOff++; + len--; + } + + // + // process whole words. + // + while (len > xBuf.length) { + processWord(in, inOff); + + inOff += xBuf.length; + len -= xBuf.length; + byteCount += xBuf.length; + } + + // + // load in the remainder. + // + while (len > 0) { + update(in[inOff]); + + inOff++; + len--; + } + } + + public void finish() { + long bitLength = (byteCount << 3); + + // + // add the pad bytes. + // + update((byte) 128); + + while (xBufOff != 0) { + update((byte) 0); + } + + processLength(bitLength); + + processBlock(); + } + + public void reset() { + byteCount = 0; + + xBufOff = 0; + for (int i = 0; i < xBuf.length; i++) { + xBuf[i] = 0; + } + } + + public int getByteLength() { + return BYTE_LENGTH; + } + + protected abstract void processWord(byte[] in, int inOff); + + protected abstract void processLength(long bitLength); + + protected abstract void processBlock(); +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Hex.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Hex.java new file mode 100644 index 0000000..15082e9 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Hex.java @@ -0,0 +1,84 @@ +package com.blockchain.server.eos.eos4j.utils; + +/** + * Hex + * + * @author espritblock http://eblock.io + * + */ +public class Hex { + + /** + * toBytes + * + * @param hexString + * @return + */ + public static byte[] toBytes(String hex) { + if (hex == null || hex.length() % 2 != 0) { + throw new EException("args_eroor", "args is error"); + } + char[] hbyte = hex.toCharArray(); + int length = hbyte.length / 2; + byte[] raw = new byte[length]; + for (int i = 0; i < length; i++) { + int high = Character.digit(hbyte[i * 2], 16); + int low = Character.digit(hbyte[i * 2 + 1], 16); + if (high < 0 || low < 0) { + throw new RuntimeException("Invalid hex digit " + hbyte[i * 2] + hbyte[i * 2 + 1]); + } + int value = (high << 4) | low; + if (value > 127) + value -= 256; + raw[i] = (byte) value; + } + return raw; + } + + /** + * bytesToHexString + * + * @param src + * @return + */ + public static String bytesToHexString(byte[] src) { + StringBuilder stringBuilder = new StringBuilder(""); + if (src == null || src.length <= 0) { + return null; + } + for (int i = 0; i < src.length; i++) { + int v = src[i] & 0xFF; + String hv = Integer.toHexString(v); + if (hv.length() < 2) { + stringBuilder.append(0); + } + stringBuilder.append(hv); + } + return stringBuilder.toString(); + } + + /** + * hexStringToBytes + * + * @param hexString + * @return + */ + public static byte[] hexStringToBytes(String hexString) { + if (hexString == null || hexString.equals("")) { + return null; + } + hexString = hexString.toUpperCase(); + int length = hexString.length() / 2; + char[] hexChars = hexString.toCharArray(); + byte[] d = new byte[length]; + for (int i = 0; i < length; i++) { + int pos = i * 2; + d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); + } + return d; + } + + private static byte charToByte(char c) { + return (byte) "0123456789ABCDEF".indexOf(c); + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/ObjectUtils.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/ObjectUtils.java new file mode 100644 index 0000000..cc960c9 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/ObjectUtils.java @@ -0,0 +1,157 @@ +package com.blockchain.server.eos.eos4j.utils; + + +import com.blockchain.server.eos.eos4j.api.vo.BaseVo; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class ObjectUtils { + + private static Object getFieldValueByName(String fieldName, Object o) { + try { + String firstLetter = fieldName.substring(0, 1).toUpperCase(); + String getter = "get" + firstLetter + fieldName.substring(1); + Method method = o.getClass().getMethod(getter, new Class[] {}); + Object value = method.invoke(o, new Object[] {}); + return value; + } catch (Exception e) { + return null; + } + } + + /** + * Bean2Map + * + * @param obj + * @return + */ + public static Map Bean2Map(Object obj) { + if (obj == null) { + return null; + } + Map map = new LinkedHashMap<>(); + Field[] fields = obj.getClass().getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + map.put(fields[i].getName(), getFieldValueByName(fields[i].getName(), obj)); + } + return map; + } + + public static void writeBytes(Object vo, ByteBuffer bf) { + Map params = null; + if (vo instanceof Map) { + params = (Map) vo; + } else { + params = Bean2Map(vo); + } + Map objMap = new LinkedHashMap<>(); + for (String key : params.keySet()) { + Object obj = params.get(key); + if (obj instanceof BaseVo || obj instanceof List || obj instanceof Map) { + if ("authorization".equals(key)) { + bf.concat(ByteUtils.writerVarint32(String.valueOf(((List) obj).size()))); + for (Object ob : (List) obj) { + writeBytes(ob, bf); + } + } else if ("data".equals(key)) { + ByteBuffer databf = new ByteBuffer(); + writeBytes(obj, databf); + bf.concat(ByteUtils.writerVarint32(String.valueOf(databf.getBuffer().length))); + bf.concat(databf.getBuffer()); + } else if ("transaction_extensions".equals(key)) { + + } else { + objMap.put(key, obj); + } + } else { + if ("chain_id".equals(key)) { + bf.concat(Hex.hexStringToBytes(obj.toString())); + } else if ("expiration".equals(key)) { + bf.concat(ByteUtils.writerUnit32(obj.toString())); + } else if ("ref_block_num".equals(key)) { + bf.concat(ByteUtils.writerUnit16(obj.toString())); + } else if ("ref_block_prefix".equals(key)) { + bf.concat(ByteUtils.writerUnit32(obj.toString())); + } else if ("net_usage_words".equals(key)) { + bf.concat(ByteUtils.writerVarint32(obj.toString())); + } else if ("max_cpu_usage_ms".equals(key)) { + bf.concat(ByteUtils.writerUnit8(obj.toString())); + } else if ("delay_sec".equals(key)) { + bf.concat(ByteUtils.writerVarint32(obj.toString())); + } else if ("account".equals(key)) { + bf.concat(ByteUtils.writeName(obj.toString())); + } else if ("name".equals(key)) { + bf.concat(ByteUtils.writeName(obj.toString())); + } else if ("actor".equals(key)) { + bf.concat(ByteUtils.writeName(obj.toString())); + } else if ("permission".equals(key)) { + bf.concat(ByteUtils.writeName(obj.toString())); + } else if ("from".equals(key)) { + bf.concat(ByteUtils.writeName(obj.toString())); + } else if ("to".equals(key)) { + bf.concat(ByteUtils.writeName(obj.toString())); + } else if ("quantity".equals(key)) { + bf.concat(ByteUtils.writerAsset(obj.toString())); + } else if ("memo".equals(key)) { + bf.concat(ByteUtils.writerString(obj.toString())); + } else if ("creator".equals(key)) { + bf.concat(ByteUtils.writeName(obj.toString())); + } else if ("owner".equals(key)) { + bf.concat(ByteUtils.writerKey(obj.toString())); + } else if ("active".equals(key)) { + bf.concat(ByteUtils.writerKey(obj.toString())); + } else if ("payer".equals(key)) { + bf.concat(ByteUtils.writeName(obj.toString())); + } else if ("receiver".equals(key)) { + bf.concat(ByteUtils.writeName(obj.toString())); + } else if ("bytes".equals(key)) { + bf.concat(ByteUtils.writerUnit32(obj.toString())); + } else if ("stake_net_quantity".equals(key)) { + bf.concat(ByteUtils.writerAsset(obj.toString())); + } else if ("stake_cpu_quantity".equals(key)) { + bf.concat(ByteUtils.writerAsset(obj.toString())); + } else if ("transfer".equals(key)) { + bf.concat(ByteUtils.writerUnit8(obj.toString())); + }else if ("voter".equals(key)) { + bf.concat(ByteUtils.writeName(obj.toString())); + }else if ("proxy".equals(key)) { + bf.concat(ByteUtils.writeName(obj.toString())); + }else if ("producer".equals(key)) { + bf.concat(ByteUtils.writeName(obj.toString())); + }else if ("close-owner".equals(key)) { + bf.concat(ByteUtils.writeName(obj.toString())); + }else if ("close-symbol".equals(key)) { + bf.concat(ByteUtils.writerSymbol(obj.toString())); + } + } + } + for (String key : objMap.keySet()) { + Object obj = params.get(key); + if ("context_free_actions".equals(key)) { + bf.concat(ByteUtils.writerVarint32(String.valueOf(((List) obj).size()))); + for (Object ob : (List) obj) { + writeBytes(ob, bf); + } + } else if ("actions".equals(key)) { + bf.concat(ByteUtils.writerVarint32(String.valueOf(((List) obj).size()))); + for (Object ob : (List) obj) { + writeBytes(ob, bf); + } + }else if ("producers".equals(key)) { + bf.concat(ByteUtils.writerVarint32(String.valueOf(((List) obj).size()))); + for (Object ob : (List) obj) { + Map mp = new HashMap<>(); + mp.put("producer", ob); + writeBytes(mp, bf); + } + }else { + writeBytes(obj, bf); + } + } + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Ripemd160.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Ripemd160.java new file mode 100644 index 0000000..6d9b453 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Ripemd160.java @@ -0,0 +1,533 @@ +package com.blockchain.server.eos.eos4j.utils; + +public class Ripemd160 { + + final private byte[] mDigestBytes; + + public Ripemd160(byte[] digest) { + mDigestBytes = digest; + } + + public static Ripemd160 from(byte[] data) { + + return Ripemd160.from(data, 0, (data != null) ? data.length : 0); + } + + public static Ripemd160 from(byte[] data, int startOffset, int length) { + Ripemd160.Digest digest = new Ripemd160.Digest(); + digest.update(data, startOffset, length); + + byte[] result = new byte[Ripemd160.Digest.DIGEST_LENGTH]; + digest.doFinal(result, 0); + + return new Ripemd160(result); + } + + public byte[] bytes() { + return mDigestBytes; + } + + public static class Digest extends GeneralDigest { + static final int DIGEST_LENGTH = 20; + + private int H0, H1, H2, H3, H4; // IV's + + private int[] X = new int[16]; + private int xOff; + + /** + * Standard constructor + */ + public Digest() { + reset(); + } + + protected void processWord(byte[] in, int inOff) { + X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8) | ((in[inOff + 2] & 0xff) << 16) + | ((in[inOff + 3] & 0xff) << 24); + + if (xOff == 16) { + processBlock(); + } + } + + protected void processLength(long bitLength) { + if (xOff > 14) { + processBlock(); + } + + X[14] = (int) (bitLength & 0xffffffff); + X[15] = (int) (bitLength >>> 32); + } + + private void unpackWord(int word, byte[] out, int outOff) { + out[outOff] = (byte) word; + out[outOff + 1] = (byte) (word >>> 8); + out[outOff + 2] = (byte) (word >>> 16); + out[outOff + 3] = (byte) (word >>> 24); + } + + public int doFinal(byte[] out, int outOff) { + finish(); + + unpackWord(H0, out, outOff); + unpackWord(H1, out, outOff + 4); + unpackWord(H2, out, outOff + 8); + unpackWord(H3, out, outOff + 12); + unpackWord(H4, out, outOff + 16); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables to the IV values. + */ + public void reset() { + super.reset(); + + H0 = 0x67452301; + H1 = 0xefcdab89; + H2 = 0x98badcfe; + H3 = 0x10325476; + H4 = 0xc3d2e1f0; + + xOff = 0; + + for (int i = 0; i != X.length; i++) { + X[i] = 0; + } + } + + /* + * rotate int x left n bits. + */ + private int RL(int x, int n) { + return (x << n) | (x >>> (32 - n)); + } + + /* + * f1,f2,f3,f4,f5 are the basic Digest functions. + */ + + /* + * rounds 0-15 + */ + private int f1(int x, int y, int z) { + return x ^ y ^ z; + } + + /* + * rounds 16-31 + */ + private int f2(int x, int y, int z) { + return (x & y) | (~x & z); + } + + /* + * rounds 32-47 + */ + private int f3(int x, int y, int z) { + return (x | ~y) ^ z; + } + + /* + * rounds 48-63 + */ + private int f4(int x, int y, int z) { + return (x & z) | (y & ~z); + } + + /* + * rounds 64-79 + */ + private int f5(int x, int y, int z) { + return x ^ (y | ~z); + } + + protected void processBlock() { + int a, aa; + int b, bb; + int c, cc; + int d, dd; + int e, ee; + + a = aa = H0; + b = bb = H1; + c = cc = H2; + d = dd = H3; + e = ee = H4; + + // + // Rounds 1 - 16 + // + // left + a = RL(a + f1(b, c, d) + X[0], 11) + e; + c = RL(c, 10); + e = RL(e + f1(a, b, c) + X[1], 14) + d; + b = RL(b, 10); + d = RL(d + f1(e, a, b) + X[2], 15) + c; + a = RL(a, 10); + c = RL(c + f1(d, e, a) + X[3], 12) + b; + e = RL(e, 10); + b = RL(b + f1(c, d, e) + X[4], 5) + a; + d = RL(d, 10); + a = RL(a + f1(b, c, d) + X[5], 8) + e; + c = RL(c, 10); + e = RL(e + f1(a, b, c) + X[6], 7) + d; + b = RL(b, 10); + d = RL(d + f1(e, a, b) + X[7], 9) + c; + a = RL(a, 10); + c = RL(c + f1(d, e, a) + X[8], 11) + b; + e = RL(e, 10); + b = RL(b + f1(c, d, e) + X[9], 13) + a; + d = RL(d, 10); + a = RL(a + f1(b, c, d) + X[10], 14) + e; + c = RL(c, 10); + e = RL(e + f1(a, b, c) + X[11], 15) + d; + b = RL(b, 10); + d = RL(d + f1(e, a, b) + X[12], 6) + c; + a = RL(a, 10); + c = RL(c + f1(d, e, a) + X[13], 7) + b; + e = RL(e, 10); + b = RL(b + f1(c, d, e) + X[14], 9) + a; + d = RL(d, 10); + a = RL(a + f1(b, c, d) + X[15], 8) + e; + c = RL(c, 10); + + // right + aa = RL(aa + f5(bb, cc, dd) + X[5] + 0x50a28be6, 8) + ee; + cc = RL(cc, 10); + ee = RL(ee + f5(aa, bb, cc) + X[14] + 0x50a28be6, 9) + dd; + bb = RL(bb, 10); + dd = RL(dd + f5(ee, aa, bb) + X[7] + 0x50a28be6, 9) + cc; + aa = RL(aa, 10); + cc = RL(cc + f5(dd, ee, aa) + X[0] + 0x50a28be6, 11) + bb; + ee = RL(ee, 10); + bb = RL(bb + f5(cc, dd, ee) + X[9] + 0x50a28be6, 13) + aa; + dd = RL(dd, 10); + aa = RL(aa + f5(bb, cc, dd) + X[2] + 0x50a28be6, 15) + ee; + cc = RL(cc, 10); + ee = RL(ee + f5(aa, bb, cc) + X[11] + 0x50a28be6, 15) + dd; + bb = RL(bb, 10); + dd = RL(dd + f5(ee, aa, bb) + X[4] + 0x50a28be6, 5) + cc; + aa = RL(aa, 10); + cc = RL(cc + f5(dd, ee, aa) + X[13] + 0x50a28be6, 7) + bb; + ee = RL(ee, 10); + bb = RL(bb + f5(cc, dd, ee) + X[6] + 0x50a28be6, 7) + aa; + dd = RL(dd, 10); + aa = RL(aa + f5(bb, cc, dd) + X[15] + 0x50a28be6, 8) + ee; + cc = RL(cc, 10); + ee = RL(ee + f5(aa, bb, cc) + X[8] + 0x50a28be6, 11) + dd; + bb = RL(bb, 10); + dd = RL(dd + f5(ee, aa, bb) + X[1] + 0x50a28be6, 14) + cc; + aa = RL(aa, 10); + cc = RL(cc + f5(dd, ee, aa) + X[10] + 0x50a28be6, 14) + bb; + ee = RL(ee, 10); + bb = RL(bb + f5(cc, dd, ee) + X[3] + 0x50a28be6, 12) + aa; + dd = RL(dd, 10); + aa = RL(aa + f5(bb, cc, dd) + X[12] + 0x50a28be6, 6) + ee; + cc = RL(cc, 10); + + // + // Rounds 16-31 + // + // left + e = RL(e + f2(a, b, c) + X[7] + 0x5a827999, 7) + d; + b = RL(b, 10); + d = RL(d + f2(e, a, b) + X[4] + 0x5a827999, 6) + c; + a = RL(a, 10); + c = RL(c + f2(d, e, a) + X[13] + 0x5a827999, 8) + b; + e = RL(e, 10); + b = RL(b + f2(c, d, e) + X[1] + 0x5a827999, 13) + a; + d = RL(d, 10); + a = RL(a + f2(b, c, d) + X[10] + 0x5a827999, 11) + e; + c = RL(c, 10); + e = RL(e + f2(a, b, c) + X[6] + 0x5a827999, 9) + d; + b = RL(b, 10); + d = RL(d + f2(e, a, b) + X[15] + 0x5a827999, 7) + c; + a = RL(a, 10); + c = RL(c + f2(d, e, a) + X[3] + 0x5a827999, 15) + b; + e = RL(e, 10); + b = RL(b + f2(c, d, e) + X[12] + 0x5a827999, 7) + a; + d = RL(d, 10); + a = RL(a + f2(b, c, d) + X[0] + 0x5a827999, 12) + e; + c = RL(c, 10); + e = RL(e + f2(a, b, c) + X[9] + 0x5a827999, 15) + d; + b = RL(b, 10); + d = RL(d + f2(e, a, b) + X[5] + 0x5a827999, 9) + c; + a = RL(a, 10); + c = RL(c + f2(d, e, a) + X[2] + 0x5a827999, 11) + b; + e = RL(e, 10); + b = RL(b + f2(c, d, e) + X[14] + 0x5a827999, 7) + a; + d = RL(d, 10); + a = RL(a + f2(b, c, d) + X[11] + 0x5a827999, 13) + e; + c = RL(c, 10); + e = RL(e + f2(a, b, c) + X[8] + 0x5a827999, 12) + d; + b = RL(b, 10); + + // right + ee = RL(ee + f4(aa, bb, cc) + X[6] + 0x5c4dd124, 9) + dd; + bb = RL(bb, 10); + dd = RL(dd + f4(ee, aa, bb) + X[11] + 0x5c4dd124, 13) + cc; + aa = RL(aa, 10); + cc = RL(cc + f4(dd, ee, aa) + X[3] + 0x5c4dd124, 15) + bb; + ee = RL(ee, 10); + bb = RL(bb + f4(cc, dd, ee) + X[7] + 0x5c4dd124, 7) + aa; + dd = RL(dd, 10); + aa = RL(aa + f4(bb, cc, dd) + X[0] + 0x5c4dd124, 12) + ee; + cc = RL(cc, 10); + ee = RL(ee + f4(aa, bb, cc) + X[13] + 0x5c4dd124, 8) + dd; + bb = RL(bb, 10); + dd = RL(dd + f4(ee, aa, bb) + X[5] + 0x5c4dd124, 9) + cc; + aa = RL(aa, 10); + cc = RL(cc + f4(dd, ee, aa) + X[10] + 0x5c4dd124, 11) + bb; + ee = RL(ee, 10); + bb = RL(bb + f4(cc, dd, ee) + X[14] + 0x5c4dd124, 7) + aa; + dd = RL(dd, 10); + aa = RL(aa + f4(bb, cc, dd) + X[15] + 0x5c4dd124, 7) + ee; + cc = RL(cc, 10); + ee = RL(ee + f4(aa, bb, cc) + X[8] + 0x5c4dd124, 12) + dd; + bb = RL(bb, 10); + dd = RL(dd + f4(ee, aa, bb) + X[12] + 0x5c4dd124, 7) + cc; + aa = RL(aa, 10); + cc = RL(cc + f4(dd, ee, aa) + X[4] + 0x5c4dd124, 6) + bb; + ee = RL(ee, 10); + bb = RL(bb + f4(cc, dd, ee) + X[9] + 0x5c4dd124, 15) + aa; + dd = RL(dd, 10); + aa = RL(aa + f4(bb, cc, dd) + X[1] + 0x5c4dd124, 13) + ee; + cc = RL(cc, 10); + ee = RL(ee + f4(aa, bb, cc) + X[2] + 0x5c4dd124, 11) + dd; + bb = RL(bb, 10); + + // + // Rounds 32-47 + // + // left + d = RL(d + f3(e, a, b) + X[3] + 0x6ed9eba1, 11) + c; + a = RL(a, 10); + c = RL(c + f3(d, e, a) + X[10] + 0x6ed9eba1, 13) + b; + e = RL(e, 10); + b = RL(b + f3(c, d, e) + X[14] + 0x6ed9eba1, 6) + a; + d = RL(d, 10); + a = RL(a + f3(b, c, d) + X[4] + 0x6ed9eba1, 7) + e; + c = RL(c, 10); + e = RL(e + f3(a, b, c) + X[9] + 0x6ed9eba1, 14) + d; + b = RL(b, 10); + d = RL(d + f3(e, a, b) + X[15] + 0x6ed9eba1, 9) + c; + a = RL(a, 10); + c = RL(c + f3(d, e, a) + X[8] + 0x6ed9eba1, 13) + b; + e = RL(e, 10); + b = RL(b + f3(c, d, e) + X[1] + 0x6ed9eba1, 15) + a; + d = RL(d, 10); + a = RL(a + f3(b, c, d) + X[2] + 0x6ed9eba1, 14) + e; + c = RL(c, 10); + e = RL(e + f3(a, b, c) + X[7] + 0x6ed9eba1, 8) + d; + b = RL(b, 10); + d = RL(d + f3(e, a, b) + X[0] + 0x6ed9eba1, 13) + c; + a = RL(a, 10); + c = RL(c + f3(d, e, a) + X[6] + 0x6ed9eba1, 6) + b; + e = RL(e, 10); + b = RL(b + f3(c, d, e) + X[13] + 0x6ed9eba1, 5) + a; + d = RL(d, 10); + a = RL(a + f3(b, c, d) + X[11] + 0x6ed9eba1, 12) + e; + c = RL(c, 10); + e = RL(e + f3(a, b, c) + X[5] + 0x6ed9eba1, 7) + d; + b = RL(b, 10); + d = RL(d + f3(e, a, b) + X[12] + 0x6ed9eba1, 5) + c; + a = RL(a, 10); + + // right + dd = RL(dd + f3(ee, aa, bb) + X[15] + 0x6d703ef3, 9) + cc; + aa = RL(aa, 10); + cc = RL(cc + f3(dd, ee, aa) + X[5] + 0x6d703ef3, 7) + bb; + ee = RL(ee, 10); + bb = RL(bb + f3(cc, dd, ee) + X[1] + 0x6d703ef3, 15) + aa; + dd = RL(dd, 10); + aa = RL(aa + f3(bb, cc, dd) + X[3] + 0x6d703ef3, 11) + ee; + cc = RL(cc, 10); + ee = RL(ee + f3(aa, bb, cc) + X[7] + 0x6d703ef3, 8) + dd; + bb = RL(bb, 10); + dd = RL(dd + f3(ee, aa, bb) + X[14] + 0x6d703ef3, 6) + cc; + aa = RL(aa, 10); + cc = RL(cc + f3(dd, ee, aa) + X[6] + 0x6d703ef3, 6) + bb; + ee = RL(ee, 10); + bb = RL(bb + f3(cc, dd, ee) + X[9] + 0x6d703ef3, 14) + aa; + dd = RL(dd, 10); + aa = RL(aa + f3(bb, cc, dd) + X[11] + 0x6d703ef3, 12) + ee; + cc = RL(cc, 10); + ee = RL(ee + f3(aa, bb, cc) + X[8] + 0x6d703ef3, 13) + dd; + bb = RL(bb, 10); + dd = RL(dd + f3(ee, aa, bb) + X[12] + 0x6d703ef3, 5) + cc; + aa = RL(aa, 10); + cc = RL(cc + f3(dd, ee, aa) + X[2] + 0x6d703ef3, 14) + bb; + ee = RL(ee, 10); + bb = RL(bb + f3(cc, dd, ee) + X[10] + 0x6d703ef3, 13) + aa; + dd = RL(dd, 10); + aa = RL(aa + f3(bb, cc, dd) + X[0] + 0x6d703ef3, 13) + ee; + cc = RL(cc, 10); + ee = RL(ee + f3(aa, bb, cc) + X[4] + 0x6d703ef3, 7) + dd; + bb = RL(bb, 10); + dd = RL(dd + f3(ee, aa, bb) + X[13] + 0x6d703ef3, 5) + cc; + aa = RL(aa, 10); + + // + // Rounds 48-63 + // + // left + c = RL(c + f4(d, e, a) + X[1] + 0x8f1bbcdc, 11) + b; + e = RL(e, 10); + b = RL(b + f4(c, d, e) + X[9] + 0x8f1bbcdc, 12) + a; + d = RL(d, 10); + a = RL(a + f4(b, c, d) + X[11] + 0x8f1bbcdc, 14) + e; + c = RL(c, 10); + e = RL(e + f4(a, b, c) + X[10] + 0x8f1bbcdc, 15) + d; + b = RL(b, 10); + d = RL(d + f4(e, a, b) + X[0] + 0x8f1bbcdc, 14) + c; + a = RL(a, 10); + c = RL(c + f4(d, e, a) + X[8] + 0x8f1bbcdc, 15) + b; + e = RL(e, 10); + b = RL(b + f4(c, d, e) + X[12] + 0x8f1bbcdc, 9) + a; + d = RL(d, 10); + a = RL(a + f4(b, c, d) + X[4] + 0x8f1bbcdc, 8) + e; + c = RL(c, 10); + e = RL(e + f4(a, b, c) + X[13] + 0x8f1bbcdc, 9) + d; + b = RL(b, 10); + d = RL(d + f4(e, a, b) + X[3] + 0x8f1bbcdc, 14) + c; + a = RL(a, 10); + c = RL(c + f4(d, e, a) + X[7] + 0x8f1bbcdc, 5) + b; + e = RL(e, 10); + b = RL(b + f4(c, d, e) + X[15] + 0x8f1bbcdc, 6) + a; + d = RL(d, 10); + a = RL(a + f4(b, c, d) + X[14] + 0x8f1bbcdc, 8) + e; + c = RL(c, 10); + e = RL(e + f4(a, b, c) + X[5] + 0x8f1bbcdc, 6) + d; + b = RL(b, 10); + d = RL(d + f4(e, a, b) + X[6] + 0x8f1bbcdc, 5) + c; + a = RL(a, 10); + c = RL(c + f4(d, e, a) + X[2] + 0x8f1bbcdc, 12) + b; + e = RL(e, 10); + + // right + cc = RL(cc + f2(dd, ee, aa) + X[8] + 0x7a6d76e9, 15) + bb; + ee = RL(ee, 10); + bb = RL(bb + f2(cc, dd, ee) + X[6] + 0x7a6d76e9, 5) + aa; + dd = RL(dd, 10); + aa = RL(aa + f2(bb, cc, dd) + X[4] + 0x7a6d76e9, 8) + ee; + cc = RL(cc, 10); + ee = RL(ee + f2(aa, bb, cc) + X[1] + 0x7a6d76e9, 11) + dd; + bb = RL(bb, 10); + dd = RL(dd + f2(ee, aa, bb) + X[3] + 0x7a6d76e9, 14) + cc; + aa = RL(aa, 10); + cc = RL(cc + f2(dd, ee, aa) + X[11] + 0x7a6d76e9, 14) + bb; + ee = RL(ee, 10); + bb = RL(bb + f2(cc, dd, ee) + X[15] + 0x7a6d76e9, 6) + aa; + dd = RL(dd, 10); + aa = RL(aa + f2(bb, cc, dd) + X[0] + 0x7a6d76e9, 14) + ee; + cc = RL(cc, 10); + ee = RL(ee + f2(aa, bb, cc) + X[5] + 0x7a6d76e9, 6) + dd; + bb = RL(bb, 10); + dd = RL(dd + f2(ee, aa, bb) + X[12] + 0x7a6d76e9, 9) + cc; + aa = RL(aa, 10); + cc = RL(cc + f2(dd, ee, aa) + X[2] + 0x7a6d76e9, 12) + bb; + ee = RL(ee, 10); + bb = RL(bb + f2(cc, dd, ee) + X[13] + 0x7a6d76e9, 9) + aa; + dd = RL(dd, 10); + aa = RL(aa + f2(bb, cc, dd) + X[9] + 0x7a6d76e9, 12) + ee; + cc = RL(cc, 10); + ee = RL(ee + f2(aa, bb, cc) + X[7] + 0x7a6d76e9, 5) + dd; + bb = RL(bb, 10); + dd = RL(dd + f2(ee, aa, bb) + X[10] + 0x7a6d76e9, 15) + cc; + aa = RL(aa, 10); + cc = RL(cc + f2(dd, ee, aa) + X[14] + 0x7a6d76e9, 8) + bb; + ee = RL(ee, 10); + + // + // Rounds 64-79 + // + // left + b = RL(b + f5(c, d, e) + X[4] + 0xa953fd4e, 9) + a; + d = RL(d, 10); + a = RL(a + f5(b, c, d) + X[0] + 0xa953fd4e, 15) + e; + c = RL(c, 10); + e = RL(e + f5(a, b, c) + X[5] + 0xa953fd4e, 5) + d; + b = RL(b, 10); + d = RL(d + f5(e, a, b) + X[9] + 0xa953fd4e, 11) + c; + a = RL(a, 10); + c = RL(c + f5(d, e, a) + X[7] + 0xa953fd4e, 6) + b; + e = RL(e, 10); + b = RL(b + f5(c, d, e) + X[12] + 0xa953fd4e, 8) + a; + d = RL(d, 10); + a = RL(a + f5(b, c, d) + X[2] + 0xa953fd4e, 13) + e; + c = RL(c, 10); + e = RL(e + f5(a, b, c) + X[10] + 0xa953fd4e, 12) + d; + b = RL(b, 10); + d = RL(d + f5(e, a, b) + X[14] + 0xa953fd4e, 5) + c; + a = RL(a, 10); + c = RL(c + f5(d, e, a) + X[1] + 0xa953fd4e, 12) + b; + e = RL(e, 10); + b = RL(b + f5(c, d, e) + X[3] + 0xa953fd4e, 13) + a; + d = RL(d, 10); + a = RL(a + f5(b, c, d) + X[8] + 0xa953fd4e, 14) + e; + c = RL(c, 10); + e = RL(e + f5(a, b, c) + X[11] + 0xa953fd4e, 11) + d; + b = RL(b, 10); + d = RL(d + f5(e, a, b) + X[6] + 0xa953fd4e, 8) + c; + a = RL(a, 10); + c = RL(c + f5(d, e, a) + X[15] + 0xa953fd4e, 5) + b; + e = RL(e, 10); + b = RL(b + f5(c, d, e) + X[13] + 0xa953fd4e, 6) + a; + d = RL(d, 10); + + // right + bb = RL(bb + f1(cc, dd, ee) + X[12], 8) + aa; + dd = RL(dd, 10); + aa = RL(aa + f1(bb, cc, dd) + X[15], 5) + ee; + cc = RL(cc, 10); + ee = RL(ee + f1(aa, bb, cc) + X[10], 12) + dd; + bb = RL(bb, 10); + dd = RL(dd + f1(ee, aa, bb) + X[4], 9) + cc; + aa = RL(aa, 10); + cc = RL(cc + f1(dd, ee, aa) + X[1], 12) + bb; + ee = RL(ee, 10); + bb = RL(bb + f1(cc, dd, ee) + X[5], 5) + aa; + dd = RL(dd, 10); + aa = RL(aa + f1(bb, cc, dd) + X[8], 14) + ee; + cc = RL(cc, 10); + ee = RL(ee + f1(aa, bb, cc) + X[7], 6) + dd; + bb = RL(bb, 10); + dd = RL(dd + f1(ee, aa, bb) + X[6], 8) + cc; + aa = RL(aa, 10); + cc = RL(cc + f1(dd, ee, aa) + X[2], 13) + bb; + ee = RL(ee, 10); + bb = RL(bb + f1(cc, dd, ee) + X[13], 6) + aa; + dd = RL(dd, 10); + aa = RL(aa + f1(bb, cc, dd) + X[14], 5) + ee; + cc = RL(cc, 10); + ee = RL(ee + f1(aa, bb, cc) + X[0], 15) + dd; + bb = RL(bb, 10); + dd = RL(dd + f1(ee, aa, bb) + X[3], 13) + cc; + aa = RL(aa, 10); + cc = RL(cc + f1(dd, ee, aa) + X[9], 11) + bb; + ee = RL(ee, 10); + bb = RL(bb + f1(cc, dd, ee) + X[11], 11) + aa; + dd = RL(dd, 10); + + dd += c + H1; + H1 = H2 + d + ee; + H2 = H3 + e + aa; + H3 = H4 + a + bb; + H4 = H0 + b + cc; + H0 = dd; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.length; i++) { + X[i] = 0; + } + } + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Sha.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Sha.java new file mode 100644 index 0000000..6820b5f --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/eos4j/utils/Sha.java @@ -0,0 +1,86 @@ +package com.blockchain.server.eos.eos4j.utils; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * SHA + * + * @author espritblock http://eblock.io + * + */ +public class Sha { + + public static final String SHA256 = "SHA-256"; + + /** + * sha256 + * + * @param text + * @return + */ + public static byte[] SHA256(final String text) { + if (text == null || text.length() == 0) { + throw new EException("args_empty", "args is empty"); + } + try { + MessageDigest messageDigest = MessageDigest.getInstance(SHA256); + messageDigest.update(text.getBytes("utf8")); + byte byteBuffer[] = messageDigest.digest(); + return byteBuffer; + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * sha256 + * + * @param text + * @return + */ + public static byte[] SHA256(final byte[] b) { + if (b == null || b.length == 0) { + throw new EException("args_empty", "args is empty"); + } + try { + MessageDigest messageDigest = MessageDigest.getInstance(SHA256); + messageDigest.update(b); + byte byteBuffer[] = messageDigest.digest(); + return byteBuffer; + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * HMACSHA256 + * + * @param data + * @param key + * @return + */ + public static byte[] HmacSHA256(byte[] data, byte[] key) { + try { + SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA256"); + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(signingKey); + return mac.doFinal(data); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } + return null; + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/feign/UserServerFegin.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/feign/UserServerFegin.java new file mode 100644 index 0000000..ac5f623 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/feign/UserServerFegin.java @@ -0,0 +1,46 @@ +package com.blockchain.server.eos.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient("dapp-user-server") +public interface UserServerFegin { + + /** + * 通过手机号获取用户id + * + * @param phone + * @return + */ +// @PostMapping("/login/getUserIdByPhone") +// ResultDTO getUserIdByPhone(@RequestParam("phone") String phone); + + + /** + * 校验验证码 + * + * @param phone + * @param verifyCode + * @return + */ +// @PostMapping("/inner/validateSmsg") +// ResultDTO validateSmsg(@RequestParam("phone") String phone, @RequestParam("verifyCode") String verifyCode); + + /** + * 禁止提现 + * @param userId + * @return + */ + @PostMapping("/inner/verifyBanWithdraw") + ResultDTO verifyBanWithdraw(@RequestParam("userId") String userId); + + /** + * 免提现手续费 + * @param userOpenId + * @return + */ + @PostMapping("/inner/verifyFreeWithdraw") + ResultDTO verifyFreeWithdraw(@RequestParam("userId") String userOpenId); +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/feign/WalletTransferFegin.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/feign/WalletTransferFegin.java new file mode 100644 index 0000000..a81ee6a --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/feign/WalletTransferFegin.java @@ -0,0 +1,24 @@ +package com.blockchain.server.eos.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author Harvey + * @date 2019/2/26 18:02 + * @user WIN10 + */ +@FeignClient("dapp-eth-server") +public interface WalletTransferFegin { + + /** + * 验证密码是否正确 + * @param password + * @return + */ + @GetMapping("/inner/wallet/isPassword") + ResultDTO isPassword(@RequestParam("password") String password); + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/inner/EosWalletInnerController.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/inner/EosWalletInnerController.java new file mode 100644 index 0000000..0f08afb --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/inner/EosWalletInnerController.java @@ -0,0 +1,52 @@ +package com.blockchain.server.eos.inner; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import com.blockchain.server.eos.inner.api.EosWalletApi; +import com.blockchain.server.eos.service.EosWalletService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * @author Harvey + * @date 2019/2/19 18:09 + * @user WIN10 + */ +@Api(EosWalletApi.EOS_WALLET_API) +@RestController +@RequestMapping("/inner/walletTx") +public class EosWalletInnerController { + private static final Logger LOG = LoggerFactory.getLogger(EosWalletInnerController.class); + @Autowired + private EosWalletService eosWalletService; + + @ApiOperation(value = EosWalletApi.Order.MATHOD_API_NAME, notes = EosWalletApi.Order.MATHOD_API_NOTE) + @PostMapping("/order") + public ResultDTO order(@ApiParam(EosWalletApi.Order.MATHOD_API_WALLET_ORDER_DTO) @RequestBody WalletOrderDTO walletOrderDTO) { + LOG.info("冻结、解冻余额,walletOrderDTO:" + walletOrderDTO.toString()); + eosWalletService.updateWalletOrder(walletOrderDTO); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = EosWalletApi.Change.MATHOD_API_NAME, notes = EosWalletApi.Change.MATHOD_API_NOTE) + @PostMapping("/change") + public ResultDTO change(@ApiParam(EosWalletApi.Change.MATHOD_API_WALLET_CHANGE_DTO) @RequestBody WalletChangeDTO walletChangeDTO) { + LOG.info("扣除、增加,walletChangeDTO:" + walletChangeDTO.toString()); + eosWalletService.handleWalletChange(walletChangeDTO); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = EosWalletApi.InitEosWallet.MATHOD_API_NAME, notes = EosWalletApi.InitEosWallet.MATHOD_API_NOTE) + @GetMapping("/initEosWallet") + public ResultDTO initEosWallet(@RequestParam("userOpenId") String userOpenId) { + eosWalletService.initEosWallet(userOpenId); + return ResultDTO.requstSuccess(); + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/inner/api/EosWalletApi.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/inner/api/EosWalletApi.java new file mode 100644 index 0000000..3a9e0c7 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/inner/api/EosWalletApi.java @@ -0,0 +1,30 @@ +package com.blockchain.server.eos.inner.api; + +/** + * @author Harvey + * @date 2019/2/19 18:10 + * @user WIN10 + */ +public class EosWalletApi { + public static final String EOS_WALLET_API = "钱包控制器"; + + public static class Order { + public static final String MATHOD_API_NAME = "冻结与解冻接口"; + public static final String MATHOD_API_NOTE = "冻结与解冻接口"; + + public static final String MATHOD_API_WALLET_ORDER_DTO = "对象参数"; + } + + public static class Change { + public static final String MATHOD_API_NAME = "钱包余额变动接口"; + public static final String MATHOD_API_NOTE = "钱包余额变动接口"; + + public static final String MATHOD_API_WALLET_CHANGE_DTO = "对象参数"; + } + + public static class InitEosWallet { + public static final String MATHOD_API_NAME = "初始化钱包"; + public static final String MATHOD_API_NOTE = "初始化钱包"; + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/ApplicationMapper.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/ApplicationMapper.java new file mode 100644 index 0000000..84be5aa --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/ApplicationMapper.java @@ -0,0 +1,22 @@ +package com.blockchain.server.eos.mapper; + +import com.blockchain.server.eos.entity.Application; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * @author Harvey + * @date 2019/2/18 16:50 + * @user WIN10 + */ +@Repository +public interface ApplicationMapper extends Mapper { + + /** + * 查询是否存在该应用钱包 + * @param walletType + * @return + */ + Integer selectWalletCountByWalletType(@Param("walletType") String walletType); +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/BlockNumberMapper.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/BlockNumberMapper.java new file mode 100644 index 0000000..4c01204 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/BlockNumberMapper.java @@ -0,0 +1,66 @@ +package com.blockchain.server.eos.mapper; + +import com.blockchain.server.eos.dto.BlockNumberDTO; +import com.blockchain.server.eos.entity.BlockNumber; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.math.BigInteger; +import java.util.List; + +/** + * BlockNumberMapper 数据访问类 + * @date 2018-11-05 15:10:47 + * @version 1.0 + */ +@Repository +public interface BlockNumberMapper extends Mapper { + + /** + * 查询最旧区块 + * @return + */ + BigInteger selectMinBlockNum(); + /** + * 查询最大的区块号 + * @return + */ + BigInteger selectMaxBlockNum(); + + /** + * 查询是否存在区块 + * @param blockNum + * @return + */ + BlockNumberDTO selectBlockNumIsExist(@Param("blockNum") BigInteger blockNum); + + /** + * 查找当前区块状态 + * @param blockNum + * @return + */ + Character selectBlockNumStatusByBlockNum(@Param("blockNum") BigInteger blockNum); + + /** + * 根据区块状态获取区块 + * @param status + * @return + */ + List listBlockNumberByStatus(@Param("status") Character status); + + /** + * 更新处理区块状态 + * @param blockNum + * @param status + * @return + */ + int updateBlockNumberByStatus(@Param("blockNum") BigInteger blockNum, @Param("status") Character status); + + /** + * + * @param blockNumber + * @return + */ + int insertSelectiveIgnore(BlockNumber blockNumber); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/ConfigWalletParamMapper.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/ConfigWalletParamMapper.java new file mode 100644 index 0000000..89863cd --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/ConfigWalletParamMapper.java @@ -0,0 +1,15 @@ +package com.blockchain.server.eos.mapper; + +import com.blockchain.server.eos.entity.ConfigWalletParam; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * @author Harvey + * @date 2019/2/18 16:50 + * @user WIN10 + */ +@Repository +public interface ConfigWalletParamMapper extends Mapper { + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/EosWalletInMapper.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/EosWalletInMapper.java new file mode 100644 index 0000000..1652f32 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/EosWalletInMapper.java @@ -0,0 +1,25 @@ +package com.blockchain.server.eos.mapper; + +import com.blockchain.server.eos.dto.WalletInDTO; +import com.blockchain.server.eos.entity.WalletIn; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/19 13:58 + * @user WIN10 + */ +@Repository +public interface EosWalletInMapper extends Mapper { + + /** + * 查询充值资金账户 + * @param to + * @return + */ + List listWalletInByAccountName(@Param("to") String to); +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/TokenMapper.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/TokenMapper.java new file mode 100644 index 0000000..a88d55a --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/TokenMapper.java @@ -0,0 +1,31 @@ +package com.blockchain.server.eos.mapper; + +import com.blockchain.server.eos.dto.TokenDTO; +import com.blockchain.server.eos.entity.Token; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.HashSet; + +/** + * @author Harvey + * @date 2019/2/18 11:13 + * @user WIN10 + */ +@Repository +public interface TokenMapper extends Mapper { + + /** + * 查询所有代币名称 + * @return + */ + HashSet listTokenNameAll(); + + /** + * 根据代币名称查询代币信息 + * @param tokenName + * @return + */ + TokenDTO selectEosTokenByTokenName(@Param("tokenName") String tokenName); +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/WalletInMapper.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/WalletInMapper.java new file mode 100644 index 0000000..d7e83f4 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/WalletInMapper.java @@ -0,0 +1,41 @@ +package com.blockchain.server.eos.mapper; + +import com.blockchain.server.eos.dto.WalletInDTO; +import com.blockchain.server.eos.entity.WalletIn; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.math.BigInteger; +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/18 16:50 + * @user WIN10 + */ +@Repository +public interface WalletInMapper extends Mapper { + + + // WalletInDTO selectWalletInAccount(String tokenName, Integer eosWalletInUsable); + + /** + * 查询对应的代币名称充值账户 + * @param tokenName + * @param status + * @return + */ + WalletInDTO selectWalletInAccount(@Param("tokenName") String tokenName, @Param("status") Integer status); + + List listWalletInByAccountName(@Param("to") String to); + + /** + * 修改区块字段 + * @param accountName + * @param blockNum + * @return + */ + Integer updateWalletInBlockNumber(@Param("accountName") String accountName, @Param("blockNum") BigInteger blockNum); + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/WalletMapper.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/WalletMapper.java new file mode 100644 index 0000000..2c9fe51 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/WalletMapper.java @@ -0,0 +1,83 @@ +package com.blockchain.server.eos.mapper; + +import com.blockchain.server.eos.dto.WalletDTO; +import com.blockchain.server.eos.entity.Wallet; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/18 16:50 + * @user WIN10 + */ +@Repository +public interface WalletMapper extends Mapper { + + /** + * 用户充值修改钱包金额 + * + * @param balance + * @param id + */ + int updateWalletBalanceByIdInRowLock(@Param("balance") BigDecimal balance, @Param("id") Integer id, @Param("modifyTime") Date modifyTime); + + /** + * 根据用户id查询用户钱包 + * + * @param walletType + * @param userOpenId + * @return + */ + List listWalletByWalletType(@Param("walletType") String walletType, @Param("userOpenId") String userOpenId); + + /** + * 通过用户id和币种名称和钱包标识查找用户 + * + * @param userOpenId + * @param tokenName + * @param walletType + * @return + */ + WalletDTO selectWallet(@Param("userOpenId") String userOpenId, @Param("tokenName") String tokenName, @Param("walletType") String walletType); + + /** + * 修改用户钱包 + * + * @param balance + * @param freeBalance + * @param freezeBalance + * @param userOpenId + * @param tokenName + * @param walletType + * @return + */ + Integer updateWalletAllBalanceInRowLock(@Param("balance") BigDecimal balance, + @Param("freeBalance") BigDecimal freeBalance, + @Param("freezeBalance") BigDecimal freezeBalance, + @Param("userOpenId") String userOpenId, + @Param("tokenName") String tokenName, + @Param("walletType") String walletType, + @Param("modifyTime") Date modifyTime); + + /** + * 通过用户id统计用户钱包 + * + * @param userId + * @return + */ + Integer selectCountWalletByUserOpenId(@Param("userId") String userId); + + /** + * 根据符号查询钱包 + * @param userId + * @param tokenSymbol + * @param walletType + * @return + */ + WalletDTO selectWalletByTokenSymbol(@Param("userId") String userId, @Param("tokenSymbol") String tokenSymbol, @Param("walletType") String walletType); +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/WalletTransferMapper.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/WalletTransferMapper.java new file mode 100644 index 0000000..0d32f34 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/mapper/WalletTransferMapper.java @@ -0,0 +1,35 @@ +package com.blockchain.server.eos.mapper; + +import com.blockchain.server.eos.dto.WalletTransferDTO; +import com.blockchain.server.eos.entity.WalletTransfer; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.HashSet; +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/18 11:13 + * @user WIN10 + */ +@Repository +public interface WalletTransferMapper extends Mapper { + + /** + * 查询钱包交易记录 + * @param walletId + * @param tokenName + * @return + */ + List listWalletTransfer(@Param("walletId") Integer walletId, @Param("tokenName") String tokenName); + + /** + * 根据hash查询是否存在数据 + * @param hash + * @param transferType + * @return + */ + Integer countByHashAndTransferType(@Param("hash") String hash, @Param("transferType") String transferType); +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/scheduleTask/TransferDisposeTimerTask.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/scheduleTask/TransferDisposeTimerTask.java new file mode 100644 index 0000000..2af54de --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/scheduleTask/TransferDisposeTimerTask.java @@ -0,0 +1,54 @@ +package com.blockchain.server.eos.scheduleTask; + +import com.blockchain.server.eos.service.EosTokenService; +import com.blockchain.server.eos.service.EosWalletInService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +//@Component +public class TransferDisposeTimerTask { + @Autowired + private EosTokenService tokenService; + @Autowired + private EosWalletInService eosWalletInService; + + private static ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); + + // 日志 + private static final Logger LOG = LoggerFactory.getLogger(TransferDisposeTimerTask.class); + + /** + * 查eos区块信息 + */ + @Scheduled(cron = "0/10 * * * * ?") //5秒钟执行一次 */5 * * * * ? 5分钟执行一次 0 */5 * * * ? + public void selectTxStatus() { + LOG.info("************************eos交易记录爬取的定时任务************************"); + singleThreadExecutor.execute(new Runnable() { + @Override + public void run() { + dispose(); //查询eos账户历史充值信息 + } + }); + } + + public void dispose() { + // Token查询所有代币 + HashSet listTokenName = tokenService.listTokenNameAll(); + try { + // 充值处理方法 + eosWalletInService.handleAccountBlockTransactions(listTokenName); + } catch (Exception e) { + e.printStackTrace(); + LOG.error("************************ 充值处理异常 ************************"); + } + + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/scheduleTask/WallertTimerTask.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/scheduleTask/WallertTimerTask.java new file mode 100644 index 0000000..4310125 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/scheduleTask/WallertTimerTask.java @@ -0,0 +1,64 @@ +package com.blockchain.server.eos.scheduleTask; + +import com.blockchain.server.eos.common.constant.EosConstant; +import com.blockchain.server.eos.entity.WalletTransfer; +import com.blockchain.server.eos.eos4j.Rpc; +import com.blockchain.server.eos.service.EosWalletService; +import com.blockchain.server.eos.service.EosWalletTransferService; +import com.blockchain.server.eos.service.EosWalletUtilService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * 钱包业务处理的定时器 + */ +//@Component +public class WallertTimerTask { + + // 钱包提现处理 + private static ExecutorService txOutExecutorService = Executors.newSingleThreadExecutor(); + // 日志 + private static final Logger LOG = LoggerFactory.getLogger(WallertTimerTask.class); + + @Autowired + EosWalletTransferService walletTransferService; + @Autowired + EosWalletService walletService; + + @Scheduled(cron = "0/5 * * * * ?") + public void crawlTxOut() { + txOutExecutorService.execute(new Runnable() { + @Override + public void run() { + crawlTxOutDispose(); // 爬取提现记录 + } + }); + } + + void crawlTxOutDispose() { + // 查询提现的未处理的数据 + List txs = walletTransferService.selectByTxTypeAndStatus(EosConstant.TransferType.TRANSFER_OUT, EosConstant.TransferStatus.YI_CHU_BI, 0, 99); + for (WalletTransfer tx : txs) { + try { + Boolean status = walletService.getTransaction(tx.getBlockNumber(), tx.getHash()); + // 修改失败状态 + if (!status) walletService.updateTxOutError(tx); + // 修改余额改变成功状态 + else walletService.updateTxOutSuccess(tx); + } catch (Exception e) { + e.printStackTrace(); + continue; + } + } + + } + + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/ConfigWalletParamService.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/ConfigWalletParamService.java new file mode 100644 index 0000000..83dd9fc --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/ConfigWalletParamService.java @@ -0,0 +1,26 @@ +package com.blockchain.server.eos.service; + +import com.blockchain.common.base.dto.GasDTO; +import com.blockchain.server.eos.entity.ConfigWalletParam; + +/** + * @author Harvey + * @date 2019/2/20 10:46 + * @user WIN10 + */ +public interface ConfigWalletParamService { + + /** + * 查询单个手续费 + * @param tokenType + * @return + */ + ConfigWalletParam selectOne(String tokenType); + + /** + * 返回GasDTO + * @param configWalletParam + * @return + */ + GasDTO selectConfigWalletParam(ConfigWalletParam configWalletParam); +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosApplicationService.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosApplicationService.java new file mode 100644 index 0000000..5b7f5ae --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosApplicationService.java @@ -0,0 +1,26 @@ +package com.blockchain.server.eos.service; + +import com.blockchain.server.eos.entity.Application; + +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/26 18:16 + * @user WIN10 + */ +public interface EosApplicationService { + + /** + * 查询是否存在该应用钱包 + * @param walletType + * @return + */ + Integer selectWalletCountByWalletType(String walletType); + + /** + * 查找所有应用信息 + * @return + */ + List listEosApplication(); +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosBlockNumberService.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosBlockNumberService.java new file mode 100644 index 0000000..2296237 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosBlockNumberService.java @@ -0,0 +1,59 @@ +package com.blockchain.server.eos.service; + +import com.blockchain.server.eos.dto.BlockNumberDTO; +import com.blockchain.server.eos.entity.BlockNumber; + +import java.math.BigInteger; +import java.util.HashSet; +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/16 17:45 + * @user WIN10 + */ +public interface EosBlockNumberService { + + /** + * 查询最大区块 + * @return + */ + BigInteger selectCurrentBlockNum(); + + /** + * 添加区块交易信息 + * @param nowBlock + * @param listTokenAddr + */ + void handleBlockTransactions(BigInteger nowBlock, HashSet listTokenAddr); + + /** + * 添加 + * @param blockNumber + * @return + */ + int insertBlockNumber(BlockNumber blockNumber); + + /** + * 插入区块处理状态 + * @param nowBlock + * @param status + * @return + */ + int insertBlockNumberByStatus(BigInteger nowBlock, Character status); + + /** + * 根据区块状态获取区块 + * @param eosBlockNumberWait + * @return + */ + List listBlockNumberByStatus(Character eosBlockNumberWait); + + /** + * 更新处理区块状态 + * @param blockNum + * @param status + * @return + */ + int updateBlockNumberByStatus(BigInteger blockNum, Character status); +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosTokenService.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosTokenService.java new file mode 100644 index 0000000..f8cf64c --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosTokenService.java @@ -0,0 +1,42 @@ +package com.blockchain.server.eos.service; + +import com.blockchain.server.eos.dto.TokenDTO; +import com.blockchain.server.eos.entity.Token; + +import java.util.HashSet; +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/25 17:08 + * @user WIN10 + */ +public interface EosTokenService { + + /** + * 根据币种名称查询币种信息 + * @param tokenName + * @return + */ + TokenDTO selectEosTokenByTokenName(String tokenName); + + /** + * 查询所有代币地址 + * @return + */ + HashSet listTokenNameAll(); + + /** + * 查找eos所有币种信息 + * @return + */ + List listEosToken(); + + /** + * 根据代币名称查询 + * + * @param tokenSymbol + * @return + */ + Token selectByTokenSymbol(String tokenSymbol); +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletInService.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletInService.java new file mode 100644 index 0000000..b43217c --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletInService.java @@ -0,0 +1,35 @@ +package com.blockchain.server.eos.service; + +import com.blockchain.server.eos.dto.WalletInDTO; + +import java.util.HashSet; +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/18 16:39 + * @user WIN10 + */ +public interface EosWalletInService { + + /** + * 查询对应的代币名称充值账户 + * @param tokenName + * @return + */ + WalletInDTO selectWalletInAccount(String tokenName); + + /** + * 查询充值资金账户 + * @param to + * @return + */ + List listWalletInByAccountName(String to); + + /** + * 充值方法 + * @param listTokenName + */ + void handleAccountBlockTransactions(HashSet listTokenName); + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletService.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletService.java new file mode 100644 index 0000000..dff59b0 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletService.java @@ -0,0 +1,153 @@ +package com.blockchain.server.eos.service; + +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import com.blockchain.server.eos.dto.WalletDTO; +import com.blockchain.server.eos.entity.Wallet; +import com.blockchain.server.eos.entity.WalletTransfer; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/18 16:39 + * @user WIN10 + */ +public interface EosWalletService { + + /** + * 通过 + * + * @param walletId + * @return + */ + Wallet selectWalletById(String walletId); + + Wallet selectWalletByIdAndTokenName(Integer walletId,String tokenName); + + /** + * 用户充值修改钱包金额 + * + * @param balance + * @param id + */ + int updateWalletBalanceByIdInRowLock(BigDecimal balance, Integer id, Date modifyTime); + + /** + * 用户提现 + * + * @param password + * @param account + * @param amount + * @param tokenName + * @param walletType + * @return + */ + int handleWithdrawDeposit(String userOpenId, String password, String account, BigDecimal amount, String tokenName, String walletType); + + /** + * 币币交易金额记录 + * @param userOpenId + * @param tokenName + * @param amount + * @return + */ + /*int handleTransferCCT(String userOpenId, String tokenName, BigDecimal amount);*/ + + /** + * 查询钱包 + * + * @param walletType + * @param userOpenId + * @return + */ + List listWalletByWalletType(String walletType, String userOpenId); + + /** + * 冻结与解冻方法 + * + * @param walletOrderDTO + * @return + */ + Integer updateWalletOrder(WalletOrderDTO walletOrderDTO); + + /** + * 通过用户id和币种名称和钱包标识查找用户 + * + * @param userOpenId + * @param tokenName + * @param walletType + * @return + */ + WalletDTO selectWallet(String userOpenId, String tokenName, String walletType); + + /** + * 钱包余额变动方法 + * + * @param walletChangeDTO + */ + Integer handleWalletChange(WalletChangeDTO walletChangeDTO); + + /** + * 初始化钱包方法 + * + * @param userId + * @return + */ + Integer initEosWallet(String userId); + + /** + * 根据符号查询钱包 + * @param userId + * @param tokenSymbol + * @param walletType + * @return + */ + WalletDTO selectWalletByTokenSymbol(String userId, String tokenSymbol, String walletType); + + /** + * 查询eos出块 + * @param bolckNum + * @param hashId + * @return + */ + Boolean getTransaction(String bolckNum, String hashId); + + /** + * 提现失败处理 + * + * @param tx + */ + void updateTxOutError(WalletTransfer tx); + + /** + * 提现成功处理 + * @param tx + */ + void updateTxOutSuccess(WalletTransfer tx); + + + /** + * 根据用户ID,钱包类型,币种名称查询钱包信息 + * + * @param userId + * @param walletType + * @param coinName + * @return + */ + Wallet findWallet(String userId, String walletType, String coinName); + + /** + * 划转 + * + * @param userOpenId + * @param fromType + * @param toType + * @param coinName + * @param amount + * @return + */ + WalletTransfer handleTransfer(String userOpenId, String fromType, String toType, String coinName, BigDecimal amount); +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletTransferService.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletTransferService.java new file mode 100644 index 0000000..7f1eef7 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletTransferService.java @@ -0,0 +1,80 @@ +package com.blockchain.server.eos.service; + +import com.blockchain.server.eos.dto.WalletTransferDTO; +import com.blockchain.server.eos.entity.WalletTransfer; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/19 11:39 + * @user WIN10 + */ +public interface EosWalletTransferService { + + /** + * 添加记录并修改账户金额 + * @param hash + * @param transferType + * @param amount + * @param tokenName + * @param tokenSymbol + * @param memo + * @param accountId + * @return + */ + int handleWalletAndWalletTransfer(String hash, Integer accountId, String accountName, String transferType, BigDecimal amount, String tokenName, String tokenSymbol, String memo, BigInteger blockNumber); + + /** + * 插入记录 + * @param walletTransfer + * @return + */ + int insertWalletTransfer(WalletTransfer walletTransfer); + + /** + * 查询钱包交易记录 + * @param userOpenId + * @param tokenName + * @param walletType + * @return + */ + List listWalletTransfer(String userOpenId, String tokenName, String walletType); + + /** + * 根据记录类型与记录状态查询记录 + * + * @param transferType 记录类型 + * @param Status 记录状态 + * @param pageNum 当前页数 + * @param pageSize 页数展示条数 + * @return + */ + List selectByTxTypeAndStatus(String transferType, int Status, Integer pageNum, Integer pageSize); + + /** + * 更新状态 + * @param id + * @param status + * @param date + */ + void updateStatus(String id, int status, Date date); + + /** + * 根据hash查询交易记录 + * @param hashId + * @return + */ + WalletTransfer selectByHashId(String hashId); + + /** + * 根据hash查询是否存在数据 + * @param hash + * @param transferType + * @return + */ + Integer countByHashAndTransferType(String hash, String transferType); +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletUtilService.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletUtilService.java new file mode 100644 index 0000000..ef27918 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/EosWalletUtilService.java @@ -0,0 +1,48 @@ +package com.blockchain.server.eos.service; + +import com.alibaba.fastjson.JSONObject; +import com.blockchain.server.eos.dto.BlockNumberDTO; + +import java.math.BigInteger; + +/** + * @author Harvey + * @date 2019/2/18 13:57 + * @user WIN10 + */ +public interface EosWalletUtilService { + + /** + * 获取最新区块 + * @return + */ + BigInteger getEosBlock(); + + /** + * 获取区块交易记录 + * @param nowBlock + * @return + */ + JSONObject listBlockTransaction(BigInteger nowBlock); + + /** + * 获取区块高度 + * @return + */ + BlockNumberDTO getBlockNum(); + + /** + * 获取出币信息 + * @param blockNum + * @param hashId + * @return + */ + JSONObject getTransaction(String blockNum, String hashId); + + /** + * 获取账户历史交易信息 + * @param accountName + * @return + */ + JSONObject getActions(String accountName, String offset); +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/ConfigWalletParamServiceImpl.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/ConfigWalletParamServiceImpl.java new file mode 100644 index 0000000..9e640cd --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/ConfigWalletParamServiceImpl.java @@ -0,0 +1,52 @@ +package com.blockchain.server.eos.service.impl; + +import com.blockchain.common.base.dto.GasDTO; +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.common.base.util.JsonUtils; +import com.blockchain.server.eos.common.constant.EosConstant; +import com.blockchain.server.eos.entity.ConfigWalletParam; +import com.blockchain.server.eos.mapper.ConfigWalletParamMapper; +import com.blockchain.server.eos.service.ConfigWalletParamService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Harvey + * @date 2019/2/20 10:47 + * @user WIN10 + */ +@Service +public class ConfigWalletParamServiceImpl implements ConfigWalletParamService { + + @Autowired + private ConfigWalletParamMapper configWalletParamMapper; + + /** + * 查询单个手续费 + * @param tokenType + * @return + */ + @Override + public ConfigWalletParam selectOne(String tokenType) { + // 查询单个手续费配置信息 + ConfigWalletParam configWalletParam = new ConfigWalletParam(); + configWalletParam.setModuleType(EosConstant.EOS_TOKEN_TYPE); + configWalletParam.setParamName(EosConstant.EosGasConfig.EOS_GAS_PRIFIX + tokenType); + configWalletParam.setStatus(EosConstant.EosGasConfig.EOS_GAS_STATUS_USABLE); + return configWalletParamMapper.selectOne(configWalletParam); + } + + @Override + public GasDTO selectConfigWalletParam(ConfigWalletParam configWalletParam) { + ExceptionPreconditionUtils.notEmpty(configWalletParam); + ExceptionPreconditionUtils.notEmpty(configWalletParam.getParamValue()); + try { + return JsonUtils.jsonToPojo(configWalletParam.getParamValue(), GasDTO.class); + } catch (Exception e) { + return null; + } + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosApplicationServiceImpl.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosApplicationServiceImpl.java new file mode 100644 index 0000000..4ee8b65 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosApplicationServiceImpl.java @@ -0,0 +1,42 @@ +package com.blockchain.server.eos.service.impl; + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.eos.entity.Application; +import com.blockchain.server.eos.mapper.ApplicationMapper; +import com.blockchain.server.eos.service.EosApplicationService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/26 18:22 + * @user WIN10 + */ +@Service +public class EosApplicationServiceImpl implements EosApplicationService { + + @Autowired + private ApplicationMapper applicationMapper; + + /** + * 查询是否存在该应用钱包 + * @param walletType + * @return + */ + @Override + public Integer selectWalletCountByWalletType(String walletType) { + ExceptionPreconditionUtils.notEmpty(walletType); + return applicationMapper.selectWalletCountByWalletType(walletType); + } + + /** + * 查找所有应用信息 + * @return + */ + @Override + public List listEosApplication() { + return applicationMapper.selectAll(); + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosBlockNumberServiceImpl.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosBlockNumberServiceImpl.java new file mode 100644 index 0000000..a081c71 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosBlockNumberServiceImpl.java @@ -0,0 +1,201 @@ +package com.blockchain.server.eos.service.impl; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.eos.common.constant.EosConstant; +import com.blockchain.server.eos.common.util.RedisUtil; +import com.blockchain.server.eos.dto.WalletInDTO; +import com.blockchain.server.eos.entity.BlockNumber; +import com.blockchain.server.eos.entity.Wallet; +import com.blockchain.server.eos.mapper.BlockNumberMapper; +import com.blockchain.server.eos.mapper.WalletInMapper; +import com.blockchain.server.eos.scheduleTask.TransferDisposeTimerTask; +import com.blockchain.server.eos.service.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; +import java.util.HashSet; +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/16 17:49 + * @user WIN10 + */ +@Service +public class EosBlockNumberServiceImpl implements EosBlockNumberService { + + @Autowired + private BlockNumberMapper blockNumberMapper; + @Autowired + private EosWalletUtilService eosWalletUtilService; + @Autowired + private EosWalletService eosWalletService; + @Autowired + private EosWalletTransferService eosWalletTransferService; + @Autowired + private EosWalletInService eosWalletInService; + @Autowired + private RedisTemplate redisTemplate; + + // 日志 + private static final Logger LOG = LoggerFactory.getLogger(TransferDisposeTimerTask.class); + + /** + * 查询最大区块 + * + * @return + */ + @Override + public BigInteger selectCurrentBlockNum() { + BigInteger current = RedisUtil.getCurrentBlock(redisTemplate); + if (current == null) current = blockNumberMapper.selectMaxBlockNum(); + return current; + } + + /** + * 添加区块交易信息 + * + * @param nowBlock 区块号 + * @param listTokenAddr 所有系统代币地址 + */ + @Override + @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED) + public void handleBlockTransactions(BigInteger nowBlock, HashSet listTokenAddr) { + ExceptionPreconditionUtils.notEmpty(nowBlock); + ExceptionPreconditionUtils.notEmpty(listTokenAddr); + if (EosConstant.BlockNumber.EOS_BLOCK_NUMBER_WAIT.equals(blockNumberMapper.selectBlockNumStatusByBlockNum(nowBlock))) { + // 查询eos此区块所有交易信息 + JSONObject resultData = eosWalletUtilService.listBlockTransaction(nowBlock); + if (resultData != null) { + JSONArray transactions = resultData.getJSONArray("transactions"); + if (transactions.size() > 0) { + for (int i = 0; i < transactions.size(); i++) { + // 遍历 jsonarray 数组,把每一个对象转成 json 对象 + JSONObject job = transactions.getJSONObject(i); + + JSONObject trx = null; + try { + trx = job.getJSONObject("trx"); + } catch (Exception e) { + continue; + } + String hash = trx.getObject("id", String.class); + JSONObject transaction = trx.getJSONObject("transaction"); + if (transaction.size() > 0) { + JSONArray actions = transaction.getJSONArray("actions"); + JSONObject action = actions.getJSONObject(0); + // System.out.println("action: " + action); + JSONObject data = null; + try { + data = action.getJSONObject("data"); + } catch (Exception e) { + continue; + } + + if (null != data && !"".equals(data)) { + // 解析收款地址是否平台收款地址 + String to = data.getObject("to", String.class); + if(to == null) continue; + List walletInDTOS = eosWalletInService.listWalletInByAccountName(to); + if (walletInDTOS != null && walletInDTOS.size() > 0) { + // 解析区块信息,获取充值记录 + String from = data.getObject("from", String.class); + String tokenName = action.getObject("account", String.class); + // String hexData = action.getObject("hex_data", String.class); + String quantity = data.getObject("quantity", String.class); + String[] quantitys = null; + if (null != quantity && quantity.length() > 0) quantitys = quantity.split(" "); + // 备注格式为备注 钱包地址 + String memo = data.getObject("memo", String.class).trim(); + + for (WalletInDTO walletInDTO : walletInDTOS) { + // 判断代币地址 + if (tokenName.equals(walletInDTO.getTokenName())) { + Wallet wallet = eosWalletService.selectWalletById(memo); + if (null != wallet) { + // 判断token_name 和 token_symbol + if (wallet.getTokenName().equals(tokenName) && wallet.getTokenSymbol().equals(quantitys[1])) { + // 添加充值记录 + eosWalletTransferService.handleWalletAndWalletTransfer(hash, + wallet.getId(), + null, + EosConstant.TransferType.TRANSFER_IN, + new BigDecimal(quantitys[0]), + tokenName, + quantitys[1], + memo, + nowBlock); + LOG.info("************************充值成功:" + hash + " + " + wallet.getId() + " + " + quantity + " + " + memo + "************************"); + } + } + } + } + + } + } + } + } + } + } + } + } + + @Override + @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED) + public int insertBlockNumber(BlockNumber blockNumber) { + // System.out.println("添加区块高度"); + return blockNumberMapper.insertSelectiveIgnore(blockNumber); + } + + /** + * 插入区块处理状态 + * + * @param nowBlock + * @param status + * @return + */ + @Override + @Transactional + public int insertBlockNumberByStatus(BigInteger nowBlock, Character status) { + BlockNumber blockNumber = new BlockNumber(); + blockNumber.setBlockNumber(nowBlock); + blockNumber.setStatus(status); + blockNumber.setCreateTime(new Date()); + return this.insertBlockNumber(blockNumber); + } + + /** + * 根据区块状态获取区块 + * + * @param status + * @return + */ + @Override + public List listBlockNumberByStatus(Character status) { + return blockNumberMapper.listBlockNumberByStatus(status); + } + + /** + * 更新处理区块状态 + * + * @param blockNum + * @param status + * @return + */ + @Override + @Transactional + public int updateBlockNumberByStatus(BigInteger blockNum, Character status) { + return blockNumberMapper.updateBlockNumberByStatus(blockNum, status); + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosTokenServiceImpl.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosTokenServiceImpl.java new file mode 100644 index 0000000..e56758e --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosTokenServiceImpl.java @@ -0,0 +1,64 @@ +package com.blockchain.server.eos.service.impl; + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.eos.dto.TokenDTO; +import com.blockchain.server.eos.entity.Token; +import com.blockchain.server.eos.mapper.TokenMapper; +import com.blockchain.server.eos.service.EosTokenService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashSet; +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/25 17:09 + * @user WIN10 + */ +@Service +public class EosTokenServiceImpl implements EosTokenService { + + @Autowired + private TokenMapper tokenMapper; + + /** + * 根据币种名称查询币种信息 + * @param tokenName + * @return + */ + @Override + public TokenDTO selectEosTokenByTokenName(String tokenName) { + ExceptionPreconditionUtils.notEmpty(tokenName); + return tokenMapper.selectEosTokenByTokenName(tokenName); + } + + /** + * 查询所有代币名称 + * @return + */ + @Override + public HashSet listTokenNameAll() { + return tokenMapper.listTokenNameAll(); + } + + /** + * 查找所有币种信息 + * @return + */ + @Override + public List listEosToken() { + return tokenMapper.selectAll(); + } + + + @Override + public Token selectByTokenSymbol(String tokenSymbol) { + ExceptionPreconditionUtils.notEmpty(tokenSymbol); + Token where = new Token(); + where.setTokenSymbol(tokenSymbol); + Token token = tokenMapper.selectOne(where); + ExceptionPreconditionUtils.notEmpty(token); + return token; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletInServiceImpl.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletInServiceImpl.java new file mode 100644 index 0000000..0788abc --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletInServiceImpl.java @@ -0,0 +1,89 @@ +package com.blockchain.server.eos.service.impl; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.eos.common.constant.EosConstant; +import com.blockchain.server.eos.common.util.EosUtil; +import com.blockchain.server.eos.dto.WalletInDTO; +import com.blockchain.server.eos.mapper.WalletInMapper; +import com.blockchain.server.eos.service.EosWalletInService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigInteger; +import java.util.HashSet; +import java.util.List; + +/** + * @author Harvey + * @date 2019/2/25 10:11 + * @user WIN10 + */ +@Service +public class EosWalletInServiceImpl implements EosWalletInService { + + @Autowired + private WalletInMapper walletInMapper; + @Autowired + private EosUtil eosUtil; + + /** + * 查询对应的代币名称充值账户 + * @param tokenName + * @return + */ + @Override + public WalletInDTO selectWalletInAccount(String tokenName) { + ExceptionPreconditionUtils.notEmpty(tokenName); + return walletInMapper.selectWalletInAccount(tokenName, EosConstant.WalletInStatus.EOS_WALLET_IN_USABLE); + } + + /** + * 查询充值资金账户 + * @param to + * @return + */ + @Override + public List listWalletInByAccountName(String to) { + ExceptionPreconditionUtils.notEmpty(to); + return walletInMapper.listWalletInByAccountName(to); + } + + /** + * 充值方法 + * + * @param listTokenName + */ + @Override + @Transactional + public void handleAccountBlockTransactions(HashSet listTokenName) { + for (String tokenName : listTokenName) { + WalletInDTO walletInDTO = this.selectWalletInAccount(tokenName); + + JSONArray actions = eosUtil.recursion(walletInDTO, EosConstant.RPCConstant.OFFSET); + if (actions == null) return; + Integer row = updateWalletInBlockNumber(walletInDTO, actions); + if (row != 1) return; + + eosUtil.handTransaction(actions, tokenName, walletInDTO); + } + } + + /** + * 修改资金账户区块字段 + * + * @param walletInDTO + * @param actions + * @return + */ + private Integer updateWalletInBlockNumber(WalletInDTO walletInDTO, JSONArray actions) { + JSONObject job = actions.getJSONObject(actions.size() - 1); + JSONObject action_trace = job.getJSONObject("action_trace"); + BigInteger blockNum = action_trace.getBigInteger("block_num"); + return walletInMapper.updateWalletInBlockNumber(walletInDTO.getAccountName(), blockNum); + } + + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletServiceImpl.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletServiceImpl.java new file mode 100644 index 0000000..96b2947 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletServiceImpl.java @@ -0,0 +1,433 @@ +package com.blockchain.server.eos.service.impl; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.dto.GasDTO; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import com.blockchain.common.base.exception.RPCException; +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.eos.common.constant.EosConstant; +import com.blockchain.server.eos.common.enums.EosWalletEnums; +import com.blockchain.server.eos.common.exception.EosWalletException; +import com.blockchain.server.eos.common.util.EosUtil; +import com.blockchain.server.eos.dto.WalletDTO; +import com.blockchain.server.eos.entity.Application; +import com.blockchain.server.eos.entity.Token; +import com.blockchain.server.eos.entity.Wallet; +import com.blockchain.server.eos.entity.WalletTransfer; +import com.blockchain.server.eos.feign.UserServerFegin; +import com.blockchain.server.eos.feign.WalletTransferFegin; +import com.blockchain.server.eos.mapper.WalletMapper; +import com.blockchain.server.eos.service.*; +import com.codingapi.tx.annotation.ITxTransaction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +/** + * @author Harvey + * @date 2019/2/18 16:39 + * @user WIN10 + */ +@Service +public class EosWalletServiceImpl implements EosWalletService, ITxTransaction { + + @Autowired + private WalletMapper walletMapper; + @Autowired + private ConfigWalletParamService configWalletParamService; + @Autowired + private EosWalletTransferService eosWalletTransferService; + @Autowired + private WalletTransferFegin walletTransferFegin; + @Autowired + private EosApplicationService eosApplicationService; + @Autowired + private EosTokenService eosTokenService; + @Autowired + private UserServerFegin userServerFegin; + @Autowired + private EosUtil eosUtil; + + /** + * 通过id查询钱包 + * + * @param walletId + * @return + */ + @Override + public Wallet selectWalletById(String walletId) { + return walletMapper.selectByPrimaryKey(walletId); + } + + @Override + public Wallet selectWalletByIdAndTokenName(Integer walletId, String tokenName) { + // 数据验证 + Wallet where = new Wallet(); + where.setId(walletId); + where.setTokenName(tokenName); + Wallet wallet = walletMapper.selectOne(where); + if (wallet == null) throw new EosWalletException(EosWalletEnums.EOSWALLET_GETWALLET_ERROR); + return wallet; + } + + /** + * 用户充值修改钱包金额 + * + * @param balance + * @param id + */ + @Override + @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED) + public int updateWalletBalanceByIdInRowLock(BigDecimal balance, Integer id, Date modifyTime) { + return walletMapper.updateWalletBalanceByIdInRowLock(balance, id, modifyTime); + } + + /** + * 用户提现 + * + * @param password + * @param account + * @param amount + * @param tokenName + * @param walletType + * @return + */ + @Override + @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED) + public int handleWithdrawDeposit(String userOpenId, String password, String account, BigDecimal amount, String tokenName, String walletType) { + ExceptionPreconditionUtils.checkStringNotBlank(userOpenId, new EosWalletException(EosWalletEnums.PARAM_ERROR)); + ExceptionPreconditionUtils.checkStringNotBlank(account, new EosWalletException(EosWalletEnums.PARAM_ERROR)); + ExceptionPreconditionUtils.checkStringNotBlank(tokenName, new EosWalletException(EosWalletEnums.PARAM_ERROR)); + if (amount == null) throw new EosWalletException(EosWalletEnums.PARAM_ERROR); + // 查询是否存在提现黑名单中 + // 抛出错误表示用户禁止提现 + userServerFegin.verifyBanWithdraw(userOpenId); + WalletDTO wallet = this.selectWallet(userOpenId, tokenName, walletType); + if (amount.compareTo(wallet.getFreeBalance()) > 0) + throw new EosWalletException(EosWalletEnums.BALANCE_AMOUNT_ERROR); + ResultDTO resultDTO = walletTransferFegin.isPassword(password); + if (resultDTO.getCode() != BaseConstant.REQUEST_SUCCESS) throw new RPCException(resultDTO); + + // 扣除手续费,可用余额转入冻结余额 + // 查询config_wallet_para手续费配置表 + GasDTO gasDTO = configWalletParamService.selectConfigWalletParam(configWalletParamService.selectOne(wallet.getTokenSymbol().toLowerCase())); + // 与最小提现数额做对比 + if (gasDTO.getMinWdAmount().compareTo(amount) > 0) throw new EosWalletException(EosWalletEnums.ERROR); + // 判断用户是否存在提现白名单中 + ResultDTO freeWithdraw = userServerFegin.verifyFreeWithdraw(userOpenId); + if (freeWithdraw.getCode() != BaseConstant.REQUEST_SUCCESS) + throw new RPCException(freeWithdraw.getCode(), freeWithdraw.getMsg()); + // 用户存在提现白名单中,设置提现手续费为零 + if (freeWithdraw.getData()) gasDTO.setGasPrice(BigDecimal.ZERO); + Date now = new Date(); + int updateRow = walletMapper.updateWalletAllBalanceInRowLock( + BigDecimal.ZERO, + amount.multiply(new BigDecimal(-1)), + amount, + userOpenId, + tokenName, + walletType, + now); + if (updateRow == 0) throw new EosWalletException(EosWalletEnums.WITHDRAW_ERROR); + // 添加待审核记录 + WalletTransfer walletTransfer = new WalletTransfer(); + walletTransfer.setId(UUID.randomUUID().toString()); + walletTransfer.setFromId(wallet.getId()); + walletTransfer.setAccountName(account); + walletTransfer.setAmount(amount); + walletTransfer.setTokenName(tokenName); + walletTransfer.setTokenSymbol(wallet.getTokenSymbol()); + walletTransfer.setGasPrice(gasDTO.getGasPrice()); + walletTransfer.setGasTokenName(gasDTO.getGasTokenName()); + walletTransfer.setGasTokenType(gasDTO.getGasTokenType()); + walletTransfer.setGasTokenSymbol(gasDTO.getGasTokenSymbol()); + walletTransfer.setTransferType(EosConstant.TransferType.TRANSFER_OUT); + walletTransfer.setStatus(EosConstant.TransferStatus.FIRST_TRIAL); + walletTransfer.setTimestamp(now); + return eosWalletTransferService.insertWalletTransfer(walletTransfer); + } + + + /** + * 查询钱包 + * + * @param walletType + * @param userOpenId + * @return + */ + @Override + public List listWalletByWalletType(String walletType, String userOpenId) { + ExceptionPreconditionUtils.notEmpty(userOpenId); + return walletMapper.listWalletByWalletType(walletType, userOpenId); + } + + /** + * 冻结与解冻方法 + * + * @param walletOrderDTO + * @return + */ + @Override + @Transactional + public Integer updateWalletOrder(WalletOrderDTO walletOrderDTO) { + ExceptionPreconditionUtils.notEmpty(walletOrderDTO); + BigDecimal freeBalance = walletOrderDTO.getFreeBalance(); + BigDecimal freezeBalance = walletOrderDTO.getFreezeBalance(); + ExceptionPreconditionUtils.notEmpty(freeBalance); + ExceptionPreconditionUtils.notEmpty(freezeBalance); + // 判断传过来的可用余额和冻结余额是否有误 + if (freeBalance.add(freezeBalance).compareTo(BigDecimal.ZERO) != 0) + throw new EosWalletException(EosWalletEnums.PARAM_ERROR); + // 判断是否存在该钱包 + WalletDTO walletDTO = this.selectWalletByTokenSymbol(walletOrderDTO.getUserId(), walletOrderDTO.getTokenName(), walletOrderDTO.getWalletType()); + // 对比可用余额和冻结余额 + if (freeBalance.add(walletDTO.getFreeBalance()).compareTo(BigDecimal.ZERO) < 0) + throw new EosWalletException(EosWalletEnums.BALANCE_AMOUNT_ERROR); + if (freezeBalance.add(walletDTO.getFreezeBalance()).compareTo(BigDecimal.ZERO) < 0) + throw new EosWalletException(EosWalletEnums.BALANCE_AMOUNT_ERROR); + return walletMapper.updateWalletAllBalanceInRowLock(BigDecimal.ZERO, freeBalance, freezeBalance, walletOrderDTO.getUserId(), walletDTO.getTokenName(), walletOrderDTO.getWalletType(), new Date()); + } + + /** + * 通过用户id和币种名称和钱包标识查找用户 + * + * @param userOpenId + * @param tokenName + * @return + */ + @Override + public WalletDTO selectWallet(String userOpenId, String tokenName, String walletType) { + ExceptionPreconditionUtils.notEmpty(walletType); + Integer count = eosApplicationService.selectWalletCountByWalletType(walletType); + if (count < 1) throw new EosWalletException(EosWalletEnums.EOSWALLET_GETWALLET_ERROR); + WalletDTO walletDTO = walletMapper.selectWallet(userOpenId, tokenName, walletType); + if (walletDTO == null) throw new EosWalletException(EosWalletEnums.EOSWALLET_GETWALLET_ERROR); + return walletDTO; + } + + /** + * 钱包余额变动方法 + * + * @param walletChangeDTO + * @return + */ + @Override + @Transactional + public Integer handleWalletChange(WalletChangeDTO walletChangeDTO) { + // 判断参数是否为空 + ExceptionPreconditionUtils.notEmpty(walletChangeDTO); + BigDecimal freeBalance = walletChangeDTO.getFreeBalance(); + BigDecimal freezeBalance = walletChangeDTO.getFreezeBalance(); + BigDecimal gasBalance = walletChangeDTO.getGasBalance(); + ExceptionPreconditionUtils.notEmpty(freeBalance); + ExceptionPreconditionUtils.notEmpty(freezeBalance); + ExceptionPreconditionUtils.notEmpty(gasBalance); + // 判断是否存在该钱包 + WalletDTO walletDTO = this.selectWalletByTokenSymbol(walletChangeDTO.getUserId(), walletChangeDTO.getTokenName(), walletChangeDTO.getWalletType()); + BigDecimal dtoBalance = walletDTO.getBalance(); + BigDecimal dtoFreeBalance = walletDTO.getFreeBalance(); + BigDecimal dtoFreezeBalance = walletDTO.getFreezeBalance(); + // 判断余额是否充足 + if (dtoFreezeBalance.add(freezeBalance).compareTo(BigDecimal.ZERO) < 0) + throw new EosWalletException(EosWalletEnums.BALANCE_AMOUNT_ERROR); + if (dtoFreeBalance.add(freeBalance).compareTo(BigDecimal.ZERO) < 0) + throw new EosWalletException(EosWalletEnums.BALANCE_AMOUNT_ERROR); +// if (dtoBalance.compareTo(dtoFreeBalance.add(dtoFreezeBalance).add(freeBalance.add(freezeBalance))) < 0) +// throw new EosWalletException(EosWalletEnums.BALANCE_AMOUNT_ERROR); + //记录总额改动 + BigDecimal balance = BigDecimal.ZERO; + Date now = new Date(); + // 添加记录 + WalletTransfer walletTransfer = new WalletTransfer(); + walletTransfer.setId(UUID.randomUUID().toString()); + walletTransfer.setHash(walletChangeDTO.getRecordId()); + balance = freeBalance.add(freezeBalance); + // 可用余额增加 + if (balance.compareTo(BigDecimal.ZERO) > 0) walletTransfer.setToId(walletDTO.getId()); + else walletTransfer.setFromId(walletDTO.getId()); + walletTransfer.setAmount(balance); + walletTransfer.setTokenName(walletDTO.getTokenName()); + walletTransfer.setTokenSymbol(walletDTO.getTokenSymbol()); + walletTransfer.setGasPrice(gasBalance.abs()); + walletTransfer.setGasTokenName(walletDTO.getTokenSymbol()); + walletTransfer.setGasTokenSymbol(walletDTO.getTokenSymbol()); + walletTransfer.setGasTokenType(walletDTO.getTokenName()); + walletTransfer.setTransferType(walletDTO.getWalletType()); + walletTransfer.setStatus(EosConstant.TransferStatus.SUCCESS); + walletTransfer.setTimestamp(now); + int insert = eosWalletTransferService.insertWalletTransfer(walletTransfer); + if (insert != 1) throw new EosWalletException(EosWalletEnums.GET_PUBLICKEY_ERROR); + return walletMapper.updateWalletAllBalanceInRowLock(balance, freeBalance, freezeBalance, walletChangeDTO.getUserId(), walletDTO.getTokenName(), walletChangeDTO.getWalletType(), now); + } + + /** + * 初始化钱包方法 + * + * @param userId + * @return + */ + @Override + @Transactional + public Integer initEosWallet(String userId) { + // 判断是否存在钱包 + ExceptionPreconditionUtils.notEmpty(userId); + Integer walletCount = walletMapper.selectCountWalletByUserOpenId(userId); + if (walletCount != 0) throw new EosWalletException(EosWalletEnums.WALLETTOKENT_ADD_ERROR); + // 获取币种和应用 + List applications = eosApplicationService.listEosApplication(); + List tokens = eosTokenService.listEosToken(); + if (applications == null || applications.size() == 0 || tokens == null || tokens.size() == 0) + throw new EosWalletException(EosWalletEnums.CREATION_WALLET_ERROR); + // 创建钱包 + BigDecimal zero = BigDecimal.ZERO; + for (Application application : applications) { + for (Token token : tokens) { + Wallet wallet = new Wallet(); + wallet.setTokenName(token.getTokenName()); + wallet.setUserOpenId(userId); + wallet.setTokenSymbol(token.getTokenSymbol()); + wallet.setBalance(zero.toString()); + wallet.setFreeBalance(zero.toString()); + wallet.setFreezeBalance(zero.toString()); + Date date = new Date(); + wallet.setCreateTime(date); + wallet.setUpdateTime(date); + wallet.setWalletType(application.getAppId()); + walletMapper.insertSelective(wallet); + } + } + + return 1; + } + + /** + * 根据符号查询钱包 + * + * @param userId + * @param tokenSymbol + * @param walletType + * @return + */ + @Override + public WalletDTO selectWalletByTokenSymbol(String userId, String tokenSymbol, String walletType) { + ExceptionPreconditionUtils.notEmpty(walletType); + Integer count = eosApplicationService.selectWalletCountByWalletType(walletType); + if (count < 1) throw new EosWalletException(EosWalletEnums.EOSWALLET_GETWALLET_ERROR); + WalletDTO walletDTO = walletMapper.selectWalletByTokenSymbol(userId, tokenSymbol, walletType); + if (walletDTO == null) throw new EosWalletException(EosWalletEnums.EOSWALLET_GETWALLET_ERROR); + return walletDTO; + } + + @Override + public Boolean getTransaction(String blockNum, String hashId) { + ExceptionPreconditionUtils.notEmpty(blockNum); + ExceptionPreconditionUtils.notEmpty(hashId); + WalletTransfer walletTransfer = eosWalletTransferService.selectByHashId(hashId); + // 调用接口查询出币信息 + return eosUtil.getTransactions(blockNum, hashId, walletTransfer); + } + + + @Override + @Transactional + public void updateTxOutError(WalletTransfer tx) { + Wallet wallet = this.selectWalletByIdAndTokenName(tx.getFromId(), tx.getTokenName()); + Date date = new Date(); + // 业务操作(1)- 恢复提现余额 + BigDecimal amount = tx.getAmount(); + int row = walletMapper.updateWalletAllBalanceInRowLock(BigDecimal.ZERO, amount, amount.negate(), wallet.getUserOpenId(), wallet.getTokenName(), wallet.getWalletType(), date); + if (row == 0) throw new EosWalletException(EosWalletEnums.GET_PUBLICKEY_ERROR); + // 业务操作(2)- 修改记录状态 + eosWalletTransferService.updateStatus(tx.getId(), EosConstant.TransferStatus.DEFEATED, date); + } + + @Override + @Transactional + public void updateTxOutSuccess(WalletTransfer tx) { + // 数据验证 + Wallet wallet = this.selectWalletByIdAndTokenName(tx.getFromId(), tx.getTokenName()); + Date date = new Date(); + // 业务操作(1)- 扣除提现余额 + BigDecimal amount = tx.getAmount(); + int row = walletMapper.updateWalletAllBalanceInRowLock(amount.negate(), BigDecimal.ZERO, amount.negate(), wallet.getUserOpenId(), wallet.getTokenName(), wallet.getWalletType(), date); + if (row == 0) throw new EosWalletException(EosWalletEnums.GET_PUBLICKEY_ERROR); + // 业务操作(2)- 修改记录状态 + eosWalletTransferService.updateStatus(tx.getId(), EosConstant.TransferStatus.SUCCESS, date); + } + + @Override + public Wallet findWallet(String userId,String walletType, String coinName) { + Wallet where = new Wallet(); + where.setWalletType(walletType); + where.setTokenSymbol(coinName); + where.setUserOpenId(userId); + Wallet wallet = walletMapper.selectOne(where); + if(wallet == null) { + throw new EosWalletException(EosWalletEnums.INEXISTENCE_WALLET); + } + return wallet; + } + + @Override + @Transactional + public WalletTransfer handleTransfer(String userOpenId, String fromType, String toType, String coinName, BigDecimal amount) { + Date end = new Date(); + amount = amount.abs(); + // 钱包类型验证 + Integer count = eosApplicationService.selectWalletCountByWalletType(fromType); + if (count < 1) throw new EosWalletException(EosWalletEnums.EOSWALLET_GETWALLET_ERROR); + count = eosApplicationService.selectWalletCountByWalletType(toType); + if (count < 1) throw new EosWalletException(EosWalletEnums.EOSWALLET_GETWALLET_ERROR); + Token token = eosTokenService.selectByTokenSymbol(coinName); + Wallet fromWallet = this.findWallet(userOpenId,fromType,coinName); + Wallet toWallet = this.findWallet(userOpenId,toType,coinName); + //该用户减去提现可用余额、总额 + count = walletMapper.updateWalletAllBalanceInRowLock(amount.negate(), + amount.negate(), + BigDecimal.ZERO, + fromWallet.getUserOpenId(), + fromWallet.getTokenName(), + fromWallet.getWalletType(), + end); + if (count == 0) { + throw new EosWalletException(EosWalletEnums.ERROR); + } + //接收用户加上可用余额、总额 + count = walletMapper.updateWalletAllBalanceInRowLock(amount, + amount, + BigDecimal.ZERO, + toWallet.getUserOpenId(), + toWallet.getTokenName(), + toWallet.getWalletType(), + end); + if (count == 0) { + throw new EosWalletException(EosWalletEnums.ERROR); + } + // 添加待审核记录 + WalletTransfer walletTransfer = new WalletTransfer(); + walletTransfer.setId(UUID.randomUUID().toString()); + walletTransfer.setFromId(fromWallet.getId()); + walletTransfer.setToId(toWallet.getId()); + walletTransfer.setAmount(amount); + walletTransfer.setTokenName(token.getTokenName()); + walletTransfer.setTokenSymbol(token.getTokenSymbol()); + walletTransfer.setTransferType(EosConstant.TransferType.TYPE_FAST); + walletTransfer.setStatus(EosConstant.TransferStatus.SUCCESS); + walletTransfer.setTimestamp(end); + count = eosWalletTransferService.insertWalletTransfer(walletTransfer); + if (count == 0) { + throw new EosWalletException(EosWalletEnums.ERROR); + } + return walletTransfer; + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletTransferServiceImpl.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletTransferServiceImpl.java new file mode 100644 index 0000000..b3e9d17 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletTransferServiceImpl.java @@ -0,0 +1,153 @@ +package com.blockchain.server.eos.service.impl; + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.eos.common.constant.EosConstant; +import com.blockchain.server.eos.common.enums.EosWalletEnums; +import com.blockchain.server.eos.common.exception.EosWalletException; +import com.blockchain.server.eos.dto.WalletDTO; +import com.blockchain.server.eos.dto.WalletTransferDTO; +import com.blockchain.server.eos.entity.WalletTransfer; +import com.blockchain.server.eos.mapper.WalletTransferMapper; +import com.blockchain.server.eos.service.EosWalletService; +import com.blockchain.server.eos.service.EosWalletTransferService; +import com.github.pagehelper.PageHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +/** + * @author Harvey + * @date 2019/2/19 11:40 + * @user WIN10 + */ +@Service +public class EosWalletTransferServiceImpl implements EosWalletTransferService { + + @Autowired + private EosWalletService eosWalletService; + @Autowired + private WalletTransferMapper walletTransferMapper; + + /** + * 添加记录并修改账户金额 + * + * @param hash + * @param transferType + * @param amount + * @param tokenName + * @param tokenSymbol + * @param memo + * @param accountId + * @return + */ + @Override + @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED) + public int handleWalletAndWalletTransfer(String hash, Integer accountId, String accountName, String transferType, BigDecimal amount, String tokenName, String tokenSymbol, String memo, BigInteger blockNumber) { + Date now = new Date(); + WalletTransfer walletTransfer = new WalletTransfer(); + walletTransfer.setId(UUID.randomUUID().toString()); + walletTransfer.setRemark(memo); + walletTransfer.setTimestamp(now); + walletTransfer.setBlockNumber(blockNumber.toString()); + // 充值保存数据 + if (EosConstant.TransferType.TRANSFER_IN.equals(transferType)) { + walletTransfer.setHash(hash); + walletTransfer.setAmount(amount); + walletTransfer.setTokenName(tokenName); + walletTransfer.setTokenSymbol(tokenSymbol); + walletTransfer.setToId(accountId); + walletTransfer.setTransferType(EosConstant.TransferType.TRANSFER_IN); + } + walletTransfer.setStatus(EosConstant.TransferStatus.SUCCESS); + this.insertWalletTransfer(walletTransfer); + + // 添加一条交易记录 + // 修改账户金额 + + return eosWalletService.updateWalletBalanceByIdInRowLock(amount, accountId, now); + } + + /** + * 插入交易记录 + * + * @param walletTransfer + * @return + */ + @Override + @Transactional + public int insertWalletTransfer(WalletTransfer walletTransfer) { + ExceptionPreconditionUtils.notEmpty(walletTransfer); + return walletTransferMapper.insertSelective(walletTransfer); + } + + /** + * 查询钱包交易记录 + * + * @param userOpenId + * @param tokenName + * @param walletType + * @return + */ + @Override + public List listWalletTransfer(String userOpenId, String tokenName, String walletType) { + WalletDTO walletDTO = eosWalletService.selectWallet(userOpenId, tokenName, walletType); + Integer walletId = walletDTO.getId(); + return walletTransferMapper.listWalletTransfer(walletId, tokenName); + } + + + @Override + public List selectByTxTypeAndStatus(String transferType, int Status, Integer pageNum, Integer pageSize) { + PageHelper.startPage(pageNum, pageSize); + WalletTransfer where = new WalletTransfer(); + where.setTransferType(transferType); + where.setStatus(Status); + return walletTransferMapper.select(where); + } + + @Override + @Transactional + public void updateStatus(String id, int status, Date date) { + WalletTransfer ethWalletTransfer = walletTransferMapper.selectByPrimaryKey(id); + ExceptionPreconditionUtils.checkNotNull(ethWalletTransfer, new EosWalletException(EosWalletEnums.INEXISTENCE_TX)); + ethWalletTransfer.setStatus(status); + ethWalletTransfer.setTimestamp(date); + // 失败抛出异常 + int row = walletTransferMapper.updateByPrimaryKeySelective(ethWalletTransfer); + if (row == 0) throw new EosWalletException(EosWalletEnums.GET_PUBLICKEY_ERROR); + } + + /** + * 根据hash查询交易记录 + * @param hashId + * @return + */ + @Override + public WalletTransfer selectByHashId(String hashId) { + WalletTransfer example = new WalletTransfer(); + example.setHash(hashId); + return walletTransferMapper.selectOne(example); + } + + /** + * 根据hash查询是否存在数据 + * @param hash + * @param transferType + * @return + */ + @Override + public Integer countByHashAndTransferType(String hash, String transferType) { + ExceptionPreconditionUtils.notEmpty(hash); + ExceptionPreconditionUtils.notEmpty(transferType); + return walletTransferMapper.countByHashAndTransferType(hash, transferType); + } + +} diff --git a/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletUtilServiceImpl.java b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletUtilServiceImpl.java new file mode 100644 index 0000000..c5568cd --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/java/com/blockchain/server/eos/service/impl/EosWalletUtilServiceImpl.java @@ -0,0 +1,109 @@ +package com.blockchain.server.eos.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.blockchain.server.eos.dto.BlockNumberDTO; +import com.blockchain.server.eos.eos4j.Rpc; +import com.blockchain.server.eos.eos4j.api.vo.ChainInfo; +import com.blockchain.server.eos.service.EosBlockNumberService; +import com.blockchain.server.eos.service.EosWalletUtilService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import java.math.BigInteger; + +/** + * @author Harvey + * @date 2019/2/18 13:57 + * @user WIN10 + */ +@Service +public class EosWalletUtilServiceImpl implements EosWalletUtilService { + + @Autowired + private EosBlockNumberService eosBlockNumberService; + + + private Rpc eosRpc = null; + + @Value("${rpc_url}") + private String RPC_URL; + @Value("${get_block_url}") + private String GET_BLOCK_URL; + @Value("${get_transaction}") + private String GET_TRANSACTION_URL; + @Value("${get_actions}") + private String GET_ACTIONS; + + + @Override + public BigInteger getEosBlock() { + eosRpc = new Rpc(RPC_URL); + ChainInfo chainInfo = eosRpc.getChainInfo(); + String headBlockNum = chainInfo.getHeadBlockNum(); + return new BigInteger(headBlockNum); + } + + /** + * 获取区块交易信息 + * + * @param nowBlock + * @return + */ + @Override + public JSONObject listBlockTransaction(BigInteger nowBlock) { + eosRpc = new Rpc(RPC_URL); + String url = RPC_URL + GET_BLOCK_URL; + ResponseEntity body = eosRpc.get_block(url, nowBlock); + if (body != null) return body.getBody(); + return null; + } + + @Override + public BlockNumberDTO getBlockNum() { + + BigInteger newstBlock = this.getEosBlock(); //最新区块 + // 从redis获取区块信息 + // 当前区块高度 + BigInteger current = eosBlockNumberService.selectCurrentBlockNum(); + if (current == null) current = newstBlock; + // 判断当前区块是否大于最新区块 + if (current.compareTo(newstBlock) > 0) newstBlock = this.getEosBlock(); //最新区块 + BlockNumberDTO blockNumberDTO = new BlockNumberDTO(); + blockNumberDTO.setCurrentBlock(current); + blockNumberDTO.setNewBlock(newstBlock); + return blockNumberDTO; + } + + /** + * 获取出币信息 + * + * @param blockNum + * @param hashId + * @return + */ + @Override + public JSONObject getTransaction(String blockNum, String hashId) { + eosRpc = new Rpc(RPC_URL); + String url = RPC_URL + GET_TRANSACTION_URL; + ResponseEntity body = eosRpc.getTransaction(url, blockNum, hashId); + if (body != null) return body.getBody(); + return null; + } + + /** + * 获取账户历史交易信息 + * + * @param accountName + * @return + */ + @Override + public JSONObject getActions(String accountName, String offset) { + eosRpc = new Rpc(RPC_URL); + String url = RPC_URL + GET_ACTIONS; + ResponseEntity body = eosRpc.getActions(url, accountName, offset); + if (body != null) return body.getBody(); + return null; + } +} diff --git a/blockchain-server/blockchain-server-eos/src/main/resources/application.yml b/blockchain-server/blockchain-server-eos/src/main/resources/application.yml new file mode 100644 index 0000000..1dac60c --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/resources/application.yml @@ -0,0 +1,3 @@ +#日志配置路径 +logging: + config: classpath:logback/logback-${spring.cloud.config.profile}.xml diff --git a/blockchain-server/blockchain-server-eos/src/main/resources/bootstrap.yml b/blockchain-server/blockchain-server-eos/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..8dda277 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/resources/bootstrap.yml @@ -0,0 +1,22 @@ +server: + port: 8401 +#注册中心 +eureka: + client: + service-url: + defaultZone: http://eureka:8001/eureka/ + instance: + prefer-ip-address: true + instance-id: ${spring.cloud.client.ip-address}:${server.port} + hostname: ${spring.cloud.client.ip-address} +spring: + cloud: + #配置中心 + config: + discovery: + service-id: dapp-config-server + enabled: true + profile: dev + name: springconf,springcloudconf,redisconf,dbconf,txconf,xssconf,eosconf,ipconf + application: + name: dapp-eos-server diff --git a/blockchain-server/blockchain-server-eos/src/main/resources/i18n/MessgesBundle_zh_CN.properties b/blockchain-server/blockchain-server-eos/src/main/resources/i18n/MessgesBundle_zh_CN.properties new file mode 100644 index 0000000..f5db252 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/resources/i18n/MessgesBundle_zh_CN.properties @@ -0,0 +1,5 @@ +#\u516C\u5171 +ERROR_PARAMETER_NOTNULL=\u53C2\u6570\u4E0D\u80FD\u4E3A\u7A7A,\u8BF7\u68C0\u67E5\u53C2\u6570 +RESULT_ERROR_MSG=\u672A\u77E5\u5F02\u5E38 +LOGIN_INVALID_MSG=\u767B\u5F55\u4FE1\u606F\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55 +SYSTEM_BUSY_MSG=\u7F51\u7EDC\u7E41\u5FD9\uFF01 \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/resources/logback/logback-dev.xml b/blockchain-server/blockchain-server-eos/src/main/resources/logback/logback-dev.xml new file mode 100644 index 0000000..9a0e324 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/resources/logback/logback-dev.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/resources/logback/logback-pro.xml b/blockchain-server/blockchain-server-eos/src/main/resources/logback/logback-pro.xml new file mode 100644 index 0000000..5b55059 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/resources/logback/logback-pro.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/resources/mapper/ApplicationMapper.xml b/blockchain-server/blockchain-server-eos/src/main/resources/mapper/ApplicationMapper.xml new file mode 100644 index 0000000..d426644 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/resources/mapper/ApplicationMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + dapp_eos_application + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/resources/mapper/BlockNumberMapper.xml b/blockchain-server/blockchain-server-eos/src/main/resources/mapper/BlockNumberMapper.xml new file mode 100644 index 0000000..eecb5ce --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/resources/mapper/BlockNumberMapper.xml @@ -0,0 +1,55 @@ + + + + + + + + + + dapp_eos_block_number + + + + + + + + + + + + + + update + set status = #{status} + where block_number = #{blockNum} + + + + insert ignore into + (block_number, status, create_time) + value (#{blockNumber}, #{status}, #{createTime}) + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/resources/mapper/ConfigWalletParamMapper.xml b/blockchain-server/blockchain-server-eos/src/main/resources/mapper/ConfigWalletParamMapper.xml new file mode 100644 index 0000000..95fa006 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/resources/mapper/ConfigWalletParamMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + config_wallet_param + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/resources/mapper/TokenMapper.xml b/blockchain-server/blockchain-server-eos/src/main/resources/mapper/TokenMapper.xml new file mode 100644 index 0000000..02159e4 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/resources/mapper/TokenMapper.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + dapp_eos_token + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/resources/mapper/WalletInMapper.xml b/blockchain-server/blockchain-server-eos/src/main/resources/mapper/WalletInMapper.xml new file mode 100644 index 0000000..99e66f0 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/resources/mapper/WalletInMapper.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + dapp_eos_wallet_in + + + + + + + update + + set + block_number = #{blockNum} + where account_name = #{accountName} + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/resources/mapper/WalletMapper.xml b/blockchain-server/blockchain-server-eos/src/main/resources/mapper/WalletMapper.xml new file mode 100644 index 0000000..5518a26 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/resources/mapper/WalletMapper.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + dapp_eos_wallet + + + UPDATE + + wallet + SET + wallet.balance = wallet.balance + #{balance}, + wallet.free_balance = wallet.free_balance + #{balance}, + wallet.update_time = #{modifyTime} + WHERE + wallet.id = #{id} + + + + + + UPDATE wallet + SET + wallet.balance = wallet.balance + #{balance}, + wallet.free_balance = wallet.free_balance + #{freeBalance}, + wallet.freeze_balance = wallet.freeze_balance + #{freezeBalance}, + wallet.update_time = #{modifyTime} + WHERE wallet.user_open_id = #{userOpenId} + and token_name = #{tokenName} + and wallet_type = #{walletType} + AND wallet.free_balance + #{freeBalance} >= 0 + AND wallet.freeze_balance + #{freezeBalance} >= 0 + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eos/src/main/resources/mapper/WalletTransferMapper.xml b/blockchain-server/blockchain-server-eos/src/main/resources/mapper/WalletTransferMapper.xml new file mode 100644 index 0000000..12d6c06 --- /dev/null +++ b/blockchain-server/blockchain-server-eos/src/main/resources/mapper/WalletTransferMapper.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + dapp_eos_wallet_transfer + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/pom.xml b/blockchain-server/blockchain-server-eth/pom.xml new file mode 100644 index 0000000..ed5b417 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/pom.xml @@ -0,0 +1,63 @@ + + + + blockchain-server + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + blockchain-server-eth + + + com.blockchain + blockchain-server-base + 1.0-SNAPSHOT + + + com.blockchain + blockchain-common-tx + 1.0-SNAPSHOT + + + + + org.web3j + core + 3.4.0 + + + org.web3j + geth + 3.4.0 + + + org.web3j + abi + 3.4.0 + + + org.bitcoinj + bitcoinj-core + 0.14.6 + compile + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/EthApplication.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/EthApplication.java new file mode 100644 index 0000000..d3651b0 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/EthApplication.java @@ -0,0 +1,20 @@ +package com.blockchain.server.eth; + +import com.blockchain.server.base.BaseConf; +import com.blockchain.server.eth.common.config.EthConfig; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +/** + * @author huangxl + * @create 2018-11-13 10:15 + */ + +@SpringBootApplication(scanBasePackageClasses = {BaseConf.class, EthApplication.class}) +public class EthApplication { + public static void main(String[] args) { + SpringApplication.run(EthApplication.class, args); + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/config/EthConfig.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/config/EthConfig.java new file mode 100644 index 0000000..3f41cee --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/config/EthConfig.java @@ -0,0 +1,31 @@ +package com.blockchain.server.eth.common.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +/** + * YH + * 2019年2月16日16:57:21 + */ +@Data +@Configuration +@Component +@ConfigurationProperties(prefix = "ethconfig") +public class EthConfig { + + String emptyAddress; + + String RPC_URL; + + String os; + + String urlType; + + String url; + + String path; + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/config/InterceptorConf.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/config/InterceptorConf.java new file mode 100644 index 0000000..c1235c5 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/config/InterceptorConf.java @@ -0,0 +1,32 @@ +package com.blockchain.server.eth.common.config; + +import com.blockchain.server.base.interceptor.LoginInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class InterceptorConf implements WebMvcConfigurer { + @Bean + public LoginInterceptor loginInterceptor() { + return new LoginInterceptor(); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(loginInterceptor()) + .addPathPatterns("/**") + .excludePathPatterns("/inner/**") + .excludePathPatterns("/webjars/**") + .excludePathPatterns("/swagger-ui.html") + .excludePathPatterns("/swagger-resources/**") + .excludePathPatterns("/v2/api-docs/**"); + } + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**").allowedOrigins("*"); + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/constants/EthConfigConstants.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/constants/EthConfigConstants.java new file mode 100644 index 0000000..2dbe60b --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/constants/EthConfigConstants.java @@ -0,0 +1,30 @@ +package com.blockchain.server.eth.common.constants; + +/** + * 以太坊Config配置常量参数 + * + * @author YH + * @date 2019年2月18日15:25:04 + */ +public class EthConfigConstants { + + public static final String MODULE_TYPE = "eth"; // 所属于钱包模块 + public static final int STATUS_DISABLED = 0; // 禁用 + public static final int STATUS_NORMAL = 1; // 启用 + + /** + * 是否允许提现的配置 + */ + public static class IsApplyTxout { + public static final String PARAM_NAME = "is_apply_txout"; // 参数名称 + public static final String PARAM_VALUE_YES = "Y"; // 允许 + public static final String PARAM_VALUE_NO = "N"; // 不允许 + } + + /** + * 手续费配置 + */ + public static class EthGasConfig { + public static final String PARAM_NAME = "eth_gas_config_"; // 参数名称 + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/constants/EthWalletConstants.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/constants/EthWalletConstants.java new file mode 100644 index 0000000..5d66515 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/constants/EthWalletConstants.java @@ -0,0 +1,49 @@ +package com.blockchain.server.eth.common.constants; + +/** + * 以太坊钱包常量参数 + * + * @author YH + * @date 2019年2月18日15:25:04 + */ +public class EthWalletConstants { + public static int BASE_PAGESIZE = 10; // 默认页面条数 + public static int WALLERT_LENGTH = 42; // 地址长度 + + /** + * 钱包流水账的转账类型 + */ + public static class TransferType { + public final static String OUT = "OUT"; + public final static String IN = "IN"; + public final static String CCT = "CCT"; + public final static String GAS = "GAS"; + public final static String TXMIN = "TXMIN"; // 内部转账(自身) + public final static String TXOIN = "TXOIN"; // 内部转账(他人) + //交易类型 转内快速转账 + public static final String FAST = "FAST"; + } + + public static class StatusType { + // 内部转账状态 + public final static int ERROR = 0; //充值失败 + public final static int SUCCESS = 1; //充值成功 + // 充币状态 + public final static int IN_ERROR = 0; // 充值失败 + public final static int IN_SUCCESS = 1; // 充值成功 + public final static int IN_LOAD = 5; // 打包中 + // 提币状态 + public final static int OUT_LOAD1 = 2; // 待初审提币 + public final static int OUT_LOAD2 = 3; // 待复审提币 + public final static int OUT_LOAD3 = 4; // 待出币 + public final static int OUT_LOAD4 = 5; // 已出币(打包中) + public final static int OUT_ERROR = 6; // 出币失败 + public final static int OUT_SUCCESS = 1; // 出币成功 + public final static int OUT_NOTPASS = 7; // 审核不通过 + // 区块插入状态 + public final static char BLOCK_ERROR = 'N'; //查询失败 + public final static char BLOCK_SUCCESS = 'Y'; //查询成功 + public final static char BLOCK_WAIT = 'W'; //等待 + } + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/enums/EthWalletEnums.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/enums/EthWalletEnums.java new file mode 100644 index 0000000..9fa053c --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/enums/EthWalletEnums.java @@ -0,0 +1,97 @@ +package com.blockchain.server.eth.common.enums; + +public enum EthWalletEnums { + SUCCESS(200, "请求成功", "Request success", ""), + NO_LOGIN(201, "未登录", "No login", ""), + NOT_PERFECT(500, "接口未完善", "Not perfect", ""), + NOT_WALLETPASSWORD(800, "你还没有设置资金密码", "You have not set the fund password", "妳還沒有設置資金密碼"), + // 关于为空的提示 + NULL_ERROR(12500, "没有接收到参数", "No arguments were received", "沒有接收到參數"), + NULL_USEROPENID(12500, "用户标识不能为空", "The user id cannot be empty", "用戶標識不能為空"), + NULL_ADDR(12500, "钱包地址不能为空", "The wallet address cannot be empty", "錢包地址不能為空"), + NULL_TOKENADDR(12500, "货币地址不能为空", "The currency address cannot be empty", "貨幣地址不能為空"), + NULL_WALLETTYPE(12500, "钱包类型不能为空", "The wallet type cannot be empty", "錢包類型不能為空"), + NULL_PASSWORD(12500, "钱包密码不能为空", "The wallet password cannot be empty", "錢包密碼不能為空"), + NULL_AMOUNT(12500, "转账金额不能为空", "The transfer amount cannot be empty", "轉賬金額不能為空"), + NULL_TXIN(12500, "充值记录不能为空", "The recharge record cannot be empty", "充值記錄不能為空"), + NULL_OUT_TOADDR(12500, "提现的钱包地址不能为空", "The withdrawal address cannot be empty", "提現地址不能為空"), + NULL_FREEBLANCE(12500, "可用余额不能为空", "The available balance cannot be empty", "可用余額不能為空"), + NULL_FREEZEBLANCE(12500, "冻结余额不能为空", "The frozen balance cannot be empty", "凍結余額不能為空"), + // 关于查询不到的提示 + INEXISTENCE_TOKENADDR(12600, "该币种暂不支持此操作", "This operation is not currently supported in this currency", + "該幣種暫不支持此操作"), + IEXIST_TOKENADDRS(12600, "该币种存在多个", "There are multiple versions of this currency", + "該幣種存在多個"), + INEXISTENCE_WALLET(12600, "钱包信息未找到", "Wallet information not found", "錢包信息未找到"), + INEXISTENCE_WALLETTYPE(12600, "该钱包类型不存在", "The wallet type does not exist", "該錢包類型不存在"), + INEXISTENCE_TX(12500, "该记录未找到", "The record was not found", "該記錄未找到"), + // 关于校验错误的提示 + DATA_EXCEPTION_ERROR(12700, "操作失败,数据异常", "Operation failed, data abnormal", "操作失败,数据异常"), + OUT_TOADDR_ERROR(12700, "操作失败,提现钱包的钱包地址格式有误", "Operation failed, the wallet address format of the withdrawal " + + "wallet is wrong", "操作失敗,提現錢包的錢包地址格式有誤"), + PASSWORD_ERROR(12710, "操作失败,密码错误", "Operation failed, password error", "操作失敗,密碼錯誤"), + INITWALLERT_ERROR(12700, "操作失败,钱包不能重复初始化", "Operation failed. Wallet cannot be initialized again", + "操作失敗,錢包不能重復初始化"), + EQWALLERTTYPE_ERROR(12700, "操作失败,不支持同钱包类型划转", "Operation failed, do not support the same wallet type transfer", + "操作失敗,不支持同錢包類型劃轉"), + NUMBER_TYPE_ERROR(12700, "操作失败,转账金额格式有误", "Operation failed, the transfer amount format is wrong", "操作失敗,轉賬金額格式有誤"), + NUMBER_COUNT_ERROR(12700, "操作失败,转账金额必须大于0", "Operation failed, transfer amount must be greater than 0", + "操作失敗,轉賬金額必須大於0"), + NUMBER_INSUFFICIENT_ERROR(12700, "操作失败,该数字货币可用余额不足", "Operation failed, the available balance of the digital " + + "currency is insufficient", "操作失敗,該數字貨幣可用余額不足"), + NUMBER_INSUFFICIENTZE_ERROR(12700, "操作失败,该数字货币冻结余额不足", "Operation failed, the digital currency freeze balance is " + + "insufficient", "操作失敗,該數字貨幣凍結余額不足"), + NUMBER_INSUFFICIENT_GAS_ERROR(12700, "操作失败,提现手续费不足", "Operation failure, insufficient withdrawal charge", + "操作失敗,提現手續費不足"), + NUMBER_MINWDAMOUNT_ERROR(12700, "提现余额不得小于最小提现额度", "The withdrawal balance shall not be less than the minimum withdrawal limit", + "提現余額不得小於最小提現額度"), + // 数据的增删改报错的提示 + INSERT_INITWALLERT(12800, "钱包初始化失败,请稍后重试", "Wallet initialization failed. Please try again later", "錢包初始化失敗,請稍後重試"), + INSERT_ADDWALLERT(12800, "该币种钱包已经创建", "The currency wallet has been created", "該幣種錢包已經創建"), + // 关于连接失败,服务器繁忙的提示 + SERVER_IS_TOO_BUSY(15000, "服务器繁忙,请稍后重试", "The server is busy, please try again later", "服務器繁忙,請稍後重試"), + ; + private int code; + private String hkmsg; + private String enMsg; + private String cnmsg; + + EthWalletEnums(int code, String cnmsg, String enMsg, String hkmsg) { + this.code = code; + this.cnmsg = cnmsg; + this.enMsg = enMsg; + this.hkmsg = hkmsg; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getHkmsg() { + return hkmsg; + } + + public void setHkmsg(String hkmsg) { + this.hkmsg = hkmsg; + } + + public String getEnMsg() { + return enMsg; + } + + public void setEnMsg(String enMsg) { + this.enMsg = enMsg; + } + + public String getCnmsg() { + return cnmsg; + } + + public void setCnmsg(String cnmsg) { + this.cnmsg = cnmsg; + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/exception/EthWalletException.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/exception/EthWalletException.java new file mode 100644 index 0000000..49d4b08 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/exception/EthWalletException.java @@ -0,0 +1,39 @@ +package com.blockchain.server.eth.common.exception; + + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.eth.common.enums.EthWalletEnums; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +public class EthWalletException extends BaseException { + public EthWalletException(EthWalletEnums rs) { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + //可能是定时器调用,避免获取request空指针 + if (servletRequestAttributes == null) { + this.code = rs.getCode(); + this.msg = rs.getCnmsg(); + } else { + HttpServletRequest request = servletRequestAttributes.getRequest(); + String userLocale = HttpRequestUtil.getUserLocale(request); + String msg = ""; + switch (userLocale) { + case BaseConstant.USER_LOCALE_EN_US: + msg = rs.getEnMsg(); + break; + case BaseConstant.USER_LOCALE_ZH_HK: + msg = rs.getHkmsg(); + break; + default: + msg = rs.getCnmsg(); + break; + } + this.code = rs.getCode(); + this.msg = msg; + } + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/BaseHelperUtil.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/BaseHelperUtil.java new file mode 100644 index 0000000..a4796f1 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/BaseHelperUtil.java @@ -0,0 +1,22 @@ +package com.blockchain.server.eth.common.util; + +import com.blockchain.server.eth.common.constants.EthWalletConstants; +import com.github.pagehelper.PageHelper; + +/** + * 常用方法简易工具 + */ +public class BaseHelperUtil { + + /** + * 查询分页处理 + * + * @param pageNum 当前页数 + * @param pageSize 页数展示条数 + */ + public static void startPage(Integer pageNum, Integer pageSize) { + if (null == pageNum) pageNum = 0; + if (null == pageSize) pageSize = EthWalletConstants.BASE_PAGESIZE; + PageHelper.startPage(pageNum, pageSize); + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/DataCheckUtil.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/DataCheckUtil.java new file mode 100644 index 0000000..df93f4c --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/DataCheckUtil.java @@ -0,0 +1,84 @@ +package com.blockchain.server.eth.common.util; + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.eth.common.enums.EthWalletEnums; +import com.blockchain.server.eth.common.exception.EthWalletException; + +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * 检查数据类型工具类 + */ +public class DataCheckUtil { + + /** + * 转账金额格式化 + * + * @param amount 转账金额 + * @return + */ + public static BigDecimal strToBigDecimal(String amount) { + ExceptionPreconditionUtils.checkStringNotBlank(amount, new EthWalletException(EthWalletEnums.NULL_TOKENADDR)); + BigDecimal _amount = BigDecimal.ZERO; + try { + _amount = new BigDecimal(amount); + } catch (Exception e) { + ExceptionPreconditionUtils.checkStringNotBlank(amount, + new EthWalletException(EthWalletEnums.NUMBER_TYPE_ERROR)); + } + if (_amount.compareTo(BigDecimal.ZERO) > 0) { + return _amount; + } else { + throw new EthWalletException(EthWalletEnums.NUMBER_COUNT_ERROR); + } + } + + /** + * 转账金额格式 + * + * @param amount + * @param decimal + * @return + */ + public static BigDecimal bitToBigDecimal(BigInteger amount, int decimal) { + BigDecimal _amount = null; + _amount = new BigDecimal(amount); + _amount = _amount.divide(BigDecimal.TEN.pow(decimal)); + return _amount; + } + + /** + * 转账金额格式 + * + * @param amount + * @return + */ + public static BigDecimal bitToBigDecimal(BigInteger amount) { + return bitToBigDecimal(amount, 18); + } + + /** + * 对象转BigInteger + * + * @param object + * @return + */ + public static BigInteger objToBigInteger(Object object) { + if (object == null) return null; + if (!(object instanceof BigInteger)) return null; + return (BigInteger) object; + } + + /** + * 对象转int + * + * @param object + * @return + */ + public static Integer objToInt(Object object) { + if (object == null) return null; + if (!(object instanceof Integer)) return null; + return (Integer) object; + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/RedisBlockUtil.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/RedisBlockUtil.java new file mode 100644 index 0000000..c36450e --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/RedisBlockUtil.java @@ -0,0 +1,133 @@ +package com.blockchain.server.eth.common.util; + +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStringCommands; +import org.springframework.data.redis.core.RedisTemplate; + +import java.math.BigInteger; +import java.util.Map; +import java.util.Set; + +/** + * 缓存区块充值记录数据爬取的缓存 + */ +public class RedisBlockUtil { + // 储存区块的初始化高度 + static final String ETH_BLOCKCHAIN_BEGIN = "eth:blockchain:begin"; + // 储存区块的当前最新高度 + static final String ETH_BLOCKCHAIN_CURRENT = "eth:blockchain:current"; + // 储存区块的链上最新高度 + static final String ETH_BLOCKCHAIN_NEWEST = "eth:blockchain:newest"; + // 储存所有遗漏区块的爬取状态 + static final String ETH_BLOCKCHAIN_OPT_HASH = "eth:blockchain:opt:hash"; + + /** + * 缓存初始化的区块高度 + * + * @param blockNumber + * @param redisTemplate + */ + public static void setBlockBegin(BigInteger blockNumber, RedisTemplate redisTemplate) { + redisTemplate.opsForValue().set(ETH_BLOCKCHAIN_BEGIN, blockNumber); + } + + /** + * 获取初始化的区块高度 + * + * @param redisTemplate + * @return + */ + public static BigInteger getBlockBegin(RedisTemplate redisTemplate) { + Object blockBegin = redisTemplate.opsForValue().get(ETH_BLOCKCHAIN_BEGIN); + return DataCheckUtil.objToBigInteger(blockBegin); + } + + /** + * 缓存初始化的区块高度 + * + * @param blockNumber + * @param redisTemplate + */ + public static void setBlockNewest(BigInteger blockNumber, RedisTemplate redisTemplate) { + redisTemplate.opsForValue().set(ETH_BLOCKCHAIN_NEWEST, blockNumber); + } + + /** + * 获取初始化的区块高度 + * + * @param redisTemplate + * @return + */ + public static BigInteger getBlockNewest(RedisTemplate redisTemplate) { + Object blockBegin = redisTemplate.opsForValue().get(ETH_BLOCKCHAIN_NEWEST); + return DataCheckUtil.objToBigInteger(blockBegin); + } + + /** + * 缓存链上的最新区块高度 + * + * @param blockNumber + * @param redisTemplate + */ + public static void setBlockCurrent(BigInteger blockNumber, RedisTemplate redisTemplate) { + redisTemplate.opsForValue().set(ETH_BLOCKCHAIN_CURRENT, blockNumber); + } + + /** + * 获取链上的最新高度 + * + * @param redisTemplate + * @return + */ + public static BigInteger getBlockCurrent(RedisTemplate redisTemplate) { + Object blockBegin = redisTemplate.opsForValue().get(ETH_BLOCKCHAIN_CURRENT); + return DataCheckUtil.objToBigInteger(blockBegin); + } + + + + /** + * 根据区块高度,获取爬取的次数 + * + * @param hashKey 区块高度 + * @return + */ + public static Integer getBlockOptMapCount(BigInteger hashKey, RedisTemplate redisTemplate) { + Object val = redisTemplate.opsForHash().get(ETH_BLOCKCHAIN_OPT_HASH, hashKey); + Integer count = DataCheckUtil.objToInt(val); + return count != null ? count : 0; + } + + /** + * 存入爬取次数 + * + * @param hashKey 区块高度 + * @param redisTemplate + */ + public static void setBlockOptMapCount(BigInteger hashKey, RedisTemplate redisTemplate) { + redisTemplate.opsForHash().put(ETH_BLOCKCHAIN_OPT_HASH, hashKey, 1); + } + + /** + * 存入爬取次数 + * + * @param hashKey 区块高度 + * @param redisTemplate + */ + public static void incrementBlockOptMapCount(BigInteger hashKey, RedisTemplate redisTemplate) { + redisTemplate.opsForHash().increment(ETH_BLOCKCHAIN_OPT_HASH, hashKey, 1); + } + + /** + * 移除区块的爬取记录 + * + * @param hashKey 区块高度 + * @param redisTemplate + */ + public static void delBlockOptMapCount(BigInteger hashKey, RedisTemplate redisTemplate) { + redisTemplate.opsForHash().delete(ETH_BLOCKCHAIN_OPT_HASH, hashKey); + } + + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/RedisPrivateUtil.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/RedisPrivateUtil.java new file mode 100644 index 0000000..2f9934b --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/RedisPrivateUtil.java @@ -0,0 +1,60 @@ +package com.blockchain.server.eth.common.util; + +import com.blockchain.common.base.util.RSACoderUtils; +import com.blockchain.server.eth.common.enums.EthWalletEnums; +import com.blockchain.server.eth.common.exception.EthWalletException; +import org.apache.commons.codec.binary.Base64; +import org.springframework.data.redis.core.RedisTemplate; + +import java.security.KeyPair; +import java.util.concurrent.TimeUnit; + +public class RedisPrivateUtil { + public static final String PRIVATE_KEY = "wallet:private:"; // 储存私钥的key值,用于解密公钥加密的密码 + public static final int INVALID_DAYS = 1; // 设置过期时间一天 + + /** + * 缓存加密私钥 + * + * @param key + * @param privateKey 私钥 + * @param redisTemplate + */ + private static void setPrivateKey(String key, String privateKey, RedisTemplate redisTemplate) { + String _key = RedisPrivateUtil.PRIVATE_KEY + key; + redisTemplate.opsForValue().set(_key, privateKey, RedisPrivateUtil.INVALID_DAYS, TimeUnit.MINUTES); + } + + /** + * 获取缓存中的私钥 + * + * @param key + * @param redisTemplate + * @return 私钥 + */ + public static String getPrivateKey(String key, RedisTemplate redisTemplate) { + String _key = RedisPrivateUtil.PRIVATE_KEY + key; + String privateKey = (String) redisTemplate.opsForValue().get(_key); + redisTemplate.delete(RedisPrivateUtil.PRIVATE_KEY + key); + return privateKey; + } + + /** + * 把私钥存入缓存,返回公钥 + * + * @param key + * @param redisTemplate + * @return 公钥 + */ + public static String getPublicKey(String key, RedisTemplate redisTemplate) { + try { + KeyPair keyPair = RSACoderUtils.initKey(); + String puclicKey = Base64.encodeBase64String(keyPair.getPublic().getEncoded()); // 获取公钥 + String privateKey = Base64.encodeBase64String(keyPair.getPrivate().getEncoded()); // 获取私钥 + RedisPrivateUtil.setPrivateKey(key, privateKey, redisTemplate); // 存入私钥 + return puclicKey; + } catch (Exception e) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/RedisWalletAddrUtil.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/RedisWalletAddrUtil.java new file mode 100644 index 0000000..5b925ce --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/common/util/RedisWalletAddrUtil.java @@ -0,0 +1,91 @@ +package com.blockchain.server.eth.common.util; + +import com.blockchain.common.base.util.RSACoderUtils; +import com.blockchain.server.eth.common.enums.EthWalletEnums; +import com.blockchain.server.eth.common.exception.EthWalletException; +import com.blockchain.server.eth.entity.EthToken; +import org.apache.commons.codec.binary.Base64; +import org.springframework.data.redis.core.RedisTemplate; + +import java.security.KeyPair; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * 缓存所有钱包用户的地址 + */ +public class RedisWalletAddrUtil { + // 储存所有钱包地址对象的key值 + static final String ETH_WALLET_ADDRS = "eth:wallet:addrs"; + // 储存所有代币地址对象的key值 + static final String ETH_WALLET_COINADDRS = "eth:wallet:coinAddrs"; + + /** + * 添加新的钱包地址 + * + * @param addr 钱包地址 + * @param redisTemplate + */ + public static void setWalletAddr(String addr, RedisTemplate redisTemplate) { + if (!redisTemplate.opsForSet().isMember(ETH_WALLET_ADDRS, addr)) { + redisTemplate.opsForSet().add(ETH_WALLET_ADDRS, addr); + } + } + + /** + * 获取缓存中所有用户的钱包地址 + * + * @param redisTemplate + * @return + */ + public static Set getWalletAddrs(RedisTemplate redisTemplate) { + return redisTemplate.opsForSet().members(ETH_WALLET_ADDRS); + } + + /** + * 缓存中所有用户的钱包地址 + * + * @param addrs + * @param redisTemplate + */ + public static void setWalletAddrs(Set addrs, RedisTemplate redisTemplate) { + redisTemplate.opsForSet().add(ETH_WALLET_ADDRS, addrs.toArray()); + } + + /** + * 添加新币种地址 + * + * @param coin 代币对象 + * @param redisTemplate + */ + public static void setWalletCoinAddr(EthToken coin, RedisTemplate redisTemplate) { + redisTemplate.opsForHash().put(ETH_WALLET_COINADDRS, coin.getTokenAddr(), coin); + } + + /** + * 获取缓存中代币地址 + * + * @param redisTemplate + * @return + */ + public static Map getWalletCoinAddrs(RedisTemplate redisTemplate) { + return redisTemplate.opsForHash().entries(ETH_WALLET_COINADDRS); + } + + /** + * 缓存中所有币种地址 + * + * @param coinAddrs + * @param redisTemplate + */ + public static Map setWalletCoinAddrs(List coinAddrs, RedisTemplate redisTemplate) { + for (EthToken coin : coinAddrs) { + coin.setDescr(""); + redisTemplate.opsForHash().put(ETH_WALLET_COINADDRS, coin.getTokenAddr(), coin); + } + return redisTemplate.opsForHash().entries(ETH_WALLET_COINADDRS); + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/ConfigWalletParamController.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/ConfigWalletParamController.java new file mode 100644 index 0000000..1c76d1b --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/ConfigWalletParamController.java @@ -0,0 +1,29 @@ +package com.blockchain.server.eth.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.eth.common.constants.EthConfigConstants; +import com.blockchain.server.eth.controller.api.ConfigWalletParamApi; +import com.blockchain.server.eth.service.IConfigWalletParamService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Api(ConfigWalletParamApi.MARKET_CONTROLLER_API) +@RestController +@RequestMapping("/walletParam") +public class ConfigWalletParamController { + @Autowired + private IConfigWalletParamService walletParamService; + + @ApiOperation(value = ConfigWalletParamApi.GetGasConfig.METHOD_API_NAME, notes = ConfigWalletParamApi.GetGasConfig.METHOD_API_NOTE) + @GetMapping("/getGasConfig") + public ResultDTO getGasConfig(@ApiParam(ConfigWalletParamApi.GetGasConfig.TOKENSYMBOL) @RequestParam(value = "tokenSymbol", defaultValue = EthConfigConstants.MODULE_TYPE) String tokenSymbol) { + return ResultDTO.requstSuccess(walletParamService.getGasConfig(tokenSymbol)); + } + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/EthTokenController.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/EthTokenController.java new file mode 100644 index 0000000..aaae78a --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/EthTokenController.java @@ -0,0 +1,38 @@ +package com.blockchain.server.eth.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.eth.common.config.EthConfig; +import com.blockchain.server.eth.controller.api.EthTokenApi; +import com.blockchain.server.eth.service.IEthTokenService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author YH + * @date 2018年11月6日15:51:56 + */ +@Api(EthTokenApi.MARKET_CONTROLLER_API) +@RestController +@RequestMapping("/token") +public class EthTokenController { + @Autowired + IEthTokenService ethTokenService; + + @ApiOperation(value = EthTokenApi.FindToken.METHOD_API_NAME, notes = EthTokenApi.FindToken.METHOD_API_NOTE) + @GetMapping("/findToken") + public ResultDTO findToken(@ApiParam(EthTokenApi.FindToken.METHOD_API_TOKENADDR) @RequestParam(name = "tokenAddr") String tokenAddr) { + return ResultDTO.requstSuccess(ethTokenService.findByTokenAddr(tokenAddr)); + } + + @ApiOperation(value = EthTokenApi.SelectTokenAll.METHOD_API_NAME, notes = EthTokenApi.SelectTokenAll.METHOD_API_NOTE) + @GetMapping("/selectTokenAll") + public ResultDTO selectTokenAll() { + return ResultDTO.requstSuccess(ethTokenService.selectAll()); + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/EthWalletController.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/EthWalletController.java new file mode 100644 index 0000000..f697bff --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/EthWalletController.java @@ -0,0 +1,150 @@ +package com.blockchain.server.eth.controller; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.SessionUserDTO; +import com.blockchain.common.base.exception.RPCException; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.eth.common.enums.EthWalletEnums; +import com.blockchain.server.eth.common.exception.EthWalletException; +import com.blockchain.server.eth.controller.api.EthTokenApi; +import com.blockchain.server.eth.controller.api.EthWalletApi; +import com.blockchain.server.eth.entity.EthWalletTransfer; +import com.blockchain.server.eth.feign.UserFeign; +import com.blockchain.server.eth.service.IEthTokenService; +import com.blockchain.server.eth.service.IEthWalletKeyService; +import com.blockchain.server.eth.service.IEthWalletService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.apache.tomcat.jni.User; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.math.BigDecimal; + +/** + * @author YH + * @date 2018年11月6日15:51:56 + */ +@Api(EthWalletApi.MARKET_CONTROLLER_API) +@RestController +@RequestMapping("/wallet") +public class EthWalletController { + @Autowired + IEthWalletService ethWalletService; + @Autowired + IEthWalletKeyService ethWalletKeyService; + @Autowired + RedisTemplate redisTemplate; + @Autowired + private UserFeign userFeign; + + + @ApiOperation(value = EthWalletApi.GetWallet.METHOD_API_NAME, notes = EthWalletApi.GetWallet.METHOD_API_NOTE) + @GetMapping("/getWallet") + public ResultDTO getWallet( + @ApiParam(EthWalletApi.GetWallet.METHOD_API_WALLETTYPE) @RequestParam(name = "walletType", required = + false) String walletType, + @ApiParam(EthWalletApi.GetWallet.METHOD_API_TOKENADDR) @RequestParam(name = "tokenAddr", + required = false) String tokenAddr, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + return ResultDTO.requstSuccess(ethWalletService.selectByUserOpenIdAndTokenAddrAndWalletType(userId, tokenAddr + , walletType)); + } + + @ApiOperation(value = EthWalletApi.GetWallets.METHOD_API_NAME, notes = EthWalletApi.GetWallets.METHOD_API_NOTE) + @GetMapping("/getWallets") + public ResultDTO getWallets( + @ApiParam(EthWalletApi.GetWallets.METHOD_API_WALLETTYPE) @RequestParam(name = "walletType", required = + false) String walletType, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + return ResultDTO.requstSuccess(ethWalletService.selectByUserOpenIdAndWalletType(userId, walletType)); + } + + @ApiOperation(value = EthWalletApi.GetPublicKey.METHOD_API_NAME, notes = EthWalletApi.GetPublicKey.METHOD_API_NOTE) + @GetMapping("/getPublicKey") + public ResultDTO getPublicKey(HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + return ResultDTO.requstSuccess(ethWalletService.getPublicKey(userId)); + } + + @ApiOperation(value = EthWalletApi.CreationWallet.METHOD_API_NAME, notes = + EthWalletApi.CreationWallet.METHOD_API_NOTE) + @PostMapping("/creationWallet") + public ResultDTO creationWallet(@ApiParam(EthWalletApi.CreationWallet.METHOD_API_WALLETTYPE) @RequestParam(name = + "walletType", required = false) String walletType, + @ApiParam(EthWalletApi.CreationWallet.METHOD_API_TOKENADDR) @RequestParam(name = + "tokenAddr", required = false) String tokenAddr, + @ApiParam(EthWalletApi.CreationWallet.METHOD_API_PASSWORD) @RequestParam(name = + "password", required = false) String password, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + return ResultDTO.requstSuccess(ethWalletService.insert(userId, tokenAddr, walletType, password)); + } + + @ApiOperation(value = EthWalletApi.SaveWalletPass.METHOD_API_NAME, notes = + EthWalletApi.SaveWalletPass.METHOD_API_NOTE) + @PostMapping("/saveWalletPass") + public ResultDTO saveWalletPass( + @ApiParam(EthWalletApi.SaveWalletPass.METHOD_API_PASSWORD) @RequestParam(name = "password", required = false) String password, + @ApiParam(EthWalletApi.SaveWalletPass.METHOD_API_CODE) @RequestParam(name = "code", required = false) String code, + HttpServletRequest request) { + SessionUserDTO user = SSOHelper.getUser(redisTemplate, request); + ResultDTO resultDTO = userFeign.validateSmsg(code, user.getTel()); + if (resultDTO == null) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + if (resultDTO.getCode() != BaseConstant.REQUEST_SUCCESS) { + throw new RPCException(resultDTO.getCode(), resultDTO.getMsg()); + } + ethWalletService.updatePassword(user.getId(), password); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = EthWalletApi.ExistsPass.METHOD_API_NAME, notes = EthWalletApi.ExistsPass.METHOD_API_NOTE) + @GetMapping("/existsPass") + public ResultDTO existsPass(HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + boolean existsPass = ethWalletKeyService.existsPass(userId); + return ResultDTO.requstSuccess(existsPass); + } + + @ApiOperation(value = EthWalletApi.WithdrawDeposit.METHOD_API_NAME, notes = + EthWalletApi.WithdrawDeposit.METHOD_API_NOTE) + @PostMapping("/withdrawDeposit") + public ResultDTO withdrawDeposit( + @ApiParam(EthWalletApi.WithdrawDeposit.METHOD_API_WALLETTYPE) @RequestParam(name = "walletType", + required = false) String walletType, + @ApiParam(EthWalletApi.WithdrawDeposit.METHOD_API_TOKENADDR) @RequestParam(name = "tokenAddr", required = + false) String tokenAddr, + @ApiParam(EthWalletApi.WithdrawDeposit.METHOD_API_PASSWORD) @RequestParam(name = "password", required = + false) String password, + @ApiParam(EthWalletApi.WithdrawDeposit.METHOD_API_TOADDDR) @RequestParam(name = "toAddr", required = + false) String toAddr, + @ApiParam(EthWalletApi.WithdrawDeposit.METHOD_API_AMOUNT) @RequestParam(name = "amount", + required = false) String amount, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + EthWalletTransfer tx = ethWalletService.handleWelletOutApply(userId, tokenAddr, toAddr, walletType, amount, + password); + return ResultDTO.requstSuccess(tx); + } + + @ApiOperation(value = EthWalletApi.Transfer.METHOD_API_NAME, notes = EthWalletApi.Transfer.METHOD_API_NOTE) + @PostMapping("/transfer") + public ResultDTO handleTransfer(HttpServletRequest request, + @ApiParam(EthWalletApi.Transfer.METHOD_API_FROMTYPE) @RequestParam("fromType") String fromType, + @ApiParam(EthWalletApi.Transfer.METHOD_API_TOTYPE) @RequestParam("toType") String toType, + @ApiParam(EthWalletApi.Transfer.METHOD_API_COINNAME) @RequestParam("coinName") String coinName, + @ApiParam(EthWalletApi.Transfer.METHOD_API_AMOUNT) @RequestParam("amount") BigDecimal amount) { + String userOpenId = SSOHelper.getUserId(redisTemplate, request); + return ResultDTO.requstSuccess(ethWalletService.handleTransfer(userOpenId,fromType, toType, coinName, amount)); + } + + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/EthWalletTransferController.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/EthWalletTransferController.java new file mode 100644 index 0000000..3c6934b --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/EthWalletTransferController.java @@ -0,0 +1,61 @@ +package com.blockchain.server.eth.controller; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.base.controller.BaseController; +import com.blockchain.server.eth.controller.api.EthTokenApi; +import com.blockchain.server.eth.controller.api.EthWalletApi; +import com.blockchain.server.eth.controller.api.EthWalletTransferApi; +import com.blockchain.server.eth.entity.EthWalletTransfer; +import com.blockchain.server.eth.service.IEthWalletTransferService; +import com.github.pagehelper.PageHelper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +@Api(EthWalletTransferApi.MARKET_CONTROLLER_API) +@RestController +@RequestMapping("/walletTransfer") +public class EthWalletTransferController extends BaseController { + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private IEthWalletTransferService ethWalletTransferService; + + + @GetMapping("/getTransfer") + public ResultDTO getTransfer(HttpServletRequest request, + @ApiParam(EthWalletTransferApi.PAGENUM) @RequestParam(name = "pageNum", defaultValue = BaseConstant.PAGE_DEFAULT_INDEX) Integer pageNum, + @ApiParam(EthWalletTransferApi.PAGESIZE) @RequestParam(name = "pageSize", defaultValue = BaseConstant.PAGE_DEFAULT_SIZE) Integer pageSize, + @ApiParam(EthWalletTransferApi.GetTransfer.METHOD_API_TOKENADDR) @RequestParam(value = "tokenAddr", required = false) String tokenAddr, + @ApiParam(EthWalletTransferApi.GetTransfer.METHOD_API_WALLETTYPE) @RequestParam(value = "walletType", required = false) String walletType) { + String userOpenId = SSOHelper.getUserId(redisTemplate, request); + //分页查询记录 + PageHelper.startPage(pageNum, pageSize); + return ResultDTO.requstSuccess(ethWalletTransferService.selectTransfer(userOpenId, tokenAddr, walletType)); + } + + @GetMapping("/pcGetTransfer") + public ResultDTO pcGetTransfer(HttpServletRequest request, + @ApiParam(EthWalletTransferApi.PAGENUM) @RequestParam(name = "pageNum", defaultValue = BaseConstant.PAGE_DEFAULT_INDEX) Integer pageNum, + @ApiParam(EthWalletTransferApi.PAGESIZE) @RequestParam(name = "pageSize", defaultValue = BaseConstant.PAGE_DEFAULT_SIZE) Integer pageSize, + @ApiParam(EthWalletTransferApi.pcGetTransfer.METHOD_API_TOKENADDR) @RequestParam(value = "tokenAddr", required = false) String tokenAddr, + @ApiParam(EthWalletTransferApi.pcGetTransfer.METHOD_API_WALLETTYPE) @RequestParam(value = "walletType", required = false) String walletType) { + String userOpenId = SSOHelper.getUserId(redisTemplate, request); + //分页查询记录 + PageHelper.startPage(pageNum, pageSize); + List list = ethWalletTransferService.selectTransfer(userOpenId, tokenAddr, walletType); + return generatePage(list); + } +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/ConfigWalletParamApi.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/ConfigWalletParamApi.java new file mode 100644 index 0000000..e1067ba --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/ConfigWalletParamApi.java @@ -0,0 +1,12 @@ +package com.blockchain.server.eth.controller.api; + +public class ConfigWalletParamApi { + public static final String MARKET_CONTROLLER_API = "钱包配置表控制器"; + + public static class GetGasConfig { + public static final String METHOD_API_NAME = "获取币种手续费配置"; + public static final String METHOD_API_NOTE = "获取币种手续费配置"; + public static final String TOKENSYMBOL = "币种名称"; + } + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/EthTokenApi.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/EthTokenApi.java new file mode 100644 index 0000000..54925f1 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/EthTokenApi.java @@ -0,0 +1,16 @@ +package com.blockchain.server.eth.controller.api; + +public class EthTokenApi { + public static final String MARKET_CONTROLLER_API = "以太坊币种信息控制器"; + + public static class FindToken { + public static final String METHOD_API_NAME = "查询以太坊指定的币种信息"; + public static final String METHOD_API_NOTE = "根据币种地址获取以太坊的币种信息"; + public static final String METHOD_API_TOKENADDR = "币种地址"; + } + + public static class SelectTokenAll { + public static final String METHOD_API_NAME = "查询以太坊所有币种信息"; + public static final String METHOD_API_NOTE = "查询以太坊所有币种信息"; + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/EthWalletApi.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/EthWalletApi.java new file mode 100644 index 0000000..7c1ca4d --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/EthWalletApi.java @@ -0,0 +1,71 @@ +package com.blockchain.server.eth.controller.api; + +public class EthWalletApi { + public static final String MARKET_CONTROLLER_API = "以太坊钱包控制器"; + + public static class GetWallet { + public static final String METHOD_API_NAME = "查询指定的钱包信息"; + public static final String METHOD_API_NOTE = "查询指定的钱包信息"; + public static final String METHOD_API_WALLETTYPE = "钱包类型"; + public static final String METHOD_API_TOKENADDR = "币种地址"; + } + + public static class GetWallets { + public static final String METHOD_API_NAME = "查询指定应用的所有钱包信息"; + public static final String METHOD_API_NOTE = "查询指定应用的所有钱包信息"; + public static final String METHOD_API_WALLETTYPE = "钱包类型"; + } + + public static class GetPublicKey { + public static final String METHOD_API_NAME = "获取密码加密的公钥"; + public static final String METHOD_API_NOTE = "获取密码加密的公钥"; + } + + public static class InitWallets { + public static final String METHOD_API_NAME = "初始化所有的钱包"; + public static final String METHOD_API_NOTE = "初始化所有的钱包"; + public static final String METHOD_API_PASSWORD = "钱包密码"; + } + + public static class CreationWallet { + public static final String METHOD_API_NAME = "创建指定的钱包信息"; + public static final String METHOD_API_NOTE = "创建指定的钱包信息"; + public static final String METHOD_API_WALLETTYPE = "钱包类型"; + public static final String METHOD_API_TOKENADDR = "币种地址"; + public static final String METHOD_API_PASSWORD = "待验证的钱包密码"; + } + + + public static class SaveWalletPass { + public static final String METHOD_API_NAME = "修改钱包密码"; + public static final String METHOD_API_NOTE = "修改钱包密码"; + public static final String METHOD_API_PASSWORD = "钱包密码"; + public static final String METHOD_API_CODE = "手机验证码"; + } + + public static class ExistsPass { + public static final String METHOD_API_NAME = "是否设置资金密码"; + public static final String METHOD_API_NOTE = "是否设置资金密码"; + } + + public static class WithdrawDeposit { + public static final String METHOD_API_NAME = "提现"; + public static final String METHOD_API_NOTE = "提现"; + public static final String METHOD_API_PASSWORD = "钱包密码"; + public static final String METHOD_API_WALLETTYPE = "钱包类型"; + public static final String METHOD_API_TOKENADDR = "币种地址"; + public static final String METHOD_API_TOADDDR = "提现收款地址"; + public static final String METHOD_API_AMOUNT = "提现金额"; + } + + public static class Transfer { + + public static final String METHOD_API_NAME = "钱包划转"; + public static final String METHOD_API_NOTE = "钱包划转"; + public static final String METHOD_API_FROMTYPE = "支付方钱包类型"; + public static final String METHOD_API_TOTYPE = "收款钱包类型"; + public static final String METHOD_API_COINNAME = "划转币种的名称"; + public static final String METHOD_API_AMOUNT = "金额"; + } + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/EthWalletTransferApi.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/EthWalletTransferApi.java new file mode 100644 index 0000000..156b2d5 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/controller/api/EthWalletTransferApi.java @@ -0,0 +1,17 @@ +package com.blockchain.server.eth.controller.api; + +public class EthWalletTransferApi { + public static final String MARKET_CONTROLLER_API = "以太坊钱包控制器"; + public static final String PAGENUM = "当前页数"; + public static final String PAGESIZE = "页数展示条数"; + + public static class GetTransfer { + public static final String METHOD_API_WALLETTYPE = "钱包类型"; + public static final String METHOD_API_TOKENADDR = "币种地址"; + } + + public static class pcGetTransfer { + public static final String METHOD_API_WALLETTYPE = "钱包类型"; + public static final String METHOD_API_TOKENADDR = "币种地址"; + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/EthGasDTO.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/EthGasDTO.java new file mode 100644 index 0000000..57c7238 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/EthGasDTO.java @@ -0,0 +1,26 @@ +package com.blockchain.server.eth.dto; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * EthServiceChargeDTO 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Data +public class EthGasDTO { + private String gasPrice; + private String gasTokenAddr; + private String gasTokenSymbol; + private String gasTokenName; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/EthWalletDTO.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/EthWalletDTO.java new file mode 100644 index 0000000..79791eb --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/EthWalletDTO.java @@ -0,0 +1,30 @@ +package com.blockchain.server.eth.dto; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Table; + +/** + * EthWallet 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Data +public class EthWalletDTO { + private String addr; + private String tokenAddr; + private String userOpenId; + private String tokenSymbol; + private int tokenDecimals; + private String balance; + private String freeBalance; + private String freezeBalance; + private String walletType; + private java.util.Date createTime; + private java.util.Date updateTime; +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/EthWalletTransferDTO.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/EthWalletTransferDTO.java new file mode 100644 index 0000000..7c0ce25 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/EthWalletTransferDTO.java @@ -0,0 +1,38 @@ +package com.blockchain.server.eth.dto; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * EthWalletTransfer 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Data +public class EthWalletTransferDTO { + private String id; + private String hash; + private String fromAddr; + private String toAddr; + private String amount; + private String tokenAddr; + private String tokenSymbol; + private String gasPrice; + private String gasTokenType; + private String gasTokenName; + private String gasTokenSymbol; + private String transferType; + private Integer status; + private String remark; + private java.util.Date createTime; + private java.util.Date updateTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/Web3jTransferDTO.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/Web3jTransferDTO.java new file mode 100644 index 0000000..7303ff2 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/Web3jTransferDTO.java @@ -0,0 +1,19 @@ +package com.blockchain.server.eth.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.math.BigInteger; + +@Data +public class Web3jTransferDTO { + String hash; // 转账生成的标识hash值 + String fromAddr; // 支付方钱包地址 + String toAddr; // 收款方钱包地址 + String tokenAddr; // 代币地址 + BigDecimal gasPrice; //旷工费用[需除以18位小数] + BigDecimal amount; //转账金额[需除以代币小数位数] + Boolean state; //交易状态 +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/Web3jWalletDTO.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/Web3jWalletDTO.java new file mode 100644 index 0000000..19435e5 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/dto/Web3jWalletDTO.java @@ -0,0 +1,10 @@ +package com.blockchain.server.eth.dto; + +import lombok.Data; + +@Data +public class Web3jWalletDTO { + private String addr; + private String privateKey; + private String keystore; +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/ConfigWalletParam.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/ConfigWalletParam.java new file mode 100644 index 0000000..5792185 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/ConfigWalletParam.java @@ -0,0 +1,34 @@ +package com.blockchain.server.eth.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Table; + +/** + * EthApplication 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Table(name = "config_wallet_param") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ConfigWalletParam extends BaseModel { + @Column(name = "id") + private String id; + @Column(name = "module_type") + private String moduleType; + @Column(name = "param_name") + private String paramName; + @Column(name = "param_value") + private String paramValue; + @Column(name = "param_descr") + private String paramDescr; + @Column(name = "status") + private Integer status; +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthApplication.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthApplication.java new file mode 100644 index 0000000..9f1d491 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthApplication.java @@ -0,0 +1,26 @@ +package com.blockchain.server.eth.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Table; + +/** + * EthApplication 数据传输类 + * @date 2019-02-16 15:44:06 + * @version 1.0 + */ +@Table(name = "dapp_eth_application") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class EthApplication extends BaseModel { + @Column(name = "app_id") + private String appId; + @Column(name = "app_name") + private String appName; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthBlockNumber.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthBlockNumber.java new file mode 100644 index 0000000..0ebc8ca --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthBlockNumber.java @@ -0,0 +1,33 @@ +package com.blockchain.server.eth.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigInteger; + +/** + * EthBlockNumber 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Table(name = "dapp_eth_block_number") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class EthBlockNumber extends BaseModel { + @Id + @Column(name = "block_number") + private BigInteger blockNumber; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "update_time") + private java.util.Date updateTime; + @Column(name = "status") + private char status; +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthGasWallet.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthGasWallet.java new file mode 100644 index 0000000..ce412bd --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthGasWallet.java @@ -0,0 +1,32 @@ +package com.blockchain.server.eth.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * EthWalletKey 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Table(name = "dapp_eth_gas_wallet") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class EthGasWallet extends BaseModel { + @Id + @Column(name = "addr") + private String addr; + @Column(name = "private_key") + private String privateKey; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "update_time") + private java.util.Date updateTime; +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthToken.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthToken.java new file mode 100644 index 0000000..d7d00e0 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthToken.java @@ -0,0 +1,43 @@ +package com.blockchain.server.eth.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * EthToken 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Table(name = "dapp_eth_token") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class EthToken extends BaseModel { + @Id + @Column(name = "token_addr") + private String tokenAddr; + @Column(name = "token_symbol") + private String tokenSymbol; + @Column(name = "token_decimals") + private Integer tokenDecimals; + @Column(name = "issue_time") + private java.util.Date issueTime; + @Column(name = "total_supply") + private String totalSupply; + @Column(name = "total_circulation") + private String totalCirculation; + @Column(name = "descr") + private String descr; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "update_time") + private java.util.Date updateTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthTransferAuditing.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthTransferAuditing.java new file mode 100644 index 0000000..fede69a --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthTransferAuditing.java @@ -0,0 +1,34 @@ +package com.blockchain.server.eth.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Table; + +/** + * EthTransferAuditing 数据传输类 + * @date 2019-02-16 15:44:06 + * @version 1.0 + */ +@Table(name = "dapp_eth_transfer_auditing") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class EthTransferAuditing extends BaseModel { + @Column(name = "id") + private String id; + @Column(name = "sys_user_id") + private String sysUserId; + @Column(name = "ip_addr") + private String ipAddr; + @Column(name = "transfer_id") + private String transferId; + @Column(name = "transfer_status") + private String transferStatus; + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWallet.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWallet.java new file mode 100644 index 0000000..78658f7 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWallet.java @@ -0,0 +1,45 @@ +package com.blockchain.server.eth.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * EthWallet 数据传输类 + * @date 2019-02-16 15:44:06 + * @version 1.0 + */ +@Table(name = "dapp_eth_wallet") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class EthWallet extends BaseModel { + @Column(name = "addr") + private String addr; + @Column(name = "token_addr") + private String tokenAddr; + @Column(name = "user_open_id") + private String userOpenId; + @Column(name = "token_symbol") + private String tokenSymbol; + @Column(name = "token_decimals") + private Integer tokenDecimals; + @Column(name = "balance") + private BigDecimal balance; + @Column(name = "free_balance") + private BigDecimal freeBalance; + @Column(name = "freeze_balance") + private BigDecimal freezeBalance; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "update_time") + private java.util.Date updateTime; + @Column(name = "wallet_type") + private String walletType; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWalletKey.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWalletKey.java new file mode 100644 index 0000000..95524a9 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWalletKey.java @@ -0,0 +1,36 @@ +package com.blockchain.server.eth.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * EthWalletKey 数据传输类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Table(name = "dapp_eth_wallet_key") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class EthWalletKey extends BaseModel { + @Id + @Column(name = "user_open_id") + private String userOpenId; + @Column(name = "addr") + private String addr; + @Column(name = "private_key") + private String privateKey; + @Column(name = "keystore") + private String keystore; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "update_time") + private java.util.Date updateTime; +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWalletOut.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWalletOut.java new file mode 100644 index 0000000..97250fa --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWalletOut.java @@ -0,0 +1,36 @@ +package com.blockchain.server.eth.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Table; + +/** + * EthWalletOut 数据传输类 + * @date 2019-02-16 15:44:06 + * @version 1.0 + */ +@Table(name = "dapp_eth_wallet_out") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class EthWalletOut extends BaseModel { + @Column(name = "id") + private String id; + @Column(name = "addr") + private String addr; + @Column(name = "token_addr") + private String tokenAddr; + @Column(name = "token_symbol") + private String tokenSymbol; + @Column(name = "token_decimals") + private Integer tokenDecimals; + @Column(name = "keystore") + private String keystore; + @Column(name = "remark") + private String remark; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWalletTransfer.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWalletTransfer.java new file mode 100644 index 0000000..5a63846 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/entity/EthWalletTransfer.java @@ -0,0 +1,57 @@ +package com.blockchain.server.eth.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * EthWalletTransfer 数据传输类 + * @date 2019-02-16 15:44:06 + * @version 1.0 + */ +@Table(name = "dapp_eth_wallet_transfer") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class EthWalletTransfer extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "hash") + private String hash; + @Column(name = "from_addr") + private String fromAddr; + @Column(name = "to_addr") + private String toAddr; + @Column(name = "amount") + private BigDecimal amount; + @Column(name = "token_addr") + private String tokenAddr; + @Column(name = "token_symbol") + private String tokenSymbol; + @Column(name = "gas_price") + private BigDecimal gasPrice; + @Column(name = "gas_token_type") + private String gasTokenType; + @Column(name = "gas_token_name") + private String gasTokenName; + @Column(name = "gas_token_symbol") + private String gasTokenSymbol; + @Column(name = "transfer_type") + private String transferType; + @Column(name = "status") + private Integer status; + @Column(name = "remark") + private String remark; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "update_time") + private java.util.Date updateTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/feign/UserFeign.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/feign/UserFeign.java new file mode 100644 index 0000000..654eb4f --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/feign/UserFeign.java @@ -0,0 +1,42 @@ +package com.blockchain.server.eth.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.eth.feign.api.UserInnerApi; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author huangxl + * @create 2019-03-27 16:19 + */ +@FeignClient("dapp-user-server") +public interface UserFeign { + + String CONTENT_PATH = "/inner"; + + @ApiOperation(value = UserInnerApi.ValidateSmsg.METHOD_TITLE_NAME, notes = UserInnerApi.ValidateSmsg.METHOD_TITLE_NOTE) + @PostMapping(CONTENT_PATH + "/validateSmsg") + ResultDTO validateSmsg(@ApiParam(UserInnerApi.ValidateSmsg.METHOD_API_VERIFYCODE) @RequestParam("verifyCode") String verifyCode, + @ApiParam(UserInnerApi.ValidateSmsg.METHOD_API_PHONE) @RequestParam("phone") String phone); + + + /** + * 禁止提现 + * @param userId + * @return + */ + @PostMapping("/inner/verifyBanWithdraw") + ResultDTO verifyBanWithdraw(@RequestParam("userId") String userId); + + /** + * 免提现手续费 + * @param userOpenId + * @return + */ + @PostMapping("/inner/verifyFreeWithdraw") + ResultDTO verifyFreeWithdraw(@RequestParam("userId") String userOpenId); + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/feign/api/UserInnerApi.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/feign/api/UserInnerApi.java new file mode 100644 index 0000000..ee64d29 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/feign/api/UserInnerApi.java @@ -0,0 +1,12 @@ +package com.blockchain.server.eth.feign.api; + +public class UserInnerApi { + public static final String USER_INNER_API = "用户服务接口"; + public static class ValidateSmsg { + public static final String METHOD_TITLE_NAME = "验证修改资金密码短信验证码"; + public static final String METHOD_TITLE_NOTE = "验证短信验证码"; + public static final String METHOD_API_PHONE = "手机号"; + public static final String METHOD_API_VERIFYCODE = "验证码"; + } + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/EthWalletInner.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/EthWalletInner.java new file mode 100644 index 0000000..2d166b6 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/EthWalletInner.java @@ -0,0 +1,63 @@ +package com.blockchain.server.eth.inner; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.SessionUserDTO; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.eth.common.enums.EthWalletEnums; +import com.blockchain.server.eth.common.exception.EthWalletException; +import com.blockchain.server.eth.common.util.RedisPrivateUtil; +import com.blockchain.server.eth.inner.api.EthWalletApi; +import com.blockchain.server.eth.service.IEthWalletKeyService; +import com.blockchain.server.eth.service.IEthWalletService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.Date; + +/** + * @author YH + * @date 2018年11月6日15:51:56 + */ +@Api(EthWalletApi.MARKET_CONTROLLER_API) +@RestController +@RequestMapping("/inner/wallet") +public class EthWalletInner { + private static final Logger LOG = LoggerFactory.getLogger(EthWalletInner.class); + @Autowired + IEthWalletService ethWalletService; + @Autowired + RedisTemplate redisTemplate; + + @ApiOperation(value = EthWalletApi.IsPassword.METHOD_API_NAME, notes = EthWalletApi.IsPassword.METHOD_API_NOTE) + @GetMapping("/isPassword") + public ResultDTO isPassword(@ApiParam(EthWalletApi.IsPassword.METHOD_API_PASSWORD) @RequestParam(name = "password", required = false) String password, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + LOG.info("校验密码,密码:" + password); + ethWalletService.isPassword(userId, password); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = EthWalletApi.GetPublicKey.METHOD_API_NAME, notes = EthWalletApi.GetPublicKey.METHOD_API_NOTE) + @GetMapping("/getPublicKey") + public ResultDTO getPublicKey(@ApiParam(EthWalletApi.GetPublicKey.METHOD_API_USERID) @RequestParam("userOpenId") String userOpenId, HttpServletRequest request) { + LOG.info("获取公钥密码,userOpenId:" + userOpenId); + return ResultDTO.requstSuccess(ethWalletService.getPublicKey(userOpenId)); + } + + @ApiOperation(value = EthWalletApi.InitWallets.METHOD_API_NAME, notes = EthWalletApi.InitWallets.METHOD_API_NOTE) + @GetMapping("/initWallets") + public ResultDTO initWallets(@ApiParam(EthWalletApi.InitWallets.METHOD_API_USERID) @RequestParam("userOpenId") String userOpenId) { + LOG.info("获取公钥密码,userOpenId:" + userOpenId); + ethWalletService.insertInit(userOpenId); + return ResultDTO.requstSuccess(); + } + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/EthWalletTransferInner.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/EthWalletTransferInner.java new file mode 100644 index 0000000..9b61d05 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/EthWalletTransferInner.java @@ -0,0 +1,51 @@ +package com.blockchain.server.eth.inner; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import com.blockchain.server.eth.inner.api.EthWalletTransferApi; +import com.blockchain.server.eth.service.IEthWalletService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + + +/** + * @author YH + * @date 2018年11月6日15:51:56 + */ +@Api(EthWalletTransferApi.MARKET_CONTROLLER_API) +@RestController +@RequestMapping("/inner/walletTx") +public class EthWalletTransferInner { + private static final Logger LOG = LoggerFactory.getLogger(EthWalletTransferInner.class); + @Autowired + IEthWalletService ethWalletService; + @Autowired + RedisTemplate redisTemplate; + + @ApiOperation(value = EthWalletTransferApi.Order.METHOD_API_NAME, notes = + EthWalletTransferApi.Order.METHOD_API_NOTE) + @PostMapping("/order") + public ResultDTO order(@ApiParam(EthWalletTransferApi.Order.METHOD_API_ORDERDTO) @RequestBody WalletOrderDTO orderDTO) { + LOG.info("冻结、解冻余额,orderDTO:" + orderDTO.toString()); + ethWalletService.updateBlanceTransform(orderDTO); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = EthWalletTransferApi.Change.METHOD_API_NAME, notes = + EthWalletTransferApi.Change.METHOD_API_NOTE) + @PostMapping("/change") + public ResultDTO change(@ApiParam(EthWalletTransferApi.Change.METHOD_API_CHANGEDTO) @RequestBody WalletChangeDTO changeDTO) { + LOG.info("扣除、增加,changeDTO:" + changeDTO.toString()); + ethWalletService.updateBlance(changeDTO); + return ResultDTO.requstSuccess(); + } + + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/api/EthWalletApi.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/api/EthWalletApi.java new file mode 100644 index 0000000..9c53052 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/api/EthWalletApi.java @@ -0,0 +1,39 @@ +package com.blockchain.server.eth.inner.api; + +public class EthWalletApi { + public static final String MARKET_CONTROLLER_API = "以太坊钱包控制器"; + + public static class GetPublicKey { + public static final String METHOD_API_NAME = "获取密码加密的公钥"; + public static final String METHOD_API_NOTE = "获取密码加密的公钥"; + public static final String METHOD_API_USERID = "用户标识"; + } + + public static class IsPassword { + public static final String METHOD_API_NAME = "验证钱包密码"; + public static final String METHOD_API_NOTE = "验证钱包密码"; + public static final String METHOD_API_PASSWORD = "钱包密码"; + } + + public static class InitWallets { + public static final String METHOD_API_NAME = "初始化所有的钱包"; + public static final String METHOD_API_NOTE = "初始化所有的钱包"; + public static final String METHOD_API_USERID = "用户标识"; + } + + public static class CreationWallet { + public static final String METHOD_API_NAME = "创建指定的钱包信息"; + public static final String METHOD_API_NOTE = "创建指定的钱包信息"; + public static final String METHOD_API_WALLETTYPE = "钱包类型"; + public static final String METHOD_API_TOKENADDR = "币种地址"; + public static final String METHOD_API_PASSWORD = "待验证的钱包密码"; + } + + + public static class SaveWalletPass { + public static final String METHOD_API_NAME = "修改钱包密码"; + public static final String METHOD_API_NOTE = "修改钱包密码"; + public static final String METHOD_API_PASSWORD = "钱包密码"; + public static final String METHOD_API_CODE = "手机验证码"; + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/api/EthWalletTransferApi.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/api/EthWalletTransferApi.java new file mode 100644 index 0000000..6acc4d3 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/inner/api/EthWalletTransferApi.java @@ -0,0 +1,17 @@ +package com.blockchain.server.eth.inner.api; + +public class EthWalletTransferApi { + public static final String MARKET_CONTROLLER_API = "以太坊钱包转账控制器"; + + public static class Order { + public static final String METHOD_API_NAME = "下单与撤单的接口"; + public static final String METHOD_API_NOTE = "下单与撤单的接口"; + public static final String METHOD_API_ORDERDTO = "参数对象"; + } + + public static class Change { + public static final String METHOD_API_NAME = "余额变动的接口"; + public static final String METHOD_API_NOTE = "余额变动的接口"; + public static final String METHOD_API_CHANGEDTO = "参数对象"; + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/ConfigWalletParamMapper.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/ConfigWalletParamMapper.java new file mode 100644 index 0000000..1f515d5 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/ConfigWalletParamMapper.java @@ -0,0 +1,16 @@ +package com.blockchain.server.eth.mapper; + +import com.blockchain.server.eth.entity.ConfigWalletParam; +import com.blockchain.server.eth.entity.EthWalletOut; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * ConfigWalletParam 数据访问类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Repository +public interface ConfigWalletParamMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthApplicationMapper.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthApplicationMapper.java new file mode 100644 index 0000000..9411a97 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthApplicationMapper.java @@ -0,0 +1,14 @@ +package com.blockchain.server.eth.mapper; + +import com.blockchain.server.eth.entity.EthApplication; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * EthApplicationMapper 数据访问类 + * @date 2019-02-16 15:44:06 + * @version 1.0 + */ +@Repository +public interface EthApplicationMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthBlockNumberMapper.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthBlockNumberMapper.java new file mode 100644 index 0000000..da32605 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthBlockNumberMapper.java @@ -0,0 +1,30 @@ +package com.blockchain.server.eth.mapper; + +import com.blockchain.server.eth.entity.EthBlockNumber; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.math.BigInteger; + +/** + * EthBlockNumberMapper 数据访问类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Repository +public interface EthBlockNumberMapper extends Mapper { + /** + * 查询最大的区块号 + * + * @return + */ + BigInteger selectMaxBlockNumber(); + + /** + * 查询最小的区块号 + * + * @return + */ + BigInteger selectMinBlockNumber(); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthGasWalletMapper.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthGasWalletMapper.java new file mode 100644 index 0000000..50066e4 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthGasWalletMapper.java @@ -0,0 +1,18 @@ +package com.blockchain.server.eth.mapper; + +import com.blockchain.server.eth.entity.EthGasWallet; +import com.blockchain.server.eth.entity.EthWalletKey; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.Set; + +/** + * EthWalletKeyMapper 数据访问类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Repository +public interface EthGasWalletMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthTokenMapper.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthTokenMapper.java new file mode 100644 index 0000000..9d7d585 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthTokenMapper.java @@ -0,0 +1,22 @@ +package com.blockchain.server.eth.mapper; + +import com.blockchain.server.eth.entity.EthToken; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.Set; + +/** + * EthTokenMapper 数据访问类 + * @date 2019-02-16 15:44:06 + * @version 1.0 + */ +@Repository +public interface EthTokenMapper extends Mapper { + /** + * 获取所有的币种地址 + * + * @return + */ + Set selectAllTokenAddr(); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthTransferAuditingMapper.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthTransferAuditingMapper.java new file mode 100644 index 0000000..7a77a96 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthTransferAuditingMapper.java @@ -0,0 +1,14 @@ +package com.blockchain.server.eth.mapper; + +import com.blockchain.server.eth.entity.EthTransferAuditing; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * EthTransferAuditingMapper 数据访问类 + * @date 2019-02-16 15:44:06 + * @version 1.0 + */ +@Repository +public interface EthTransferAuditingMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletKeyMapper.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletKeyMapper.java new file mode 100644 index 0000000..458e53a --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletKeyMapper.java @@ -0,0 +1,26 @@ +package com.blockchain.server.eth.mapper; + +import com.blockchain.server.eth.entity.EthWalletKey; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.Set; + +/** + * EthWalletKeyMapper 数据访问类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Repository +public interface EthWalletKeyMapper extends Mapper { + /** + * 获取所有用户钱包地址 + * + * @return + */ + Set selectAllAddrs(); + + int update(@Param("ethWalletKey") EthWalletKey ethWalletKey); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletMapper.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletMapper.java new file mode 100644 index 0000000..8b19c47 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletMapper.java @@ -0,0 +1,117 @@ +package com.blockchain.server.eth.mapper; + +import com.blockchain.server.eth.dto.EthWalletDTO; +import com.blockchain.server.eth.entity.EthWallet; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import org.springframework.web.bind.annotation.PostMapping; +import tk.mybatis.mapper.common.Mapper; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * EthWalletMapper 数据访问类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Repository +public interface EthWalletMapper extends Mapper { + + /** + * 根据钱包地址、币种地址、钱包类型查询钱包信息 + * + * @param addr 钱包地址 + * @param tokenAddr 币种地址 + * @return + */ + EthWalletDTO selectByAddrAndTokenAddr(@Param("addr") String addr, + @Param("tokenAddr") String tokenAddr); + + /** + * 根据钱包地址、币种地址、钱包类型查询钱包信息 + * + * @param addr 钱包地址 + * @param tokenAddr 币种地址 + * @param walletType 钱包类型 + * @return + */ + EthWalletDTO selectByAddrAndTokenAddrAndWalletType(@Param("addr") String addr, + @Param("tokenAddr") String tokenAddr, + @Param("walletType") String walletType); + + /** + * 根据用户ID、币种地址、钱包类型查询钱包信息 + * + * @param userOpenId 用户ID + * @param tokenAddr 币种地址 + * @param walletType 钱包类型 + * @return + */ + EthWalletDTO selectByUserOpenIdAndTokenAddrAndWalletType(@Param("userOpenId") String userOpenId, + @Param("tokenAddr") String tokenAddr, + @Param("walletType") String walletType); + + /** + * 根据钱包地址、钱包类型查询钱包信息 + * + * @param addr 钱包地址 + * @param walletType 钱包类型 + * @return + */ + List selectByAddrAndWalletType(@Param("addr") String addr, + @Param("walletType") String walletType); + + /** + * 根据用户ID、钱包类型查询钱包信息 + * + * @param userOpenId 用户ID + * @param walletType 钱包类型 + * @return + */ + List selectByUserOpenIdAndWalletType(@Param("userOpenId") String userOpenId, + @Param("walletType") String walletType); + + + /** + * 修改用户指定钱包的金额 + * + * @param userOpenId 用户标识 + * @param tokenAddr 币种地址 + * @param walletType 应用钱包 + * @param balance 总金额 + * @param freeBalance 可用金额 + * @param freezeBalance 冻结金额 + * @return + */ + int updateBalanceByUserIdInRowLock(@Param("userOpenId") String userOpenId, + @Param("tokenAddr") String tokenAddr, + @Param("walletType") String walletType, + @Param("balance") BigDecimal balance, + @Param("freeBalance") BigDecimal freeBalance, + @Param("freezeBalance") BigDecimal freezeBalance, + @Param("updateTime") Date updateTime); + + /** + * 修改用户指定钱包的金额 + * + * @param addr 钱包地址 + * @param tokenAddr 币种地址 + * @param walletType 应用钱包 + * @param balance 总金额 + * @param freeBalance 可用金额 + * @param freezeBalance 冻结金额 + * @return + */ + int updateBalanceByAddrInRowLock(@Param("addr") String addr, + @Param("tokenAddr") String tokenAddr, + @Param("walletType") String walletType, + @Param("balance") BigDecimal balance, + @Param("freeBalance") BigDecimal freeBalance, + @Param("freezeBalance") BigDecimal freezeBalance, + @Param("updateTime") Date updateTime); + + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletOutMapper.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletOutMapper.java new file mode 100644 index 0000000..8b5ee06 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletOutMapper.java @@ -0,0 +1,14 @@ +package com.blockchain.server.eth.mapper; + +import com.blockchain.server.eth.entity.EthWalletOut; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * EthWalletOutMapper 数据访问类 + * @date 2019-02-16 15:44:06 + * @version 1.0 + */ +@Repository +public interface EthWalletOutMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletTransferMapper.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletTransferMapper.java new file mode 100644 index 0000000..cd1f3ac --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/mapper/EthWalletTransferMapper.java @@ -0,0 +1,29 @@ +package com.blockchain.server.eth.mapper; + +import com.blockchain.server.eth.entity.EthWalletTransfer; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * EthWalletTransferMapper 数据访问类 + * + * @version 1.0 + * @date 2019-02-16 15:44:06 + */ +@Repository +public interface EthWalletTransferMapper extends Mapper { + + /** + * 查询钱包记录信息 + * + * @param addr + * @param tokenAddr + * @return + */ + List selectTransfer(@Param("addr") String addr, + @Param("tokenAddr") String tokenAddr); + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/scheduleTask/EthTxInTimerTask.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/scheduleTask/EthTxInTimerTask.java new file mode 100644 index 0000000..5579477 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/scheduleTask/EthTxInTimerTask.java @@ -0,0 +1,141 @@ +package com.blockchain.server.eth.scheduleTask; + +import com.blockchain.server.eth.common.constants.EthWalletConstants; +import com.blockchain.server.eth.common.util.RedisBlockUtil; +import com.blockchain.server.eth.entity.EthBlockNumber; +import com.blockchain.server.eth.entity.EthToken; +import com.blockchain.server.eth.service.IEthBlockNumberService; +import com.blockchain.server.eth.service.IEthTokenService; +import com.blockchain.server.eth.service.IEthWalletKeyService; +import com.blockchain.server.eth.web3j.IWalletWeb3j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.math.BigInteger; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * 充值记录爬取的定时器 + */ +//@Component +public class EthTxInTimerTask { + + + static ExecutorService singleThreadExecutorNormal = Executors.newSingleThreadExecutor(); + + static ExecutorService singleThreadExecutorOmit = Executors.newSingleThreadExecutor(); + // 日志 + static final Logger LOG = LoggerFactory.getLogger(EthTxInTimerTask.class); + // 区块与当前爬取区块的偏差量 + static final BigInteger BIAS_CURRENT = BigInteger.valueOf(20); + // 规定遗漏爬取区块的时间差 + static final long TIME_DIFFERENCE = 60; + + @Autowired + IEthBlockNumberService ethBlockNumberService; + @Autowired + IWalletWeb3j walletWeb3j; + @Autowired + IEthWalletKeyService ethWalletKeyService; + @Autowired + IEthTokenService ethTokenService; + + @Autowired + RedisTemplate redisTemplate; + + + /** + * 充值记录爬取 + */ + @Scheduled(cron = "0/10 * * * * ?") + public void crawlTxIn() { + singleThreadExecutorNormal.execute(new Runnable() { + @Override + public void run() { + crawlTxInDispose(); // 爬取充值记录 + } + }); + } + + /** + * 查询以太坊遗漏的以太坊数据 + */ + @Scheduled(cron = "0/30 * * * * ?") + public void crawlOmitTxIn() { + singleThreadExecutorOmit.execute(new Runnable() { + @Override + public void run() { + crawlOmitTxInDispose(); // 爬取遗漏数据的业务逻辑 + } + }); + } + + + /** + * 充值记录爬取处理方法 + */ + void crawlTxInDispose() { + BigInteger newestBlock = ethBlockNumberService.selectNewest(); // 获取最新区块 + BigInteger currentBlock = ethBlockNumberService.selectCurrent(); //当前区块 + // 缓冲区块差为10个,不足退出 + if (newestBlock.compareTo(currentBlock.add(BIAS_CURRENT)) < 0) return; + Map coinAddrs = ethTokenService.selectMaps(); // 获取所有币种地址 + Set adds = ethWalletKeyService.selectAddrs(); // 获取所有用户钱包地址 + // 遍历爬取区块 + for (int i = 0; i < 10; i++) { + currentBlock = currentBlock.add(BigInteger.ONE); + try { + // 插入当前区块号 + ethBlockNumberService.insert(currentBlock, EthWalletConstants.StatusType.BLOCK_WAIT); + // 插入区块号的充值数据,修改区块状态 + ethBlockNumberService.handleBlockTxIn(currentBlock, coinAddrs, adds); + } catch (Exception e) { + } + RedisBlockUtil.setBlockCurrent(currentBlock, redisTemplate); + } + } + + /** + * 充值记录遗漏爬取的处理方法 + */ + void crawlOmitTxInDispose() { + Map coinAddrs = ethTokenService.selectMaps(); // 获取所有币种地址 + Set adds = ethWalletKeyService.selectAddrs(); // 获取所有用户钱包地址 + + BigInteger currentBlock = ethBlockNumberService.selectCurrent(); // 当前区块 + // 获取当前区块的创建时间 + long currentBlockTime = ethBlockNumberService.selectByBlockNumber(currentBlock).getCreateTime().getTime(); + List blockWaits = ethBlockNumberService.selectWaitBlocks(); // 获取等待区块列表 + for (EthBlockNumber row : blockWaits) { + long rowTime = row.getCreateTime().getTime(); // 获取等待区块的时间 + long diffTime = (currentBlockTime - rowTime) / 1000; // 获取时间差 + if (diffTime <= TIME_DIFFERENCE) return; // 区块时间未超过一分钟,跳出处理 + int count = RedisBlockUtil.getBlockOptMapCount(row.getBlockNumber(), redisTemplate); // 获取爬取次数 + if (count >= 5) { // 爬取次数超过五次跳出 + RedisBlockUtil.delBlockOptMapCount(row.getBlockNumber(), redisTemplate); + ethBlockNumberService.updateByBlockNumber(row.getBlockNumber(), + EthWalletConstants.StatusType.BLOCK_ERROR); + return; + } + // 爬取次数 + 1 + RedisBlockUtil.incrementBlockOptMapCount(row.getBlockNumber(), redisTemplate); + try { + // 插入区块号的充值数据,修改区块状态 + ethBlockNumberService.handleBlockTxIn(row.getBlockNumber(), coinAddrs, adds); + RedisBlockUtil.delBlockOptMapCount(row.getBlockNumber(), redisTemplate); + } catch (Exception e) { + } + } + + } + + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/scheduleTask/EthWallertTimerTask.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/scheduleTask/EthWallertTimerTask.java new file mode 100644 index 0000000..84a6500 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/scheduleTask/EthWallertTimerTask.java @@ -0,0 +1,116 @@ +package com.blockchain.server.eth.scheduleTask; + +import com.blockchain.server.eth.common.constants.EthWalletConstants; +import com.blockchain.server.eth.common.util.RedisBlockUtil; +import com.blockchain.server.eth.entity.EthToken; +import com.blockchain.server.eth.entity.EthWalletTransfer; +import com.blockchain.server.eth.service.*; +import com.blockchain.server.eth.web3j.IWalletWeb3j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.web3j.protocol.core.methods.response.TransactionReceipt; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * 钱包业务处理的定时器 + */ +//@Component +public class EthWallertTimerTask { + + // 钱包充值处理 + private static ExecutorService txInExecutorService = Executors.newSingleThreadExecutor(); + // 钱包提现处理 + private static ExecutorService txOutExecutorService = Executors.newSingleThreadExecutor(); + // 日志 + private static final Logger LOG = LoggerFactory.getLogger(EthWallertTimerTask.class); + + @Autowired + IEthWalletService ethWalletService; + @Autowired + IEthWalletTransferService ethWalletTransferService; + @Autowired + IWalletWeb3j walletWeb3j; + + + @Scheduled(cron = "0/10 * * * * ?") + public void crawlTxIn() { + txInExecutorService.execute(new Runnable() { + @Override + public void run() { + crawlTxInDispose(); // 爬取充值记录 + } + }); + } + + @Scheduled(cron = "0/10 * * * * ?") + public void crawlTxOut() { + txOutExecutorService.execute(new Runnable() { + @Override + public void run() { + crawlTxOutDispose(); // 爬取提现记录 + } + }); + } + + void crawlTxInDispose() { + // 查询充值的未处理的数据 + List txs = + ethWalletTransferService.selectByTxTypeAndStatus(EthWalletConstants.TransferType.IN, + EthWalletConstants.StatusType.IN_LOAD, 0, 99); + // 查询记录打包状态 + for (EthWalletTransfer tx : txs) { + try { + tx = ethWalletTransferService.updateGas(tx); // 修改记录的旷工费用 + if (tx.getStatus() == EthWalletConstants.StatusType.IN_SUCCESS) { + // 修改余额改变成功状态 + ethWalletService.updateBlanceTxIn(tx); + } else if (tx.getStatus() == EthWalletConstants.StatusType.IN_ERROR) { + // 修改失败状态 + ethWalletTransferService.updateStatus(tx); + } + } catch (Exception e) { + e.printStackTrace(); + continue; + } + } + + } + + void crawlTxOutDispose() { + // 查询提现的未处理的数据 + List txs = + ethWalletTransferService.selectByTxTypeAndStatus(EthWalletConstants.TransferType.OUT, + EthWalletConstants.StatusType.OUT_LOAD4, 0, 99); + // 查询记录打包状态 + for (EthWalletTransfer tx : txs) { + try { + tx = ethWalletTransferService.updateGas(tx); // 修改记录的旷工费用 + if (tx.getStatus() == EthWalletConstants.StatusType.IN_ERROR) { + // 修改失败状态 + ethWalletService.updateTxOutError(tx); + } else if (tx.getStatus() == EthWalletConstants.StatusType.IN_SUCCESS) { + // 修改余额改变成功状态 + ethWalletService.updateTxOutSuccess(tx); + } + } catch (Exception e) { + e.printStackTrace(); + continue; + } + } + + } + + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IConfigWalletParamService.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IConfigWalletParamService.java new file mode 100644 index 0000000..8aa83e1 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IConfigWalletParamService.java @@ -0,0 +1,21 @@ +package com.blockchain.server.eth.service; + +import com.blockchain.common.base.dto.GasDTO; +import com.blockchain.server.eth.dto.EthGasDTO; + +/** + * 配置表——业务接口 + * + * @author YH + * @date 2019年2月16日17:09:19 + */ +public interface IConfigWalletParamService { + /** + * 获取手续费详情 + * + * @param tokenSymbol 币种 + * @return + */ + GasDTO getGasConfig(String tokenSymbol); + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthApplicationService.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthApplicationService.java new file mode 100644 index 0000000..1694393 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthApplicationService.java @@ -0,0 +1,30 @@ +package com.blockchain.server.eth.service; + +import com.blockchain.server.eth.dto.EthWalletDTO; +import com.blockchain.server.eth.entity.EthApplication; +import com.blockchain.server.eth.entity.EthWallet; + +import java.util.List; + +/** + * 以太坊钱包应用提现表——业务接口 + * + * @author YH + * @date 2019年2月16日17:09:19 + */ +public interface IEthApplicationService { + /** + * 验证是否有该应用体系的钱包 + * + * @param appId 应用标识 + */ + void CheckWalletType(String appId); + + /** + * 查询所有钱包应用类型 + * + * @return + */ + List selectAll(); + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthBlockNumberService.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthBlockNumberService.java new file mode 100644 index 0000000..aa26282 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthBlockNumberService.java @@ -0,0 +1,80 @@ +package com.blockchain.server.eth.service; + +import com.blockchain.server.eth.entity.EthBlockNumber; +import com.blockchain.server.eth.entity.EthToken; + +import java.math.BigInteger; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 以太坊区块高度记录表——业务接口 + * + * @author YH + * @date 2019年2月16日17:09:19 + */ +public interface IEthBlockNumberService { + /** + * 插入一个区块 + * + * @param blockNumber 区块号 + * @param status 状态 + */ + void insert(BigInteger blockNumber, char status); + + /** + * 插入一个区块的状态 + * + * @param blockNumber 区块号 + * @param status 状态 + */ + void updateByBlockNumber(BigInteger blockNumber, char status); + + /** + * 查询区块是否存在 + * + * @param bigInteger 区块 + * @return true存在,false不存在 + */ + boolean isBlock(BigInteger bigInteger); + + /** + * 查询区块信息 + * + * @param blockNumber + * @return + */ + EthBlockNumber selectByBlockNumber(BigInteger blockNumber); + + /** + * 查询最大的区块号 + * + * @return + */ + BigInteger selectCurrent(); + + + /** + * 查询最新的区块号 + * + * @return + */ + BigInteger selectNewest(); + + /** + * 获取查询等待中的区块号 + * + * @return + */ + List selectWaitBlocks(); + + /** + * 插入该区块,所有的平台用户充值记录 + * + * @param blockNumber 区块号 + */ + void handleBlockTxIn(BigInteger blockNumber, Map coinAddrs, Set adds); + + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthGasWalletService.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthGasWalletService.java new file mode 100644 index 0000000..39517f7 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthGasWalletService.java @@ -0,0 +1,23 @@ +package com.blockchain.server.eth.service; + +import com.blockchain.server.eth.entity.EthWalletKey; + +import java.util.Set; + +/** + * 以太坊钱包主要信息表——业务接口 + * + * @author YH + * @date 2019年2月16日17:09:19 + */ +public interface IEthGasWalletService { + + /** + * 判断是否为油费钱包 + * + * @param addr + * @return + */ + boolean isGasWallet(String addr); + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthTokenService.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthTokenService.java new file mode 100644 index 0000000..fef167c --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthTokenService.java @@ -0,0 +1,52 @@ +package com.blockchain.server.eth.service; + +import com.blockchain.server.eth.entity.EthToken; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 以太坊币种表——业务接口 + * + * @author YH + * @date 2019年2月16日17:09:19 + */ +public interface IEthTokenService { + /** + * 根据币种地址获取以太坊的币种信息 + * + * @param tokenAddr 币种地址 + * @return + */ + EthToken findByTokenAddr(String tokenAddr); + + /** + * 根据币种名称获取以太坊的币种信息 + * + * @param tokenName 币种名称 + * @return + */ + EthToken findByTokenName(String tokenName); + + /** + * 获取以太坊币种表的所有币种信息 + * + * @return + */ + List selectAll(); + + /** + * 获取以太坊币种表的所有币种信息 + * + * @return + */ + Map selectMaps(); + + /** + * 获取所有的币种地址 + * + * @return + */ + Set selectTokenAddrs(); +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthWalletKeyService.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthWalletKeyService.java new file mode 100644 index 0000000..7c493d0 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthWalletKeyService.java @@ -0,0 +1,46 @@ +package com.blockchain.server.eth.service; + +import com.blockchain.server.eth.dto.EthWalletDTO; +import com.blockchain.server.eth.dto.Web3jWalletDTO; +import com.blockchain.server.eth.entity.EthWallet; +import com.blockchain.server.eth.entity.EthWalletKey; + +import java.util.List; +import java.util.Set; + +/** + * 以太坊钱包主要信息表——业务接口 + * + * @author YH + * @date 2019年2月16日17:09:19 + */ +public interface IEthWalletKeyService { + + EthWalletKey findByUserOpenId(String userOpenId); + + /** + * 是否存在密码 + * + * @param userOpenId + * @return + */ + boolean existsPass(String userOpenId); + + EthWalletKey selectOne(EthWalletKey ethWalletKey); + + int insert(EthWalletKey ethWalletKey); + + EthWalletKey insert(String userOpenId); + + int update(EthWalletKey ethWalletKey); + + Set selectAddrs(); + + /** + * 返回地址 + **/ + String isPassword(String userOpenId, String password); + + void updatePassword(String userOpenId, String password); + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthWalletService.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthWalletService.java new file mode 100644 index 0000000..2cd03c7 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthWalletService.java @@ -0,0 +1,208 @@ +package com.blockchain.server.eth.service; + +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import com.blockchain.server.eth.dto.EthWalletDTO; +import com.blockchain.server.eth.dto.Web3jTransferDTO; +import com.blockchain.server.eth.entity.EthToken; +import com.blockchain.server.eth.entity.EthWallet; +import com.blockchain.server.eth.entity.EthWalletTransfer; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 以太坊钱包表——业务接口 + * + * @author YH + * @date 2019年2月16日17:09:19 + */ +public interface IEthWalletService { + /** + * 自定义条件查询钱包信息 + * + * @param ethWallet 条件 + * @return + */ + EthWallet select(EthWallet ethWallet); + + /** + * 自定义条件查询钱包信息 + * + * @param ethWallet 条件 + * @return + */ + List selects(EthWallet ethWallet); + + /** + * 创建某应用类型指定的某类币种钱包 + * + * @param userOpenId 用户标识 + * @param tokenAddr 币种地址 + * @param walletType 钱包类型 + * @param pass 钱包密码 + * @return + */ + EthWalletDTO insert(String userOpenId, String tokenAddr, String walletType, String pass); + + /** + * 创建某应用类型所有币种钱包 + * + * @param userOpenId 用户标识 + * @param walletType 钱包类型 + * @param pass 钱包密码 + * @return + */ + List insertByWalletTypeAll(String userOpenId, String walletType, String pass); + + /** + * 初始化所有钱包 + * + * @param userOpenId 用户ID + * @return + */ + void insertInit(String userOpenId); + + /** + * 根据用户ID,币种地址,钱包类型查询钱包信息 + * + * @param userOpenId 用户ID + * @param tokenAddr 币种地址 + * @param walletType 钱包类型 + * @return + */ + EthWalletDTO selectByUserOpenIdAndTokenAddrAndWalletType(String userOpenId, String tokenAddr, String walletType); + + /** + * 根据钱包地址,币种地址查询钱包信息 + * + * @param addr 钱包地址 + * @param tokenAddr 币种地址 + * @return + */ + EthWalletDTO selectByAddrAndTokenAddr(String addr, String tokenAddr); + + /** + * 根据钱包地址,币种地址,钱包类型查询钱包信息 + * + * @param addr 钱包地址 + * @param tokenAddr 币种地址 + * @param walletType 钱包类型 + * @return + */ + EthWalletDTO selectByAddrAndTokenAddrAndWalletType(String addr, String tokenAddr, String walletType); + + /** + * 根据用户ID,币种地址,钱包类型查询钱包信息 + * + * @param userOpenId 用户ID + * @param walletType 钱包类型 + * @return + */ + List selectByUserOpenIdAndWalletType(String userOpenId, String walletType); + + /** + * 根据钱包地址,币种地址,钱包类型查询钱包信息 + * + * @param addr 钱包地址 + * @param walletType 钱包类型 + * @return + */ + List selectByAddrAndWalletType(String addr, String walletType); + + /** + * 获取加密密码的公钥 + * + * @param userOpenId + * @return + */ + String getPublicKey(String userOpenId); + + /** + * 验证钱包密码是否正确 + * + * @param userOpenId 用户标识 + * @param password 密码 + */ + void isPassword(String userOpenId, String password); + + /** + * 重置钱包密码 + * + * @param userOpenId 用户标识 + * @param password 钱包密码 + */ + void updatePassword(String userOpenId, String password); + + /** + * 用户应用钱包的相互划转 + * + * @param userOpenId 用户标识 + * @param tokenAddr 币种地址 + * @param formWalletType 支付的应用钱包 + * @param toWalletType 接收的应用钱包 + * @param amount 划转的金额 + * @param password 加密的密码 + * @return + */ + EthWalletTransfer updateTypeTransfer(String userOpenId, String tokenAddr, String formWalletType, + String toWalletType, String amount, String password); + + /** + * 应用钱包充值业务 + * + * @param ethWalletTransfer 充值记录 + */ + void updateBlanceTxIn(EthWalletTransfer ethWalletTransfer); + + /** + * 提现申请 + * + * @param userOpenId 用户标识 + * @param tokenAddr 币种地址 + * @param walletType 钱包类型 + * @param toAddr 提现地址 + * @param amount 提现额度 + * @param password 密码 + */ + EthWalletTransfer handleWelletOutApply(String userOpenId, String tokenAddr, String toAddr, String walletType, + String amount, String password); + + /** + * 钱包冻结与可用余额转换 + * + * @param orderDTO + */ + void updateBlanceTransform(WalletOrderDTO orderDTO); + + /** + * 钱包余额变动 + * + * @param changeDTO + */ + void updateBlance(WalletChangeDTO changeDTO); + + /** + * 提现失败处理 + * + * @param tx + */ + void updateTxOutError(EthWalletTransfer tx); + + /** + * 提现成功处理 + * @param tx + */ + void updateTxOutSuccess(EthWalletTransfer tx); + + /** + * 划转接口 + * + * @param fromType 支付的钱包 + * @param toType 收款的钱包 + * @param coinName 币种名称 + * @param amount 金额 + * @return + */ + EthWalletTransfer handleTransfer(String userId,String fromType, String toType, String coinName, BigDecimal amount); +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthWalletTransferService.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthWalletTransferService.java new file mode 100644 index 0000000..ab689dc --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/IEthWalletTransferService.java @@ -0,0 +1,126 @@ +package com.blockchain.server.eth.service; + +import com.blockchain.server.eth.entity.EthToken; +import com.blockchain.server.eth.entity.EthWallet; +import com.blockchain.server.eth.entity.EthWalletTransfer; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * 以太坊钱包记录表——业务接口 + * + * @author YH + * @date 2019年2月16日17:09:19 + */ +public interface IEthWalletTransferService { + + /** + * 插入一条钱包流水记录 + * + * @param hash 转账唯一标识 + * @param fromAddr 支付地址 + * @param toAddr 接收地址 + * @param amount 转账余额 + * @param amountCoin 转账币种类型 + * @param transferType 转账类型 + * @return + */ + EthWalletTransfer insert(String hash, String fromAddr, String toAddr, BigDecimal amount, EthToken amountCoin, + String transferType, Date date); + + /** + * 插入一条钱包流水记录 + * + * @param hash 转账唯一标识 + * @param fromAddr 支付地址 + * @param toAddr 接收地址 + * @param amount 转账余额 + * @param amountCoin 转账币种类型 + * @param transferType 转账类型 + * @param status 转账状态 + * @return + */ + EthWalletTransfer insert(String hash, String fromAddr, String toAddr, BigDecimal amount, EthToken amountCoin, + String transferType, int status, Date date); + + /** + * 插入一条钱包流水记录 + * + * @param hash 转账唯一标识 + * @param fromAddr 支付地址 + * @param toAddr 接收地址 + * @param amount 转账余额 + * @param gas 转账手续费 + * @param amountCoin 转账币种类型 + * @param gasCoin 手续费币种类型 + * @param transferType 转账类型 + * @param status 转账状态 + * @return + */ + EthWalletTransfer insert(String hash, String fromAddr, String toAddr, BigDecimal amount, BigDecimal gas, + EthToken amountCoin, EthToken gasCoin, String transferType, int status, Date date); + + /** + * 插入一条钱包流水记录 + * + * @param hash 转账唯一标识 + * @param fromAddr 支付地址 + * @param toAddr 接收地址 + * @param amount 转账余额 + * @param gas 转账手续费 + * @param amountCoin 转账币种类型 + * @param gasCoin 手续费币种类型 + * @param transferType 转账类型 + * @param status 转账状态 + * @param remark 转账备注 + * @return + */ + EthWalletTransfer insert(String hash, String fromAddr, String toAddr, BigDecimal amount, BigDecimal gas, + EthToken amountCoin, EthToken gasCoin, String transferType, int status, String remark, Date date); + + /** + * 修改记录gas费用 + * + * @param ethWalletTransfer 记录 + */ + EthWalletTransfer updateGas(EthWalletTransfer ethWalletTransfer); + + /** + * 修改记录状态 + * + * @param ethWalletTransfer 记录 + */ + void updateStatus(EthWalletTransfer ethWalletTransfer); + + /** + * 修改记录状态 + * + * @param id + * @param status 记录状态 + */ + void updateStatus(String id, int status, Date date); + + /** + * 根据记录类型与记录状态查询记录 + * + * @param transferType 记录类型 + * @param Status 记录状态 + * @param pageNum 当前页数 + * @param pageSize 页数展示条数 + * @return + */ + List selectByTxTypeAndStatus(String transferType, int Status, Integer pageNum, Integer pageSize); + + /** + * 获取钱包记录 + * + * @param userOpenId 用户id + * @param tokenAddr 币种地址 + * @param walletType 应用类型,CCT + * @return + */ + List selectTransfer(String userOpenId, String tokenAddr, String walletType); + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/ConfigWalletParamServiceImpl.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/ConfigWalletParamServiceImpl.java new file mode 100644 index 0000000..b1f2f94 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/ConfigWalletParamServiceImpl.java @@ -0,0 +1,39 @@ +package com.blockchain.server.eth.service.impl; + +import com.blockchain.common.base.dto.GasDTO; +import com.blockchain.common.base.util.JsonUtils; +import com.blockchain.server.eth.common.constants.EthConfigConstants; +import com.blockchain.server.eth.dto.EthGasDTO; +import com.blockchain.server.eth.entity.ConfigWalletParam; +import com.blockchain.server.eth.mapper.ConfigWalletParamMapper; +import com.blockchain.server.eth.service.IConfigWalletParamService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 配置表——业务接口 + * + * @author YH + * @date 2019年2月16日17:09:19 + */ +@Service +public class ConfigWalletParamServiceImpl implements IConfigWalletParamService { + @Autowired + ConfigWalletParamMapper configMapper; + + + @Override + public GasDTO getGasConfig(String tokenSymbol) { + ConfigWalletParam where = new ConfigWalletParam(); + where.setModuleType(EthConfigConstants.MODULE_TYPE); + where.setStatus(EthConfigConstants.STATUS_NORMAL); + String paramName = EthConfigConstants.EthGasConfig.PARAM_NAME + tokenSymbol; + where.setParamName(paramName.toLowerCase()); + ConfigWalletParam config = configMapper.selectOne(where); + if (config == null) return null; + String val = config.getParamValue(); + return JsonUtils.jsonToPojo(val, GasDTO.class); + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthApplicationServiceImpl.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthApplicationServiceImpl.java new file mode 100644 index 0000000..baf1fc3 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthApplicationServiceImpl.java @@ -0,0 +1,47 @@ +package com.blockchain.server.eth.service.impl; + + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.eth.common.enums.EthWalletEnums; +import com.blockchain.server.eth.common.exception.EthWalletException; +import com.blockchain.server.eth.entity.EthApplication; +import com.blockchain.server.eth.entity.EthToken; +import com.blockchain.server.eth.mapper.EthApplicationMapper; +import com.blockchain.server.eth.mapper.EthTokenMapper; +import com.blockchain.server.eth.service.IEthApplicationService; +import com.blockchain.server.eth.service.IEthTokenService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 以太坊币种表——业务接口 + * + * @author YH + * @date 2019年2月16日17:25:02 + */ +@Service +public class EthApplicationServiceImpl implements IEthApplicationService { + + @Autowired + EthApplicationMapper ethApplicationMapper; + + @Override + public void CheckWalletType(String appId) { + ExceptionPreconditionUtils.checkStringNotBlank(appId, new EthWalletException(EthWalletEnums.NULL_WALLETTYPE)); + List list = selectAll(); + for (EthApplication row : list) { + if (appId.equalsIgnoreCase(row.getAppId())) { + return; + } + } + throw new EthWalletException(EthWalletEnums.INEXISTENCE_WALLETTYPE); + } + + + @Override + public List selectAll() { + return ethApplicationMapper.selectAll(); + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthBlockNumberServiceImpl.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthBlockNumberServiceImpl.java new file mode 100644 index 0000000..2592105 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthBlockNumberServiceImpl.java @@ -0,0 +1,205 @@ +package com.blockchain.server.eth.service.impl; + +import com.blockchain.server.eth.common.constants.EthWalletConstants; +import com.blockchain.server.eth.common.util.DataCheckUtil; +import com.blockchain.server.eth.common.util.RedisBlockUtil; +import com.blockchain.server.eth.dto.Web3jTransferDTO; +import com.blockchain.server.eth.entity.EthBlockNumber; +import com.blockchain.server.eth.entity.EthToken; +import com.blockchain.server.eth.entity.EthWalletTransfer; +import com.blockchain.server.eth.mapper.EthBlockNumberMapper; +import com.blockchain.server.eth.service.*; +import com.blockchain.server.eth.web3j.IWalletWeb3j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.web3j.protocol.core.methods.response.EthBlock; +import org.web3j.protocol.core.methods.response.Transaction; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 以太坊区块高度记录表——业务接口 + * + * @author YH + * @date 2019年2月16日17:09:19 + */ +@Service +public class EthBlockNumberServiceImpl implements IEthBlockNumberService { + + static final String GAS_TOKENADDR = "eth"; // 默认手续费币种 + static final String REGEX = "^0xa9059cbb.+"; // 转账正则判断 + static final Pattern PATTERN = Pattern.compile("^(.+)(.{64})(.{64}$)"); // input 数据解析 + static final BigInteger BIAS_CURRENT = BigInteger.valueOf(20); + + @Autowired + EthBlockNumberMapper ethBlockNumberMapper; + @Autowired + IEthWalletKeyService ethWalletKeyService; + @Autowired + IEthTokenService ethTokenService; + @Autowired + IEthWalletTransferService ethWalletTransferService; + @Autowired + IEthGasWalletService ethGasWalletService; + + @Autowired + IWalletWeb3j walletWeb3j; + @Autowired + RedisTemplate redisTemplate; + + @Override + @Transactional + public void insert(BigInteger blockNumber, char status) { + Date date = new Date(); + EthBlockNumber ethBlockNumber = new EthBlockNumber(); + ethBlockNumber.setBlockNumber(blockNumber); + ethBlockNumber.setCreateTime(date); + ethBlockNumber.setUpdateTime(date); + ethBlockNumber.setStatus(status); + ethBlockNumberMapper.insertSelective(ethBlockNumber); + } + + @Override + @Transactional + public void updateByBlockNumber(BigInteger blockNumber, char status) { + EthBlockNumber ethBlockNumber = ethBlockNumberMapper.selectByPrimaryKey(blockNumber); + if (ethBlockNumber == null) { + insert(blockNumber, status); + } else { + Date date = new Date(); + ethBlockNumber.setUpdateTime(date); + ethBlockNumber.setStatus(status); + ethBlockNumberMapper.updateByPrimaryKeySelective(ethBlockNumber); + } + } + + @Override + public boolean isBlock(BigInteger bigInteger) { + EthBlockNumber ethBlockNumber = ethBlockNumberMapper.selectByPrimaryKey(bigInteger); + return ethBlockNumber != null; + } + + @Override + public EthBlockNumber selectByBlockNumber(BigInteger blockNumber) { + return ethBlockNumberMapper.selectByPrimaryKey(blockNumber); + } + + @Override + public BigInteger selectCurrent() { + BigInteger current = RedisBlockUtil.getBlockCurrent(redisTemplate); + if (current == null) { + current = ethBlockNumberMapper.selectMaxBlockNumber(); + if(current == null){ + current = walletWeb3j.getEthBlock(); + } + RedisBlockUtil.setBlockCurrent(current, redisTemplate); + } + return current; + } + + + @Override + public BigInteger selectNewest() { + BigInteger newest = RedisBlockUtil.getBlockNewest(redisTemplate); + if (newest == null || newest.compareTo(selectCurrent().add(BIAS_CURRENT)) <= 0) { + newest = walletWeb3j.getEthBlock(); + RedisBlockUtil.setBlockNewest(newest, redisTemplate); + } + return newest; + } + + @Override + public List selectWaitBlocks() { + EthBlockNumber ethBlockNumber = new EthBlockNumber(); + ethBlockNumber.setStatus(EthWalletConstants.StatusType.BLOCK_WAIT); + return ethBlockNumberMapper.select(ethBlockNumber); + } + + @Override + @Transactional + public void handleBlockTxIn(BigInteger blockNumber, Map coinAddrs, Set adds) { + // 查询该区块的所有交易记录 + List txResults = walletWeb3j.getTokenTransactionList(blockNumber); + for (EthBlock.TransactionResult txResult : txResults) { + Transaction tx = txResult.get(); + + // 忽略打油费的钱包的充值 + if(ethGasWalletService.isGasWallet(tx.getFrom())){ + continue; + } + + if (coinAddrs.containsKey(tx.getTo())) { // 充值ETH代币 + if (!tx.getInput().matches(REGEX)) continue; + Web3jTransferDTO transferDTO = getTokenWalletDto(tx, coinAddrs.get(tx.getTo())); + if (adds.contains(transferDTO.getToAddr())) { + // 插入ETH代币钱包充值记录 + ethWalletTransferService.insert(transferDTO.getHash(), transferDTO.getFromAddr(), + transferDTO.getToAddr(), transferDTO.getAmount(), transferDTO.getGasPrice(), + coinAddrs.get(tx.getTo()), + coinAddrs.get(GAS_TOKENADDR), + EthWalletConstants.TransferType.IN, EthWalletConstants.StatusType.IN_LOAD, new Date()); + } + } else if (adds.contains(tx.getTo())) { // 充值ETH + + Web3jTransferDTO transferDTO = getWalletDto(tx); + // 插入ETH钱包充值记录 + ethWalletTransferService.insert(transferDTO.getHash(), transferDTO.getFromAddr(), + transferDTO.getToAddr(), transferDTO.getAmount(), transferDTO.getGasPrice(), + coinAddrs.get(GAS_TOKENADDR), + coinAddrs.get(GAS_TOKENADDR), + EthWalletConstants.TransferType.IN, EthWalletConstants.StatusType.IN_LOAD, new Date()); + } + } + // 修改当前区块为成功 + updateByBlockNumber(blockNumber, EthWalletConstants.StatusType.BLOCK_SUCCESS); + } + + + /** + * 代币数据DTO + * + * @param transaction 数据信息 + * @return + */ + private Web3jTransferDTO getTokenWalletDto(Transaction transaction, EthToken coin) { + Matcher matcher = PATTERN.matcher(transaction.getInput()); + matcher.matches(); + String toAddr = matcher.group(2).replaceAll(".+(.{40})", "0x$1"); + BigInteger amount = new BigInteger(matcher.group(3).replaceAll("0+(.+)", "$1"), 16); + Web3jTransferDTO transferDTO = new Web3jTransferDTO(); + transferDTO.setHash(transaction.getHash()); + transferDTO.setFromAddr(transaction.getFrom()); + transferDTO.setToAddr(toAddr); + transferDTO.setTokenAddr(transaction.getTo()); + transferDTO.setGasPrice(new BigDecimal(transaction.getGasPrice())); + transferDTO.setAmount(DataCheckUtil.bitToBigDecimal(amount, coin.getTokenDecimals())); + return transferDTO; + } + + /** + * eth数据DTO + * + * @param transaction + * @return + */ + private Web3jTransferDTO getWalletDto(Transaction transaction) { + Web3jTransferDTO transferDTO = new Web3jTransferDTO(); + transferDTO.setHash(transaction.getHash()); + transferDTO.setFromAddr(transaction.getFrom()); + transferDTO.setToAddr(transaction.getTo()); + transferDTO.setTokenAddr(GAS_TOKENADDR); + transferDTO.setAmount(DataCheckUtil.bitToBigDecimal(transaction.getValue())); + transferDTO.setGasPrice(new BigDecimal(transaction.getGasPrice())); + return transferDTO; + } + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthGasWalletServiceImpl.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthGasWalletServiceImpl.java new file mode 100644 index 0000000..b8427a3 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthGasWalletServiceImpl.java @@ -0,0 +1,43 @@ +package com.blockchain.server.eth.service.impl; + + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.common.base.util.RSACoderUtils; +import com.blockchain.server.eth.common.enums.EthWalletEnums; +import com.blockchain.server.eth.common.exception.EthWalletException; +import com.blockchain.server.eth.common.util.RedisPrivateUtil; +import com.blockchain.server.eth.common.util.RedisWalletAddrUtil; +import com.blockchain.server.eth.dto.Web3jWalletDTO; +import com.blockchain.server.eth.entity.EthGasWallet; +import com.blockchain.server.eth.entity.EthWalletKey; +import com.blockchain.server.eth.mapper.EthGasWalletMapper; +import com.blockchain.server.eth.mapper.EthWalletKeyMapper; +import com.blockchain.server.eth.service.IEthGasWalletService; +import com.blockchain.server.eth.service.IEthWalletKeyService; +import com.blockchain.server.eth.web3j.IWalletWeb3j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.Set; + +/** + * 以太坊钱包表——业务接口 + * + * @author YH + * @date 2019年2月16日17:25:02 + */ +@Service +public class EthGasWalletServiceImpl implements IEthGasWalletService { + + @Autowired + EthGasWalletMapper ethGasWalletMapper; + + @Override + public boolean isGasWallet(String addr) { + EthGasWallet gasWallet = ethGasWalletMapper.selectByPrimaryKey(addr.toLowerCase()); + return null != gasWallet; + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthTokenServiceImpl.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthTokenServiceImpl.java new file mode 100644 index 0000000..6ef5714 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthTokenServiceImpl.java @@ -0,0 +1,81 @@ +package com.blockchain.server.eth.service.impl; + + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.eth.common.enums.EthWalletEnums; +import com.blockchain.server.eth.common.exception.EthWalletException; +import com.blockchain.server.eth.common.util.RedisWalletAddrUtil; +import com.blockchain.server.eth.common.util.RedisWalletAddrUtil; +import com.blockchain.server.eth.entity.EthToken; +import com.blockchain.server.eth.entity.EthWallet; +import com.blockchain.server.eth.mapper.EthTokenMapper; +import com.blockchain.server.eth.service.IEthTokenService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 以太坊币种表——业务接口 + * + * @author YH + * @date 2019年2月16日17:25:02 + */ +@Service +public class EthTokenServiceImpl implements IEthTokenService { + + @Autowired + EthTokenMapper ethTokenMapper; + + @Autowired + RedisTemplate redisTemplate; + + @Override + public EthToken findByTokenAddr(String tokenAddr) { + ExceptionPreconditionUtils.checkStringNotBlank(tokenAddr, + new EthWalletException(EthWalletEnums.NULL_TOKENADDR)); + String _tokenAddr = tokenAddr.toLowerCase(); // 全部转为小写 + return ethTokenMapper.selectByPrimaryKey(_tokenAddr); + } + + @Override + public EthToken findByTokenName(String tokenName) { + ExceptionPreconditionUtils.checkStringNotBlank(tokenName, + new EthWalletException(EthWalletEnums.NULL_TOKENADDR)); + EthToken where = new EthToken(); + where.setTokenSymbol(tokenName); + EthToken ethToken = null; + try { + ethToken = ethTokenMapper.selectOne(where); + } catch (Exception e) { + throw new EthWalletException(EthWalletEnums.IEXIST_TOKENADDRS); + } + if (ethToken == null) { + throw new EthWalletException(EthWalletEnums.INEXISTENCE_TOKENADDR); + } + return ethToken; + } + + @Override + public List selectAll() { + return ethTokenMapper.selectAll(); + } + + @Override + public Map selectMaps() { + Map coins = RedisWalletAddrUtil.getWalletCoinAddrs(redisTemplate); + if (coins.isEmpty()) { + coins = RedisWalletAddrUtil.setWalletCoinAddrs(ethTokenMapper.selectAll(), redisTemplate); + } + return coins; + } + + @Override + public Set selectTokenAddrs() { + return ethTokenMapper.selectAllTokenAddr(); + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthWalletKeyServiceImpl.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthWalletKeyServiceImpl.java new file mode 100644 index 0000000..a55a3cb --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthWalletKeyServiceImpl.java @@ -0,0 +1,164 @@ +package com.blockchain.server.eth.service.impl; + + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.common.base.util.RSACoderUtils; +import com.blockchain.server.eth.common.enums.EthWalletEnums; +import com.blockchain.server.eth.common.exception.EthWalletException; +import com.blockchain.server.eth.common.util.RedisPrivateUtil; +import com.blockchain.server.eth.common.util.RedisWalletAddrUtil; +import com.blockchain.server.eth.dto.Web3jWalletDTO; +import com.blockchain.server.eth.entity.EthWalletKey; +import com.blockchain.server.eth.mapper.EthWalletKeyMapper; +import com.blockchain.server.eth.service.IEthWalletKeyService; +import com.blockchain.server.eth.web3j.IWalletWeb3j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; +import java.util.Set; + +/** + * 以太坊钱包表——业务接口 + * + * @author YH + * @date 2019年2月16日17:25:02 + */ +@Service +public class EthWalletKeyServiceImpl implements IEthWalletKeyService { + + @Autowired + EthWalletKeyMapper ethWalletKeyMapper; + @Autowired + IWalletWeb3j walletWeb3j; + @Autowired + RedisTemplate redisTemplate; + + @Override + public EthWalletKey findByUserOpenId(String userOpenId) { + EthWalletKey where = new EthWalletKey(); + where.setUserOpenId(userOpenId); + List list = ethWalletKeyMapper.select(where); + return list.size() <= 0 ? null : list.get(0); + } + + @Override + public boolean existsPass(String userOpenId) { + EthWalletKey ethWalletKey = this.findByUserOpenId(userOpenId); + String keystore = ethWalletKey.getKeystore(); + return keystore != null; + } + + @Override + public EthWalletKey selectOne(EthWalletKey ethWalletKey) { + EthWalletKey _ethWalletKey = ethWalletKeyMapper.selectOne(ethWalletKey); + return _ethWalletKey; + } + + @Override + @Transactional + public int insert(EthWalletKey ethWalletKey) { + RedisWalletAddrUtil.setWalletAddr(ethWalletKey.getAddr(), redisTemplate); + return ethWalletKeyMapper.insertSelective(ethWalletKey); + } + + @Override + @Transactional + public EthWalletKey insert(String userOpenId) { +// EthWalletKey initRow = findByUserOpenId(userOpenId); // 查询钱包是否初始化过 + // 创建以太坊钱包 + EthWalletKey ethWalletKey = new EthWalletKey(); + Date date = new Date(); + ethWalletKey.setUserOpenId(userOpenId); + Web3jWalletDTO wallet = walletWeb3j.creationEthWallet("123456"); + ethWalletKey.setAddr(wallet.getAddr()); + ethWalletKey.setPrivateKey(RSACoderUtils.encryptPassword(wallet.getPrivateKey())); // 私钥加密 + ethWalletKey.setUpdateTime(date); + ethWalletKey.setCreateTime(date); + int row = ethWalletKeyMapper.insertSelective(ethWalletKey); + if (row == 0) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } else { + RedisWalletAddrUtil.setWalletAddr(ethWalletKey.getAddr(), redisTemplate); // 存入缓存 + return ethWalletKey; + } + } + + @Override + @Transactional + public int update(EthWalletKey ethWalletKey) { + return ethWalletKeyMapper.updateByPrimaryKey(ethWalletKey); + } + + @Override + public Set selectAddrs() { + Set addrs = RedisWalletAddrUtil.getWalletAddrs(redisTemplate); + if (addrs == null || addrs.size() == 0) { + addrs = ethWalletKeyMapper.selectAllAddrs(); + RedisWalletAddrUtil.setWalletAddrs(addrs, redisTemplate); + } + return addrs; + } + + @Override + public String isPassword(String userOpenId, String password) { + // 数据验证 + ExceptionPreconditionUtils.checkStringNotBlank(userOpenId, + new EthWalletException(EthWalletEnums.NULL_USEROPENID)); + EthWalletKey ethWalletKey = this.findByUserOpenId(userOpenId); + if (ethWalletKey == null) { + throw new EthWalletException(EthWalletEnums.INEXISTENCE_WALLET); + } + if (StringUtils.isBlank(ethWalletKey.getKeystore())) { + throw new EthWalletException(EthWalletEnums.NOT_WALLETPASSWORD); + } + String _pass = getPassword(userOpenId, password); + walletWeb3j.isPassword(ethWalletKey.getKeystore(), _pass); + return ethWalletKey.getAddr(); + } + + @Override + @Transactional + public void updatePassword(String userOpenId, String password) { + // 数据验证 + ExceptionPreconditionUtils.checkStringNotBlank(userOpenId, + new EthWalletException(EthWalletEnums.NULL_USEROPENID)); +// EthWalletKey ethWalletKey = ethWalletKeyMapper.selectByPrimaryKey(userOpenId); + EthWalletKey where = new EthWalletKey(); + where.setUserOpenId(userOpenId); + List list = ethWalletKeyMapper.select(where); + if (list.size() == 0) { + throw new EthWalletException(EthWalletEnums.INEXISTENCE_WALLET); + } + // 重置密码 + String _pass = getPassword(userOpenId, password); + for (EthWalletKey row : list) { + String privateKey = RSACoderUtils.decryptPassword(row.getPrivateKey()); // 获取解密后的私钥 + Web3jWalletDTO walletDTO = walletWeb3j.updateEthWallet(privateKey, _pass); + row.setKeystore(walletDTO.getKeystore()); + row.setUpdateTime(new Date()); + int update = ethWalletKeyMapper.update(row); + if (update == 0) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + } + } + + /** + * 获取解密后的密码 + * + * @param userOpenId 用户ID + * @param password 加密后的密码 + * @return + */ + private String getPassword(String userOpenId, String password) { + ExceptionPreconditionUtils.checkStringNotBlank(password, new EthWalletException(EthWalletEnums.NULL_PASSWORD)); + String key = RedisPrivateUtil.getPrivateKey(userOpenId, redisTemplate); // 获取私钥 + String _pass = RSACoderUtils.decryptPassword(password, key); // 获取解密后的密码 + return _pass; + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthWalletServiceImpl.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthWalletServiceImpl.java new file mode 100644 index 0000000..97608af --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthWalletServiceImpl.java @@ -0,0 +1,559 @@ +package com.blockchain.server.eth.service.impl; + + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.dto.GasDTO; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import com.blockchain.common.base.exception.RPCException; +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.eth.common.constants.EthWalletConstants; +import com.blockchain.server.eth.common.enums.EthWalletEnums; +import com.blockchain.server.eth.common.exception.EthWalletException; +import com.blockchain.server.eth.common.util.DataCheckUtil; +import com.blockchain.server.eth.common.util.RedisPrivateUtil; +import com.blockchain.server.eth.dto.EthGasDTO; +import com.blockchain.server.eth.dto.EthWalletDTO; +import com.blockchain.server.eth.dto.Web3jTransferDTO; +import com.blockchain.server.eth.entity.*; +import com.blockchain.server.eth.feign.UserFeign; +import com.blockchain.server.eth.mapper.EthWalletMapper; +import com.blockchain.server.eth.service.*; +import com.blockchain.server.eth.web3j.IWalletWeb3j; +import com.codingapi.tx.annotation.ITxTransaction; +import com.codingapi.tx.annotation.TxTransaction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.*; + +/** + * 以太坊钱包表——业务接口 + * + * @author YH + * @date 2019年2月16日17:25:02 + */ +@Service +public class EthWalletServiceImpl implements IEthWalletService, ITxTransaction { + + static final String DEFAULT = ""; + + @Autowired + EthWalletMapper ethWalletMapper; + @Autowired + IEthTokenService ethTokenService; + @Autowired + IEthWalletKeyService ethWalletKeyService; + @Autowired + IEthApplicationService ethApplicationService; + @Autowired + IEthWalletTransferService ethWalletTransferService; + @Autowired + IConfigWalletParamService configWalletParamService; + @Autowired + private UserFeign userFeign; + + @Autowired + IWalletWeb3j walletWeb3j; + @Autowired + RedisTemplate redisTemplate; + + @Override + public EthWallet select(EthWallet ethWallet) { + return ethWalletMapper.selectOne(ethWallet); + } + + @Override + public List selects(EthWallet ethWallet) { + return ethWalletMapper.select(ethWallet); + } + + @Override + @Transactional + public EthWalletDTO insert(String userOpenId, String tokenAddr, String walletType, String pass) { + // 数据格式判断 + ExceptionPreconditionUtils.checkStringNotBlank(userOpenId, + new EthWalletException(EthWalletEnums.NULL_USEROPENID)); + ethApplicationService.CheckWalletType(walletType); + EthToken coin = checkTokenAddr(tokenAddr); // 获取该币种信息 + String addr = ethWalletKeyService.isPassword(userOpenId, pass); // 钱包密码验证 + // 创建一个没有创建的钱包 + insertMonth(userOpenId, addr, tokenAddr, coin.getTokenDecimals(), coin.getTokenSymbol(), walletType); + return selectByUserOpenIdAndTokenAddrAndWalletType(userOpenId, tokenAddr, walletType); + } + + @Override + @Transactional + public List insertByWalletTypeAll(String userOpenId, String walletType, String pass) { // 数据格式判断 + // 数据格式验证 + ExceptionPreconditionUtils.checkStringNotBlank(userOpenId, + new EthWalletException(EthWalletEnums.NULL_USEROPENID)); + ethApplicationService.CheckWalletType(walletType); + String addr = ethWalletKeyService.isPassword(userOpenId, pass); // 钱包密码验证 + // 插入未创建的钱包 + List coins = ethTokenService.selectAll(); + List list = ethWalletMapper.selectByUserOpenIdAndWalletType(userOpenId, walletType); + for (EthToken coin : coins) { + boolean isInsert = true; + for (EthWalletDTO row : list) { + if (row.getTokenAddr().equalsIgnoreCase(coin.getTokenAddr())) { + isInsert = false; + } + } + if (isInsert) { + insertMonth(userOpenId, addr, coin.getTokenAddr(), coin.getTokenDecimals(), coin.getTokenSymbol(), + walletType); + } + } + // 返回该应用类型的所有钱包 + return ethWalletMapper.selectByUserOpenIdAndWalletType(userOpenId, walletType); + } + + @Override + @TxTransaction + @Transactional + public void insertInit(String userOpenId) { + // 数据格式验证 + ExceptionPreconditionUtils.checkStringNotBlank(userOpenId, + new EthWalletException(EthWalletEnums.NULL_USEROPENID)); + List applications = ethApplicationService.selectAll(); + for (EthApplication application : applications) { + // 初始化以太坊钱包主要信息 + EthWalletKey ethWalletKey = ethWalletKeyService.insert(userOpenId); + List coins = ethTokenService.selectAll(); + + if (0 == coins.size() || 0 == applications.size()) { + throw new EthWalletException(EthWalletEnums.INSERT_INITWALLERT); + } + for (EthToken coin : coins) { + insertMonth(userOpenId, ethWalletKey.getAddr(), coin.getTokenAddr(), coin.getTokenDecimals(), + coin.getTokenSymbol(), application.getAppId()); + } + } + } + + + @Override + public EthWalletDTO selectByUserOpenIdAndTokenAddrAndWalletType(String userOpenId, String tokenAddr, + String walletType) { + // 数据格式判断 + ExceptionPreconditionUtils.checkStringNotBlank(userOpenId, + new EthWalletException(EthWalletEnums.NULL_USEROPENID)); + checkTokenAddr(tokenAddr); + ethApplicationService.CheckWalletType(walletType); + // 查询数据 + EthWalletDTO walletDTO = ethWalletMapper.selectByUserOpenIdAndTokenAddrAndWalletType(userOpenId, tokenAddr, + walletType); + ExceptionPreconditionUtils.checkNotNull(walletDTO, new EthWalletException(EthWalletEnums.INEXISTENCE_WALLET)); + return walletDTO; + } + + @Override + public EthWalletDTO selectByAddrAndTokenAddr(String addr, String tokenAddr) { + // 数据格式判断 + ExceptionPreconditionUtils.checkStringNotBlank(addr, new EthWalletException(EthWalletEnums.NULL_ADDR)); + checkTokenAddr(tokenAddr); + // 查询数据 + EthWalletDTO walletDTO = ethWalletMapper.selectByAddrAndTokenAddr(addr, tokenAddr); + ExceptionPreconditionUtils.checkNotNull(walletDTO, new EthWalletException(EthWalletEnums.INEXISTENCE_WALLET)); + return walletDTO; + } + + @Override + public EthWalletDTO selectByAddrAndTokenAddrAndWalletType(String addr, String tokenAddr, String walletType) { + // 数据格式判断 + ExceptionPreconditionUtils.checkStringNotBlank(addr, new EthWalletException(EthWalletEnums.NULL_ADDR)); + checkTokenAddr(tokenAddr); + ethApplicationService.CheckWalletType(walletType); + // 查询数据 + EthWalletDTO walletDTO = ethWalletMapper.selectByAddrAndTokenAddrAndWalletType(addr, tokenAddr, walletType); + ExceptionPreconditionUtils.checkNotNull(walletDTO, new EthWalletException(EthWalletEnums.INEXISTENCE_WALLET)); + return walletDTO; + } + + @Override + public List selectByUserOpenIdAndWalletType(String userOpenId, String walletType) { + // 数据格式判断 + ExceptionPreconditionUtils.checkStringNotBlank(userOpenId, + new EthWalletException(EthWalletEnums.NULL_USEROPENID)); + ethApplicationService.CheckWalletType(walletType); + // 查询数据 + List list = ethWalletMapper.selectByUserOpenIdAndWalletType(userOpenId, walletType); + return list; + } + + @Override + public List selectByAddrAndWalletType(String addr, String walletType) { + // 数据格式判断 + ExceptionPreconditionUtils.checkStringNotBlank(addr, new EthWalletException(EthWalletEnums.NULL_ADDR)); + ethApplicationService.CheckWalletType(walletType); + // 查询数据 + List list = ethWalletMapper.selectByAddrAndWalletType(addr, walletType); + return list; + } + + @Override + public String getPublicKey(String userOpenId) { + return RedisPrivateUtil.getPublicKey(userOpenId, redisTemplate); + } + + @Override + public void isPassword(String userOpenId, String password) { + ethWalletKeyService.isPassword(userOpenId, password); + } + + @Override + @Transactional + public void updatePassword(String userOpenId, String password) { + ethWalletKeyService.updatePassword(userOpenId, password); + } + + @Override + @Transactional + public EthWalletTransfer updateTypeTransfer(String userOpenId, String tokenAddr, String formWalletType, + String toWalletType, String amount, String password) { + // #1-数据验证 + EthToken amountCoin = checkTokenAddr(tokenAddr); + ethApplicationService.CheckWalletType(formWalletType); + ethApplicationService.CheckWalletType(toWalletType); + if (formWalletType.equalsIgnoreCase(toWalletType)) { + throw new EthWalletException(EthWalletEnums.EQWALLERTTYPE_ERROR); + } + ethWalletKeyService.isPassword(userOpenId, password); + Date date = new Date(); + BigDecimal toAmount = DataCheckUtil.strToBigDecimal(amount); // 应用接收方增加的余额(字符串转数字类型) + BigDecimal formAmount = toAmount.multiply(BigDecimal.valueOf(-1)); // 应用支付方减少的余额 + // #2-进行行锁转账(应用钱包-应用钱包) + EthWalletDTO formWallet = + updateBalanceByUserIdMonth(userOpenId, tokenAddr, formWalletType, formAmount, date); // + // 支付应用的钱包扣减 + EthWalletDTO toWallet = updateBalanceByUserIdMonth(userOpenId, tokenAddr, toWalletType, toAmount, date); // + // 接收应用的钱包增加 + // #3-插入一条转账记录 + return ethWalletTransferService.insert(UUID.randomUUID().toString(), formWallet.getAddr(), toWallet.getAddr() + , toAmount, amountCoin, EthWalletConstants.TransferType.TXMIN, date); + } + + @Override + @Transactional + public void updateBlanceTxIn(EthWalletTransfer ethWalletTransfer) { + // 数据验证 + ExceptionPreconditionUtils.checkNotNull(ethWalletTransfer, new EthWalletException(EthWalletEnums.NULL_TXIN)); + EthWalletDTO wallet = selectByAddrAndTokenAddr(ethWalletTransfer.getToAddr(), ethWalletTransfer.getTokenAddr()); + Date date = new Date(); + // 业务操作(1)- 修改钱包余额 + updateBalanceByAddrMonth(wallet.getAddr(), wallet.getTokenAddr(), wallet.getWalletType(), + ethWalletTransfer.getAmount(), date); + // 业务操作(2)- 修改记录状态 + ethWalletTransferService.updateStatus(ethWalletTransfer.getId(), EthWalletConstants.StatusType.IN_SUCCESS, + date); + } + + @Override + @Transactional + public EthWalletTransfer handleWelletOutApply(String userOpenId, String tokenAddr, String toAddr, String walletType, + String amount, String password) { + ExceptionPreconditionUtils.checkStringNotBlank(toAddr, new EthWalletException(EthWalletEnums.NULL_OUT_TOADDR)); + if (toAddr.length() != EthWalletConstants.WALLERT_LENGTH) { + throw new EthWalletException(EthWalletEnums.OUT_TOADDR_ERROR); + } + toAddr = toAddr.toLowerCase(); + EthWalletDTO wallet = selectByUserOpenIdAndTokenAddrAndWalletType(userOpenId, tokenAddr, walletType); // 数据验证 + ethWalletKeyService.isPassword(wallet.getUserOpenId(), password); // 验证密码 + + Date date = new Date(); + Set addrs = ethWalletKeyService.selectAddrs();// 获取所有的用户地址 + Map coins = ethTokenService.selectMaps(); // 查询所有币种 + EthWalletTransfer tx; + BigDecimal amountNumber = DataCheckUtil.strToBigDecimal(amount); // 转化余额格式 + if (addrs.contains(toAddr)) { // 站内转账 + int outRow = ethWalletMapper.updateBalanceByAddrInRowLock(wallet.getAddr(), wallet.getTokenAddr(), + walletType, amountNumber.negate(), amountNumber.negate(), BigDecimal.ZERO, date); // 减少余额操作 + if (outRow == 0) { + throw new EthWalletException(EthWalletEnums.NUMBER_INSUFFICIENT_ERROR); + } + int inRow = ethWalletMapper.updateBalanceByAddrInRowLock(toAddr, wallet.getTokenAddr(), + walletType, amountNumber, amountNumber, BigDecimal.ZERO, date); // 增加余额操作 + if (inRow == 0) { + throw new EthWalletException(EthWalletEnums.NUMBER_INSUFFICIENT_ERROR); + } + tx = ethWalletTransferService.insert(UUID.randomUUID().toString(), wallet.getAddr(), toAddr, + amountNumber, coins.get(wallet.getTokenAddr()), EthWalletConstants.TransferType.FAST, + EthWalletConstants.StatusType.OUT_SUCCESS, date); // 快速到账记录 + } else { + // 查询是否存在提现黑名单中 + // 抛出错误表示用户禁止提现 + userFeign.verifyBanWithdraw(userOpenId); + // 查询手续费操作 + GasDTO ethGasDTO = configWalletParamService.getGasConfig(wallet.getTokenSymbol()); + // 判断用户是否存在提现白名单中 + ResultDTO resultDto = userFeign.verifyFreeWithdraw(userOpenId); + if (resultDto.getCode() != BaseConstant.REQUEST_SUCCESS) + throw new RPCException(resultDto.getCode(), resultDto.getMsg()); + // 用户存在提现白名单中,设置提现手续费为零 + if (resultDto.getData()) ethGasDTO.setGasPrice(BigDecimal.ZERO); + // 增加提现申请记录,冻结提现余额 + // ——冻结余额 + if (ethGasDTO.getMinWdAmount().compareTo(amountNumber) > 0) { // 与最小提现数额做对比 + throw new EthWalletException(EthWalletEnums.NUMBER_MINWDAMOUNT_ERROR); + } + int row = ethWalletMapper.updateBalanceByAddrInRowLock(wallet.getAddr(), wallet.getTokenAddr(), walletType, + BigDecimal.ZERO, amountNumber.negate(), amountNumber, date); // 冻结余额操作 + if (row == 0) { + throw new EthWalletException(EthWalletEnums.NUMBER_INSUFFICIENT_ERROR); + } + // ——生成提现申请记录 + if (ethGasDTO == null) { + tx = ethWalletTransferService.insert(UUID.randomUUID().toString(), wallet.getAddr(), toAddr, + amountNumber, coins.get(wallet.getTokenAddr()), EthWalletConstants.TransferType.OUT, + EthWalletConstants.StatusType.OUT_LOAD1, date); + } else { + BigDecimal gas = ethGasDTO.getGasPrice(); // 格式化手续费 + tx = ethWalletTransferService.insert(UUID.randomUUID().toString(), wallet.getAddr(), toAddr, + amountNumber, + gas, coins.get(wallet.getTokenAddr()), coins.get(ethGasDTO.getGasTokenType()), + EthWalletConstants.TransferType.OUT, EthWalletConstants.StatusType.OUT_LOAD1, date); + } + } + return tx; + } + + @Override + @TxTransaction + @Transactional + public void updateBlanceTransform(WalletOrderDTO orderDTO) { + ExceptionPreconditionUtils.checkNotNull(orderDTO, new EthWalletException(EthWalletEnums.NULL_ERROR)); + EthToken ethToken = ethTokenService.findByTokenName(orderDTO.getTokenName()); + ExceptionPreconditionUtils.checkNotNull(orderDTO.getFreeBalance(), + new EthWalletException(EthWalletEnums.NULL_FREEBLANCE)); + ExceptionPreconditionUtils.checkNotNull(orderDTO.getFreezeBalance(), + new EthWalletException(EthWalletEnums.NULL_FREEZEBLANCE)); + if (orderDTO.getFreeBalance().compareTo(orderDTO.getFreezeBalance().negate()) != 0) { + throw new EthWalletException(EthWalletEnums.DATA_EXCEPTION_ERROR); + } + this.updateBalanceByUserOpenId(orderDTO.getUserId(), ethToken.getTokenAddr(), orderDTO.getWalletType(), + orderDTO.getFreeBalance(), orderDTO.getFreezeBalance(), new Date()); + } + + @Override + @TxTransaction + @Transactional + public void updateBlance(WalletChangeDTO changeDTO) { + ExceptionPreconditionUtils.checkNotNull(changeDTO, new EthWalletException(EthWalletEnums.NULL_ERROR)); + EthToken ethToken = ethTokenService.findByTokenName(changeDTO.getTokenName()); + Date date = new Date(); + EthWalletDTO ethWalletDTO = this.updateBalanceByUserOpenId(changeDTO.getUserId(), ethToken.getTokenAddr(), + changeDTO.getWalletType(), changeDTO.getFreeBalance(), changeDTO.getFreezeBalance(), date); + BigDecimal count = changeDTO.getFreeBalance().add(changeDTO.getFreezeBalance()); + if (count.compareTo(BigDecimal.ZERO) >= 0) { + ethWalletTransferService.insert(changeDTO.getRecordId(), DEFAULT, ethWalletDTO.getAddr(), count.abs(), + ethToken, ethWalletDTO.getWalletType(), date); + } else { + ethWalletTransferService.insert(changeDTO.getRecordId(), ethWalletDTO.getAddr(), DEFAULT, count.abs(), + ethToken, ethWalletDTO.getWalletType(), date); + } + } + + @Override + @Transactional + public void updateTxOutError(EthWalletTransfer tx) { + // 数据验证 + EthWalletDTO wallet = selectByAddrAndTokenAddr(tx.getFromAddr(), tx.getTokenAddr()); + Date date = new Date(); + // 业务操作(1)- 恢复提现余额 + BigDecimal amount = tx.getAmount(); + this.updateBalanceByAddr(wallet.getAddr(), wallet.getTokenAddr(), wallet.getWalletType(), amount, amount.negate(), date); + // 业务操作(2)- 修改记录状态 + ethWalletTransferService.updateStatus(tx.getId(), EthWalletConstants.StatusType.OUT_ERROR, date); + } + + @Override + @Transactional + public void updateTxOutSuccess(EthWalletTransfer tx) { + // 数据验证 + EthWalletDTO wallet = selectByAddrAndTokenAddr(tx.getFromAddr(), tx.getTokenAddr()); + Date date = new Date(); + // 业务操作(1)- 扣除提现余额 + BigDecimal amount = tx.getAmount(); + this.updateBalanceByAddr(wallet.getAddr(), wallet.getTokenAddr(), wallet.getWalletType(),BigDecimal.ZERO, amount.negate(), date); + // 业务操作(2)- 修改记录状态 + ethWalletTransferService.updateStatus(tx.getId(), EthWalletConstants.StatusType.OUT_SUCCESS,date); + } + + + @Override + @Transactional + public EthWalletTransfer handleTransfer(String userId,String fromType, String toType, String coinName, BigDecimal amount) { + Date end = new Date(); + amount = amount.abs(); + //数据验证处理 + ethApplicationService.CheckWalletType(fromType); + ethApplicationService.CheckWalletType(toType); + EthToken token = ethTokenService.findByTokenName(coinName); + //錢包查詢處理 + EthWallet where = new EthWallet(); + where.setTokenSymbol(coinName); + where.setWalletType(fromType); + where.setUserOpenId(userId); + EthWallet fromWallet = ethWalletMapper.selectOne(where); + where.setWalletType(toType); + EthWallet toWallet = ethWalletMapper.selectOne(where); + //余额修改 + this.updateBalanceByAddr(fromWallet.getAddr(),token.getTokenAddr(),fromType,amount.negate(),BigDecimal.ZERO,end); + this.updateBalanceByAddr(toWallet.getAddr(), token.getTokenAddr(), toType, amount, BigDecimal.ZERO, end); + // 记录 + EthWalletTransfer tx = ethWalletTransferService.insert(UUID.randomUUID().toString(), fromWallet.getAddr(), toWallet.getAddr(), + amount, token, EthWalletConstants.TransferType.FAST, + EthWalletConstants.StatusType.OUT_SUCCESS, end); // 快速到账记录 + return tx; + } + + /** + * 检查币种地址是否正确 + * + * @param tokenAddr 币种地址 + */ + private EthToken checkTokenAddr(String tokenAddr) { + ExceptionPreconditionUtils.checkStringNotBlank(tokenAddr, + new EthWalletException(EthWalletEnums.NULL_TOKENADDR)); + List list = ethTokenService.selectAll(); + for (EthToken row : list) { + if (row.getTokenAddr().equalsIgnoreCase(tokenAddr)) { + return row; + } + } + throw new EthWalletException(EthWalletEnums.INEXISTENCE_TOKENADDR); + } + + /** + * 创建某应用的单个钱包简洁方法 + * + * @param userOpenId 用户ID + * @param addr 钱包地址 + * @param tokenAddr 币种地址 + * @param tokenDecimals 小数位 + * @param tokenSymbol 全称 + * @param walletType 钱包类型 + * @return + */ + private void insertMonth(String userOpenId, String addr, String tokenAddr, int tokenDecimals, String tokenSymbol, + String walletType) { + Date date = new Date(); + EthWallet ethWallet = new EthWallet(); + ethWallet.setAddr(addr); + ethWallet.setTokenAddr(tokenAddr.toLowerCase()); + ethWallet.setBalance(BigDecimal.ZERO); + ethWallet.setFreeBalance(BigDecimal.ZERO); + ethWallet.setFreezeBalance(BigDecimal.ZERO); + ethWallet.setTokenDecimals(tokenDecimals); + ethWallet.setUserOpenId(userOpenId); + ethWallet.setTokenSymbol(tokenSymbol); + ethWallet.setWalletType(walletType); + ethWallet.setCreateTime(date); + ethWallet.setUpdateTime(date); + try { + int ethWalletInt = ethWalletMapper.insertSelective(ethWallet); + if (ethWalletInt == 0) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + } catch (EthWalletException e) { + // 未插入成功的异常 + throw e; + } catch (Exception e) { + // 重复创建的异常 + throw new EthWalletException(EthWalletEnums.INSERT_ADDWALLERT); + } + } + + /** + * 修改指定应用钱包修改余额的简洁方法 + * + * @param userOpenId + * @param tokenAddr + * @param walletType + * @param amount + */ + private EthWalletDTO updateBalanceByUserIdMonth(String userOpenId, String tokenAddr, String walletType, + BigDecimal amount, Date date) { + return this.updateBalanceByUserOpenId(userOpenId, tokenAddr, walletType, amount, BigDecimal.ZERO, date); + } + + /** + * 修改指定应用钱包修改余额的简洁方法 + * + * @param addr + * @param tokenAddr + * @param walletType + * @param amount + */ + private EthWalletDTO updateBalanceByAddrMonth(String addr, String tokenAddr, String walletType, BigDecimal amount + , Date date) { + // 进行行锁转账(应用钱包-应用钱包) + return this.updateBalanceByAddr(addr, tokenAddr, walletType, amount, BigDecimal.ZERO, date); + } + + /** + * 修改指定应用钱包修改余额的简洁方法 + * + * @param addr + * @param tokenAddr + * @param walletType + * @param tokenAddr + * @param walletType + */ + private EthWalletDTO updateBalanceByAddr(String addr, String tokenAddr, String walletType, BigDecimal freeBlance, + BigDecimal freezeBlance, Date date) { + // 数据验证 + ExceptionPreconditionUtils.checkNotNull(freeBlance, new EthWalletException(EthWalletEnums.NULL_FREEBLANCE)); + ExceptionPreconditionUtils.checkNotNull(freezeBlance, new EthWalletException(EthWalletEnums.NULL_FREEZEBLANCE)); + EthWalletDTO walletDTO = selectByAddrAndTokenAddrAndWalletType(addr, tokenAddr, walletType); + if (freeBlance.add(new BigDecimal(walletDTO.getFreeBalance())).compareTo(BigDecimal.ZERO) < 0) { + throw new EthWalletException(EthWalletEnums.NUMBER_INSUFFICIENT_ERROR); + } + if (freezeBlance.add(new BigDecimal(walletDTO.getFreezeBalance())).compareTo(BigDecimal.ZERO) < 0) { + throw new EthWalletException(EthWalletEnums.NUMBER_INSUFFICIENTZE_ERROR); + } + // 进行行锁转账(应用钱包-应用钱包) + int row = ethWalletMapper.updateBalanceByAddrInRowLock(addr, tokenAddr, walletType, freeBlance.add(freezeBlance), + freeBlance, freezeBlance, date); + if (row == 0) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + return selectByAddrAndTokenAddrAndWalletType(addr, tokenAddr, walletType); + } + + /** + * 修改指定应用钱包修改余额的简洁方法 + * + * @param userOpenId + * @param tokenAddr + * @param walletType + * @param tokenAddr + * @param walletType + */ + private EthWalletDTO updateBalanceByUserOpenId(String userOpenId, String tokenAddr, String walletType, + BigDecimal freeBlance, BigDecimal freezeBlance, Date date) { + // 数据验证 + ExceptionPreconditionUtils.checkNotNull(freeBlance, new EthWalletException(EthWalletEnums.NULL_FREEBLANCE)); + ExceptionPreconditionUtils.checkNotNull(freezeBlance, new EthWalletException(EthWalletEnums.NULL_FREEZEBLANCE)); + EthWalletDTO walletDTO = selectByUserOpenIdAndTokenAddrAndWalletType(userOpenId, tokenAddr, walletType); + if (freeBlance.add(new BigDecimal(walletDTO.getFreeBalance())).compareTo(BigDecimal.ZERO) < 0) { + throw new EthWalletException(EthWalletEnums.NUMBER_INSUFFICIENT_ERROR); + } + if (freezeBlance.add(new BigDecimal(walletDTO.getFreezeBalance())).compareTo(BigDecimal.ZERO) < 0) { + throw new EthWalletException(EthWalletEnums.NUMBER_INSUFFICIENTZE_ERROR); + } + // 进行行锁转账(应用钱包-应用钱包) + int row = ethWalletMapper.updateBalanceByAddrInRowLock(walletDTO.getAddr(), tokenAddr, walletType, + freeBlance.add(freezeBlance), + freeBlance, freezeBlance, date); + if (row == 0) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + return selectByAddrAndTokenAddrAndWalletType(walletDTO.getAddr(), tokenAddr, walletType); + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthWalletTransferServiceImpl.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthWalletTransferServiceImpl.java new file mode 100644 index 0000000..dd8376d --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/service/impl/EthWalletTransferServiceImpl.java @@ -0,0 +1,164 @@ +package com.blockchain.server.eth.service.impl; + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.eth.common.constants.EthWalletConstants; +import com.blockchain.server.eth.common.enums.EthWalletEnums; +import com.blockchain.server.eth.common.exception.EthWalletException; +import com.blockchain.server.eth.common.util.BaseHelperUtil; +import com.blockchain.server.eth.common.util.DataCheckUtil; +import com.blockchain.server.eth.dto.EthWalletDTO; +import com.blockchain.server.eth.entity.EthToken; +import com.blockchain.server.eth.entity.EthWalletTransfer; +import com.blockchain.server.eth.mapper.EthWalletTransferMapper; +import com.blockchain.server.eth.service.IEthWalletService; +import com.blockchain.server.eth.service.IEthWalletTransferService; +import com.blockchain.server.eth.web3j.IWalletWeb3j; +import com.github.pagehelper.PageHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.web3j.protocol.core.methods.response.TransactionReceipt; + +import javax.xml.crypto.Data; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +/** + * 以太坊钱包记录表——业务接口 + * + * @author YH + * @date 2019年2月16日17:09:19 + */ +@Service +public class EthWalletTransferServiceImpl implements IEthWalletTransferService { + + static final String DEFAULT = ""; + + @Autowired + EthWalletTransferMapper ethWalletTransferMapper; + @Autowired + IEthWalletService ethWalletService; + @Autowired + IWalletWeb3j walletWeb3j; + + @Override + public EthWalletTransfer insert(String hash, String fromAddr, String toAddr, BigDecimal amount, + EthToken amountCoin, String transferType, Date date) { + return insert(hash, fromAddr, toAddr, amount, amountCoin.getTokenAddr(), amountCoin.getTokenSymbol(), + BigDecimal.ZERO, DEFAULT, DEFAULT, DEFAULT, transferType, EthWalletConstants.StatusType.SUCCESS, + DEFAULT,date); + } + + @Override + public EthWalletTransfer insert(String hash, String fromAddr, String toAddr, BigDecimal amount, + EthToken amountCoin, String transferType, int status, Date date) { + return insert(hash, fromAddr, toAddr, amount, amountCoin.getTokenAddr(), amountCoin.getTokenSymbol(), + BigDecimal.ZERO, DEFAULT, DEFAULT, DEFAULT, transferType, status, + DEFAULT,date); + } + + @Override + public EthWalletTransfer insert(String hash, String fromAddr, String toAddr, BigDecimal amount, BigDecimal gas, + EthToken amountCoin, EthToken gasCoin, String transferType, int status, Date date) { + return insert(hash, fromAddr, toAddr, amount, amountCoin.getTokenAddr(), amountCoin.getTokenSymbol(), gas, + gasCoin.getTokenAddr(), gasCoin.getTokenSymbol(), gasCoin.getTokenSymbol(), transferType, status, + DEFAULT,date); + } + + @Override + public EthWalletTransfer insert(String hash, String fromAddr, String toAddr, BigDecimal amount, BigDecimal gas, + EthToken amountCoin, EthToken gasCoin, String transferType, int status, + String remark, Date date) { + return insert(hash, fromAddr, toAddr, amount, amountCoin.getTokenAddr(), amountCoin.getTokenSymbol(), gas, + gasCoin.getTokenAddr(), gasCoin.getTokenSymbol(), gasCoin.getTokenSymbol(), transferType, status, + remark,date); + } + + @Override + @Transactional + public EthWalletTransfer updateGas(EthWalletTransfer ethWalletTransfer) { + TransactionReceipt receipt = walletWeb3j.getTransactionStatusByHash(ethWalletTransfer.getHash()); + BigInteger gas = ethWalletTransfer.getGasPrice().multiply(new BigDecimal(receipt.getGasUsed())).toBigInteger(); + ethWalletTransfer.setGasPrice(DataCheckUtil.bitToBigDecimal(gas)); + ethWalletTransfer.setUpdateTime(new Date()); + // 失败抛出异常 + int row = ethWalletTransferMapper.updateByPrimaryKeySelective(ethWalletTransfer); + if (row == 0) throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + // 判断记录是否成功,ture成功,false失败 + int status = receipt.isStatusOK() ? EthWalletConstants.StatusType.IN_SUCCESS : + EthWalletConstants.StatusType.IN_ERROR; + ethWalletTransfer.setStatus(status); + return ethWalletTransfer; + } + + @Override + @Transactional + public void updateStatus(EthWalletTransfer ethWalletTransfer) { + // 失败抛出异常 + int row = ethWalletTransferMapper.updateByPrimaryKeySelective(ethWalletTransfer); + if (row == 0) throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + + @Override + @Transactional + public void updateStatus(String id, int status, Date date) { + EthWalletTransfer ethWalletTransfer = ethWalletTransferMapper.selectByPrimaryKey(id); + ExceptionPreconditionUtils.checkNotNull(ethWalletTransfer, + new EthWalletException(EthWalletEnums.INEXISTENCE_TX)); + ethWalletTransfer.setStatus(status); + ethWalletTransfer.setUpdateTime(date); + // 失败抛出异常 + int row = ethWalletTransferMapper.updateByPrimaryKeySelective(ethWalletTransfer); + if (row == 0) throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + + @Override + public List selectByTxTypeAndStatus(String transferType, int Status, + Integer pageNum, Integer pageSize) { + BaseHelperUtil.startPage(pageNum, pageSize); // 分页处理 + EthWalletTransfer where = new EthWalletTransfer(); + where.setTransferType(transferType); + where.setStatus(Status); + return ethWalletTransferMapper.select(where); + } + + @Override + public List selectTransfer(String userOpenId, String tokenAddr, String walletType) { + EthWalletDTO wallet = ethWalletService.selectByUserOpenIdAndTokenAddrAndWalletType(userOpenId, tokenAddr, + walletType); + return ethWalletTransferMapper.selectTransfer(wallet.getAddr(), tokenAddr); + } + + /** + * 插入一条记录数据的通用方法 + */ + private EthWalletTransfer insert(String hash, String fromAddr, String toAddr, BigDecimal amount, String tokenAddr + , String tokenSymbol, BigDecimal gasPrice, String gasTokenType, String gasTokenName, + String gasTokenSymbol, String transferType, int status, String remark, Date date) { + EthWalletTransfer ethWalletTransfer = new EthWalletTransfer(); + ethWalletTransfer.setId(UUID.randomUUID().toString()); + ethWalletTransfer.setHash(hash); + ethWalletTransfer.setAmount(amount); + ethWalletTransfer.setFromAddr(fromAddr); + ethWalletTransfer.setToAddr(toAddr); + ethWalletTransfer.setTokenAddr(tokenAddr); + ethWalletTransfer.setTokenSymbol(tokenSymbol); + ethWalletTransfer.setGasPrice(gasPrice); + ethWalletTransfer.setGasTokenType(gasTokenType); + ethWalletTransfer.setGasTokenName(gasTokenName); + ethWalletTransfer.setGasTokenSymbol(gasTokenSymbol); + ethWalletTransfer.setTransferType(transferType); + ethWalletTransfer.setStatus(status); + ethWalletTransfer.setRemark(remark); + ethWalletTransfer.setUpdateTime(date); + ethWalletTransfer.setCreateTime(date); + int row = ethWalletTransferMapper.insertSelective(ethWalletTransfer); + if (row == 0) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + return ethWalletTransfer; + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/IBaseWeb3j.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/IBaseWeb3j.java new file mode 100644 index 0000000..455577e --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/IBaseWeb3j.java @@ -0,0 +1,72 @@ +package com.blockchain.server.eth.web3j; + +import org.web3j.abi.TypeReference; +import org.web3j.abi.datatypes.Type; +import org.web3j.protocol.core.methods.response.EthBlock; +import org.web3j.protocol.core.methods.response.TransactionReceipt; + +import java.math.BigInteger; +import java.util.List; + +/** + * web3j 连接基础类 + */ +public interface IBaseWeb3j { + + /** + * 执行合约打包 + * + * @param fromAddr 支付地址 + * @param fromPrivateKey 支付地址私钥 + * @param hashVal 合约地址 + * @param month 合约方法 + * @param gasPrice 旷工费用 + * @param inputParameters 方法参数 + * @return hash + */ + String transact(String fromAddr, String fromPrivateKey, String hashVal, String month, BigInteger gasPrice, BigInteger gasLimit, List inputParameters); + + /** + * 调用智能合约常量方法 + * + * @param fromAddr 支付地址 + * @param hashVal 合约地址 + * @param month 方法名 + * @param inputParameters 接收参数 + * @param outputParameters 输出参数 + * @return + */ + List staticFun(String fromAddr, String hashVal, String month, List inputParameters, List> outputParameters); + + /** + * 根据hash值查询交易信息 + * + * @param hash + * @return + */ + TransactionReceipt getTransactionStatusByHash(String hash); + + /** + * 字符串转bytes长度 + * + * @param string 字符串 + * @param length 长度 + * @return + */ + byte[] stringToBytes(String string, int length); + + /** + * 获取以太坊最新区块 + * + * @return + */ + BigInteger getEthBlock(); + + /** + * 获取区块交易数据 + * + * @param block 区块号 + * @return + */ + List getTokenTransactionList(BigInteger block); +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/IWalletWeb3j.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/IWalletWeb3j.java new file mode 100644 index 0000000..67c3560 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/IWalletWeb3j.java @@ -0,0 +1,111 @@ +package com.blockchain.server.eth.web3j; + +import com.blockchain.server.eth.dto.Web3jWalletDTO; + +import java.math.BigInteger; + +/** + * 关于以太坊钱包操作的工具类 + */ +public interface IWalletWeb3j extends IBaseWeb3j { + + /** + * 根据用户输入的托管钱包密码,创建“以太坊托管钱包” + * + * @param password 托管钱包密码 + * @return 数据对象 + */ + Web3jWalletDTO creationEthWallet(String password); + + /** + * 根据用户的私钥,修改托管钱包密码 + * + * @param privateKey 私钥 + * @param passwordNew 新密码 + * @return + */ + Web3jWalletDTO updateEthWallet(String privateKey, String passwordNew); + + /** + * 验证钱包密码 + * + * @param keystore + * @param password + * @return + */ + void isPassword(String keystore, String password); + + + /** + * 查询ETH余额 + * + * @param address 钱包地址 + * @return 余额 + */ + BigInteger getEthBalance(String address); + + /** + * 查询代币余额 + * + * @param addr 钱包地址 + * @param tokenAddr 代币地址 + * @return 余额 + */ + BigInteger getTokenBalance(String addr, String tokenAddr); + + /** + * 查询代币小数位 + * + * @return 小数位 + */ + int getEthDecimal(); + + /** + * ETH 转账 + * + * @param fromAddr 支付地址 + * @param fromPrivateKey 支付地址私钥 + * @param toAddr 接收地址 + * @param tBalance 转账的余额 + * @param gasPrice 燃烧的费用 + * @return hash + */ + String ethWalletTransfer(String fromAddr, String fromPrivateKey, String toAddr, BigInteger tBalance, BigInteger gasPrice); + + /** + * ETH 转账 + * + * @param fromAddr 支付地址 + * @param fromPrivateKey 支付地址私钥 + * @param toAddr 接收地址 + * @param tBalance 转账的余额 + * @return hash + */ + String ethWalletTransfer(String fromAddr, String fromPrivateKey, String toAddr, BigInteger tBalance); + + /** + * 代币转账 + * + * @param fromAddr 支付地址 + * @param tokenAddr 代币地址 + * @param fromPrivateKey 支付地址私钥 + * @param toAddr 接收地址 + * @param tBalance 转账代币余额 + * @param gasPrice 旷工费用 + * @return hash + */ + String ethWalletTokenTransfer(String fromAddr, String tokenAddr, String fromPrivateKey, String toAddr, BigInteger tBalance, BigInteger gasPrice); + + /** + * 代币转账 + * + * @param fromAddr 支付地址 + * @param tokenAddr 代币地址 + * @param fromPrivateKey 支付地址私钥 + * @param toAddr 接收地址 + * @param tBalance 转账代币余额 + * @return hash + */ + String ethWalletTokenTransfer(String fromAddr, String tokenAddr, String fromPrivateKey, String toAddr, BigInteger tBalance); + +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/impl/BaseWeb3jImpl.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/impl/BaseWeb3jImpl.java new file mode 100644 index 0000000..17e7d7d --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/impl/BaseWeb3jImpl.java @@ -0,0 +1,170 @@ +package com.blockchain.server.eth.web3j.impl; + +import com.blockchain.server.eth.common.config.EthConfig; +import com.blockchain.server.eth.common.enums.EthWalletEnums; +import com.blockchain.server.eth.common.exception.EthWalletException; +import com.blockchain.server.eth.dto.Web3jWalletDTO; +import com.blockchain.server.eth.web3j.IBaseWeb3j; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.web3j.abi.FunctionEncoder; +import org.web3j.abi.FunctionReturnDecoder; +import org.web3j.abi.TypeReference; +import org.web3j.abi.datatypes.Function; +import org.web3j.abi.datatypes.Type; +import org.web3j.crypto.*; +import org.web3j.protocol.ObjectMapperFactory; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.admin.Admin; +import org.web3j.protocol.core.DefaultBlockParameter; +import org.web3j.protocol.core.DefaultBlockParameterName; +import org.web3j.protocol.core.methods.response.*; +import org.web3j.protocol.geth.Geth; +import org.web3j.protocol.http.HttpService; +import org.web3j.protocol.ipc.UnixIpcService; +import org.web3j.protocol.ipc.WindowsIpcService; +import org.web3j.utils.Numeric; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; + +@Component +public class BaseWeb3jImpl implements IBaseWeb3j { + Admin admin; + Web3j web3j; + Geth geth; + + @Autowired + EthConfig ethConfig; + + // ETH小数位 + static final int ETH_DECIMALS = 18; + + + public BaseWeb3jImpl(EthConfig ethConfig) { + if (ethConfig.getUrlType().equals("ipc")) { + if (ethConfig.getOs().equals("windows")) { + this.admin = Admin.build(new WindowsIpcService(ethConfig.getUrl())); + this.web3j = Web3j.build(new WindowsIpcService(ethConfig.getUrl())); + this.geth = Geth.build(new WindowsIpcService(ethConfig.getUrl())); + } else if (ethConfig.getOs().equals("unix")) { + this.admin = Admin.build(new UnixIpcService(ethConfig.getUrl())); + this.web3j = Web3j.build(new UnixIpcService(ethConfig.getUrl())); + this.geth = Geth.build(new UnixIpcService(ethConfig.getUrl())); + } + } else if (ethConfig.getUrlType().equals("rpc")) { + this.admin = Admin.build(new HttpService(ethConfig.getUrl())); + this.web3j = Web3j.build(new HttpService(ethConfig.getUrl())); + this.geth = Geth.build(new HttpService(ethConfig.getUrl())); + } + } + + /** + * 执行合约打包 + * + * @param fromAddr 支付地址 + * @param fromPrivateKey 支付地址私钥 + * @param hashVal 合约地址 + * @param month 合约方法 + * @param gasPrice 旷工费用 + * @param inputParameters 方法参数 + * @return hash + */ + @Override + public String transact(String fromAddr, String fromPrivateKey, String hashVal, String month, BigInteger gasPrice, + BigInteger gasLimit, List inputParameters) { + try { + EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount( + fromAddr, + DefaultBlockParameterName.LATEST + ).send(); + BigInteger nonce = ethGetTransactionCount.getTransactionCount(); + Function function = new Function( + month, + inputParameters, + Arrays.asList(new TypeReference() { + })); + Credentials credentials = Credentials.create(fromPrivateKey); + String encodedFunction = FunctionEncoder.encode(function); + RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, hashVal, + encodedFunction); + byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials); + String hexValue = Numeric.toHexString(signedMessage); + EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get(); + String hash = ethSendTransaction.getTransactionHash(); + return hash; + } catch (Exception e) { + e.printStackTrace(); + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + } + + @Override + public List staticFun(String fromAddr, String hashVal, String month, List inputParameters, + List> outputParameters) { + try { + Function function = new Function(month, inputParameters, outputParameters); + String functionEncoder = FunctionEncoder.encode(function); + org.web3j.protocol.core.methods.request.Transaction transaction = + org.web3j.protocol.core.methods.request.Transaction.createEthCallTransaction(fromAddr, hashVal, + functionEncoder); + EthCall response = web3j.ethCall(transaction, DefaultBlockParameterName.LATEST).send(); + List types = FunctionReturnDecoder.decode(response.getValue(), function.getOutputParameters()); + return types; + } catch (IOException e) { + e.printStackTrace(); + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + } + + + @Override + public TransactionReceipt getTransactionStatusByHash(String hash) { + try { + EthTransaction ethTransaction = web3j.ethGetTransactionByHash(hash).send(); + if (ethTransaction.getResult() == null) { + TransactionReceipt transactionReceipt = new TransactionReceipt(); + transactionReceipt.setTransactionHash(hash); + transactionReceipt.setGasUsed("0x0"); + transactionReceipt.setStatus("0x16"); + return transactionReceipt; + } + EthGetTransactionReceipt ethGetTransactionReceipt = web3j.ethGetTransactionReceipt(hash).send(); + return ethGetTransactionReceipt.getTransactionReceipt().get(); + } catch (Exception e) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + } + + @Override + public byte[] stringToBytes(String string, int length) { + byte[] byteValue = string.getBytes(); + byte[] byteValueLen = new byte[length]; + System.arraycopy(byteValue, 0, byteValueLen, 0, byteValue.length); + return byteValueLen; + } + + @Override + public BigInteger getEthBlock() { + try { + return web3j.ethBlockNumber().send().getBlockNumber(); + } catch (Exception e) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + } + + @Override + public List getTokenTransactionList(BigInteger block) { + try { + EthBlock.Block ethBlock = + web3j.ethGetBlockByNumber(DefaultBlockParameter.valueOf(block), true).send().getBlock(); + return ethBlock.getTransactions(); + } catch (Exception e) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/impl/WalletWeb3jImpl.java b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/impl/WalletWeb3jImpl.java new file mode 100644 index 0000000..1357be6 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/java/com/blockchain/server/eth/web3j/impl/WalletWeb3jImpl.java @@ -0,0 +1,258 @@ +package com.blockchain.server.eth.web3j.impl; + +import com.blockchain.server.eth.common.config.EthConfig; +import com.blockchain.server.eth.common.enums.EthWalletEnums; +import com.blockchain.server.eth.common.exception.EthWalletException; +import com.blockchain.server.eth.dto.Web3jWalletDTO; +import com.blockchain.server.eth.web3j.IWalletWeb3j; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.web3j.abi.FunctionEncoder; +import org.web3j.abi.FunctionReturnDecoder; +import org.web3j.abi.TypeReference; +import org.web3j.abi.datatypes.Address; +import org.web3j.abi.datatypes.Function; +import org.web3j.abi.datatypes.Type; +import org.web3j.abi.datatypes.generated.Uint256; +import org.web3j.crypto.*; +import org.web3j.protocol.ObjectMapperFactory; +import org.web3j.protocol.core.DefaultBlockParameterName; +import org.web3j.protocol.core.methods.request.Transaction; +import org.web3j.protocol.core.methods.response.EthCall; +import org.web3j.protocol.core.methods.response.EthGetBalance; +import org.web3j.protocol.core.methods.response.EthGetTransactionCount; +import org.web3j.protocol.core.methods.response.EthSendTransaction; +import org.web3j.utils.Numeric; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 关于以太坊钱包操作的工具类 + */ +@Component +public class WalletWeb3jImpl extends BaseWeb3jImpl implements IWalletWeb3j { + + // 查询代币余额参数 + static final String BALANCE_OF = "balanceOf"; + // 代币交易 + static final String TRANSFER = "transfer"; + // 托管钱包地址前缀 + static final String ADDRESS_HEADER = "0x"; + + // ETH转账的手续费 + @Value("${wallert.eth.gasLimit}") + BigInteger ETH_GAS_LIMIT; + @Value("${wallert.eth.gasPrice}") + BigInteger ETH_GAS_PRICE; + // TOKEN转账的手续费 + @Value("${wallert.eth.gasLimit}") + BigInteger ETH_TOKEN_GAS_LIMIT; + @Value("${wallert.eth.gasPrice}") + BigInteger ETH_TOKEN_GAS_PRICE; + + public WalletWeb3jImpl(EthConfig ethConfig) { + super(ethConfig); + } + + @Override + public Web3jWalletDTO creationEthWallet(String password) { + try { + ECKeyPair ecKeyPair = Keys.createEcKeyPair(); + WalletFile walletFile = org.web3j.crypto.Wallet.createStandard(password, ecKeyPair); + ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper(); + + String keystore = objectMapper.writeValueAsString(walletFile); // keystore + String privateKey = ecKeyPair.getPrivateKey().toString(16); // (转为16进制)私钥 + String address = ADDRESS_HEADER + walletFile.getAddress(); // 钱包地址 + + Web3jWalletDTO dto = new Web3jWalletDTO();//数据对象 + dto.setAddr(address); // 钱包地址private_key + dto.setPrivateKey(privateKey); // 生成私钥 + dto.setKeystore(keystore); // keystore + return dto; + } catch (Exception e) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + } + + @Override + public Web3jWalletDTO updateEthWallet(String privateKey, String passwordNew) { + try { + //根据私钥修改密码 + ECKeyPair ecKeyPair = ECKeyPair.create(new BigInteger(privateKey, 16)); + WalletFile walletFile = org.web3j.crypto.Wallet.createStandard(passwordNew, ecKeyPair); + ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper(); + String _keystore = objectMapper.writeValueAsString(walletFile); // 新密码生成的新的keystore + String address = ADDRESS_HEADER + walletFile.getAddress(); // 钱包地址 + + Web3jWalletDTO dto = new Web3jWalletDTO();//数据对象 + dto.setAddr(address); // 钱包地址private_key + dto.setPrivateKey(privateKey); // 生成私钥 + dto.setKeystore(_keystore); // keystore + return dto; + } catch (CipherException e) { + throw new EthWalletException(EthWalletEnums.PASSWORD_ERROR); + } catch (Exception e) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + } + + @Override + public void isPassword(String keystore, String password) { + try { + ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper(); + WalletFile walletFileVerify = objectMapper.readValue(keystore, WalletFile.class); + Wallet.decrypt(password, walletFileVerify).getPrivateKey(); + } catch (CipherException e) { + throw new EthWalletException(EthWalletEnums.PASSWORD_ERROR); + } catch (Exception e) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + } + + /** + * 查询ETH余额 + * + * @param address 钱包地址 + * @return 余额 + */ + @Override + public BigInteger getEthBalance(String address) { + try { + EthGetBalance ethGetBalance = web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send(); + return ethGetBalance.getBalance(); + } catch (Exception e) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + } + + /** + * 查询ETH小数位 + * + * @return 小数位 + */ + @Override + public int getEthDecimal() { + return ETH_DECIMALS; + } + + /** + * 查询代币余额 + * + * @param addr 钱包地址 + * @param tokenAddr 代币地址 + * @return 余额 + */ + @Override + public BigInteger getTokenBalance(String addr, String tokenAddr) { + String methodName = BALANCE_OF; + List inputParameters = new ArrayList<>(); + List outputParameters = new ArrayList<>(); + Address address = new Address(addr); + inputParameters.add(address); + TypeReference typeReference = new TypeReference() { + }; + outputParameters.add(typeReference); + Function function = new Function(methodName, inputParameters, outputParameters); + String data = FunctionEncoder.encode(function); + Transaction transaction = Transaction.createEthCallTransaction(addr, tokenAddr, data); + BigInteger balanceValue = BigInteger.ZERO; + try { + EthCall ethCall = web3j.ethCall(transaction, DefaultBlockParameterName.LATEST).send(); + List results = FunctionReturnDecoder.decode(ethCall.getValue(), function.getOutputParameters()); + return (BigInteger) results.get(0).getValue(); + } catch (Exception e) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + } + + /** + * ETH 转账 + * + * @param fromAddr 支付地址 + * @param fromPrivateKey 支付地址私钥 + * @param toAddr 接收地址 + * @param tBalance 转账的余额 + * @param gasPrice 燃烧的费用 + * @return hash + */ + @Override + public String ethWalletTransfer(String fromAddr, String fromPrivateKey, String toAddr, BigInteger tBalance, BigInteger gasPrice) { + try { + Credentials credentials = Credentials.create(fromPrivateKey); + EthGetTransactionCount ethGetTransactionCount = + web3j.ethGetTransactionCount(fromAddr, DefaultBlockParameterName.LATEST).send(); + BigInteger nonce = ethGetTransactionCount.getTransactionCount(); + RawTransaction rawTransaction = RawTransaction.createEtherTransaction(nonce, gasPrice, ETH_GAS_LIMIT, toAddr, tBalance); + byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials); + String hexValue = Numeric.toHexString(signedMessage); + EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send(); + return ethSendTransaction.getTransactionHash(); + } catch (Exception e) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + } + + /** + * ETH 转账 + * + * @param fromAddr 支付地址 + * @param fromPrivateKey 支付地址私钥 + * @param toAddr 接收地址 + * @param tBalance 转账的余额 + * @return hash + */ + @Override + public String ethWalletTransfer(String fromAddr, String fromPrivateKey, String toAddr, BigInteger tBalance) { + try { + Credentials credentials = Credentials.create(fromPrivateKey); + EthGetTransactionCount ethGetTransactionCount = + web3j.ethGetTransactionCount(fromAddr, DefaultBlockParameterName.LATEST).send(); + BigInteger nonce = ethGetTransactionCount.getTransactionCount(); + RawTransaction rawTransaction = RawTransaction.createEtherTransaction(nonce, ETH_GAS_PRICE, ETH_GAS_LIMIT, toAddr, tBalance); + byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials); + String hexValue = Numeric.toHexString(signedMessage); + EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send(); + return ethSendTransaction.getTransactionHash(); + } catch (Exception e) { + throw new EthWalletException(EthWalletEnums.SERVER_IS_TOO_BUSY); + } + } + + /** + * 代币转账 + * + * @param fromAddr 支付地址 + * @param tokenAddr 代币地址 + * @param fromPrivateKey 支付地址私钥 + * @param toAddr 接收地址 + * @param tBalance 转账代币余额 + * @param gasPrice 旷工费用 + * @return hash + */ + @Override + public String ethWalletTokenTransfer(String fromAddr, String tokenAddr, String fromPrivateKey, String toAddr, BigInteger tBalance, BigInteger gasPrice) { + List inputParameters = Arrays.asList(new Address(toAddr), new Uint256(tBalance)); + return transact(fromAddr, fromPrivateKey, tokenAddr, TRANSFER, gasPrice, ETH_TOKEN_GAS_LIMIT, inputParameters); + } + + /** + * 代币转账 + * + * @param fromAddr 支付地址 + * @param tokenAddr 代币地址 + * @param fromPrivateKey 支付地址私钥 + * @param toAddr 接收地址 + * @param tBalance 转账代币余额 + * @return hash + */ + @Override + public String ethWalletTokenTransfer(String fromAddr, String tokenAddr, String fromPrivateKey, String toAddr, BigInteger tBalance) { + List inputParameters = Arrays.asList(new Address(toAddr), new Uint256(tBalance)); + return transact(fromAddr, fromPrivateKey, tokenAddr, TRANSFER, ETH_TOKEN_GAS_PRICE, ETH_TOKEN_GAS_LIMIT, inputParameters); + } +} diff --git a/blockchain-server/blockchain-server-eth/src/main/resources/application.yml b/blockchain-server/blockchain-server-eth/src/main/resources/application.yml new file mode 100644 index 0000000..95aad49 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/resources/application.yml @@ -0,0 +1,3 @@ +#日志配置路径 +logging: + config: classpath:logback/logback-${spring.cloud.config.profile}.xml \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/resources/bootstrap.yml b/blockchain-server/blockchain-server-eth/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..7fa595c --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/resources/bootstrap.yml @@ -0,0 +1,22 @@ +server: + port: 8201 +#注册中心 +eureka: + client: + service-url: + defaultZone: http://eureka:8001/eureka/ + instance: + prefer-ip-address: true + instance-id: ${spring.cloud.client.ip-address}:${server.port} + hostname: ${spring.cloud.client.ip-address} +spring: + cloud: + #配置中心 + config: + discovery: + service-id: dapp-config-server + enabled: true + profile: dev + name: springconf,springcloudconf,redisconf,dbconf,txconf,xssconf,ethconf,ipconf + application: + name: dapp-eth-server diff --git a/blockchain-server/blockchain-server-eth/src/main/resources/logback/logback-dev.xml b/blockchain-server/blockchain-server-eth/src/main/resources/logback/logback-dev.xml new file mode 100644 index 0000000..9a0e324 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/resources/logback/logback-dev.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/resources/logback/logback-pro.xml b/blockchain-server/blockchain-server-eth/src/main/resources/logback/logback-pro.xml new file mode 100644 index 0000000..5b55059 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/resources/logback/logback-pro.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/resources/mapper/ConfigWalletParamMapper.xml b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/ConfigWalletParamMapper.xml new file mode 100644 index 0000000..d4e9dfd --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/ConfigWalletParamMapper.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthApplicationMapper.xml b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthApplicationMapper.xml new file mode 100644 index 0000000..63e9f4f --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthApplicationMapper.xml @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthBlockNumberMapper.xml b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthBlockNumberMapper.xml new file mode 100644 index 0000000..e2a8bb7 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthBlockNumberMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + dapp_eth_block_number + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthGasWalletMapper.xml b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthGasWalletMapper.xml new file mode 100644 index 0000000..ad9ad31 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthGasWalletMapper.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthTokenMapper.xml b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthTokenMapper.xml new file mode 100644 index 0000000..f6248d2 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthTokenMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + dapp_eth_token + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthTransferAuditingMapper.xml b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthTransferAuditingMapper.xml new file mode 100644 index 0000000..c140743 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthTransferAuditingMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletKeyMapper.xml b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletKeyMapper.xml new file mode 100644 index 0000000..6dce0cb --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletKeyMapper.xml @@ -0,0 +1,28 @@ + + + + + + + + dapp_eth_wallet_key + + + + UPDATE + + SET + keystore = #{ethWalletKey.keystore}, + update_time = #{ethWalletKey.updateTime} + WHERE + user_open_id = #{ethWalletKey.userOpenId} + AND + addr = #{ethWalletKey.addr} + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletMapper.xml b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletMapper.xml new file mode 100644 index 0000000..5d8f46b --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletMapper.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + dapp_eth_wallet + + + SELECT + user_open_id, + token_symbol, + balance, + create_time, + token_decimals, + wallet_type, + update_time, + free_balance, + addr, + token_addr, + freeze_balance + FROM + + + + + + + + + + + + + + + UPDATE + + wallet + SET + wallet.balance = wallet.balance + #{balance}, + wallet.free_balance = wallet.free_balance + #{freeBalance}, + wallet.freeze_balance = wallet.freeze_balance + #{freezeBalance}, + wallet.update_time = #{updateTime} + WHERE + user_open_id = #{userOpenId} + AND token_addr = #{tokenAddr} + AND wallet_type = #{walletType} + AND wallet.balance + #{balance} >= 0 + AND wallet.free_balance + #{freeBalance} >= 0 + AND wallet.freeze_balance + #{freezeBalance} >= 0 + + + + UPDATE + + wallet + SET + wallet.balance = wallet.balance + #{balance}, + wallet.free_balance = wallet.free_balance + #{freeBalance}, + wallet.freeze_balance = wallet.freeze_balance + #{freezeBalance}, + wallet.update_time = #{updateTime} + WHERE + addr = #{addr} + AND token_addr = #{tokenAddr} + AND wallet_type = #{walletType} + AND wallet.balance + #{balance} >= 0 + AND wallet.free_balance + #{freeBalance} >= 0 + AND wallet.freeze_balance + #{freezeBalance} >= 0 + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletOutMapper.xml b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletOutMapper.xml new file mode 100644 index 0000000..d810d86 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletOutMapper.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletTransferMapper.xml b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletTransferMapper.xml new file mode 100644 index 0000000..3edfc32 --- /dev/null +++ b/blockchain-server/blockchain-server-eth/src/main/resources/mapper/EthWalletTransferMapper.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + dapp_eth_wallet_transfer + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-imJg/pom.xml b/blockchain-server/blockchain-server-imJg/pom.xml new file mode 100644 index 0000000..848f099 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/pom.xml @@ -0,0 +1,33 @@ + + + + blockchain-server + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + blockchain-server-imjg + + + + com.blockchain + blockchain-server-base + 1.0-SNAPSHOT + + + com.blockchain + blockchain-common-tx + 1.0-SNAPSHOT + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/ImJgApplication.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/ImJgApplication.java new file mode 100644 index 0000000..d24cb80 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/ImJgApplication.java @@ -0,0 +1,15 @@ +package com.blockchain.server.imjg; + +import com.blockchain.server.base.BaseConf; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication(scanBasePackageClasses = {BaseConf.class, ImJgApplication.class}) +public class ImJgApplication { + + public static void main(String[] args) { + System.out.println("==========================1.ImJg--Main=============================="); + SpringApplication.run(ImJgApplication.class, args); + } + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/enums/MessageLogEnums.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/enums/MessageLogEnums.java new file mode 100644 index 0000000..71221ad --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/enums/MessageLogEnums.java @@ -0,0 +1,26 @@ +package com.blockchain.server.imjg.common.enums; + +import lombok.Getter; +import lombok.Setter; + +public enum MessageLogEnums { + + STATUS_NORMAL("NORMAL", "正常"), + STATUS_CANCEL("CANCEL", "已删除"); + + MessageLogEnums(String code, String name) { + this.code=code; + this.name=name; + } + + @Setter + @Getter + /** **/ + private String code; + + @Setter + @Getter + /** **/ + private String name; + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/enums/NodeCueEnums.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/enums/NodeCueEnums.java new file mode 100644 index 0000000..67eaf66 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/enums/NodeCueEnums.java @@ -0,0 +1,26 @@ +package com.blockchain.server.imjg.common.enums; + + +import lombok.Getter; +import lombok.Setter; + +public enum NodeCueEnums { + + NODE_CUE_NO(0,"不是节点消息"), + NODE_CUE_ALL(1,"双方可见的节点消息"), + NODE_CUE_SEND(2,"接收方可见的节点消息"); + + + NodeCueEnums(int code, String name){ + this.code = code; + this.name = name; + } + + @Setter + @Getter + private int code; + + @Setter + @Getter + private String name; +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/util/HttpUtils.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/util/HttpUtils.java new file mode 100644 index 0000000..adfaefc --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/util/HttpUtils.java @@ -0,0 +1,206 @@ +package com.blockchain.server.imjg.common.util; + +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; + +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class HttpUtils { + + + +public static String doGet(String url, Map param) { + + // 创建Httpclient对象 + CloseableHttpClient httpclient = HttpClients.createDefault(); + + String resultString = ""; + CloseableHttpResponse response = null; + try { + // 创建uri + URIBuilder builder = new URIBuilder(url); + if (param != null) { + for (String key : param.keySet()) { + builder.addParameter(key, param.get(key)); + } + } + URI uri = builder.build(); + + // 创建http GET请求 + HttpGet httpGet = new HttpGet(uri); + //httpGet.setHeader("Authorization","Basic "+code); + response = httpclient.execute(httpGet); + + if (response.getStatusLine().getStatusCode() == 200) { + resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (response != null) { + response.close(); + } + httpclient.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return resultString; + } + + + public static String doGet(String url) { + return doGet(url, null); + } + + public static String doPost(String url, Map param) { + CloseableHttpClient httpClient = HttpClients.createDefault(); + CloseableHttpResponse response = null; + String resultString = ""; + try { + + HttpPost httpPost = new HttpPost(url); + if (param != null) { + List paramList = new ArrayList<>(); + for (String key : param.keySet()) { + paramList.add(new BasicNameValuePair(key, param.get(key))); + } + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList); + httpPost.setEntity(entity); + } + response = httpClient.execute(httpPost); + resultString = EntityUtils.toString(response.getEntity(), "utf-8"); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + response.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return resultString; + } + + public static String doPost(String url) { + return doPost(url, null); + } + + public static String doPostJson(String url, String json) { + // 创建Httpclient对象 + CloseableHttpClient httpClient = HttpClients.createDefault(); + CloseableHttpResponse response = null; + String resultString = ""; + try { + // 创建Http Post请求 + HttpPost httpPost = new HttpPost(url); + // 创建请求内容 + StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); + httpPost.setEntity(entity); + // 执行http请求 + response = httpClient.execute(httpPost); + resultString = EntityUtils.toString(response.getEntity(), "utf-8"); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + response.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + return resultString; + } + public static String doJiGuangPost(String url, String json,String code) { + + System.out.println("url>>>>>>>>>>>>>>>>>>>"+url); + CloseableHttpClient httpClient = HttpClients.createDefault(); + CloseableHttpResponse response = null; + String resultString = ""; + try { + + HttpPost httpPost = new HttpPost(url); + StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); + httpPost.setEntity(entity); + httpPost.setHeader("Authorization","Basic "+code); + // 执行http请求 + response = httpClient.execute(httpPost); + resultString = EntityUtils.toString(response.getEntity(), "utf-8"); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + response.close(); + } catch (IOException e) { + System.out.println(">>>"+e); + e.printStackTrace(); + } + } + + return resultString; + } + + + public static String doJGGet(String url, Map param,String code) { + + // 创建Httpclient对象 + CloseableHttpClient httpclient = HttpClients.createDefault(); + + String resultString = ""; + CloseableHttpResponse response = null; + try { + + URIBuilder builder = new URIBuilder(url); + + if (param != null) { + for (String key : param.keySet()) { + builder.addParameter(key, param.get(key)); + } + } + URI uri = builder.build(); + + + HttpGet httpGet = new HttpGet(uri); + httpGet.setHeader("Authorization","Basic "+code); + + + response = httpclient.execute(httpGet); + + /* if (response.getStatusLine().getStatusCode() == 200) { + resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); + }*/ + resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (response != null) { + response.close(); + } + httpclient.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return resultString; + } + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/util/JiGuangIMUtils.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/util/JiGuangIMUtils.java new file mode 100644 index 0000000..d0a449c --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/common/util/JiGuangIMUtils.java @@ -0,0 +1,18 @@ +package com.blockchain.server.imjg.common.util; + +import org.apache.tomcat.util.codec.binary.Base64; + + +public class JiGuangIMUtils { + + + public static String getAuthString(String appkey,String secret){ + return Base64.encodeBase64String((appkey+":"+secret).getBytes()); + } + + public static String doRegister(String url,String json,String appkey,String secret){ + return HttpUtils.doJiGuangPost(url,json,JiGuangIMUtils.getAuthString(appkey,secret)); + } + + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/controller/ImjgController.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/controller/ImjgController.java new file mode 100644 index 0000000..1b5325f --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/controller/ImjgController.java @@ -0,0 +1,135 @@ +package com.blockchain.server.imjg.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.JsonUtils; +import com.blockchain.common.base.util.MD5Utils; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.imjg.common.util.HttpUtils; +import com.blockchain.server.imjg.common.util.JiGuangIMUtils; +import com.blockchain.server.imjg.controller.api.ImjgApi; +import com.blockchain.server.imjg.dto.*; +import com.blockchain.server.imjg.entity.ImjgMessage; +import com.blockchain.server.imjg.service.ImjgMessageLogService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +@Api(ImjgApi.JGIM_CONTROLLER_API) +@RestController +@RequestMapping("/jgim") +public class ImjgController { + + @Value("${JiGuang.appkey}") + private String appkey; + + @Value("${JiGuang.secret}") + private String secret; + + @Value("${JiGuang.api.address}") + private String address; + + @Value("${JiGuang.random_str}") + private String random_str; + + @Value("${JiGuang.api.address_v2}") + private String address_v2; + + @Value("${JiGuang.p_type}") + private String p_type; + + @Autowired + private ImjgMessageLogService imjgMessageLogService; + @Autowired + private RedisTemplate redisTemplate; + + @ApiOperation(value = ImjgApi.GetSignature.METHOD_API_NAME, notes = ImjgApi.GetSignature.METHOD_API_NOTE) + @GetMapping("/getSignature") + public ResultDTO getSignature(HttpServletRequest request) { + long timestamp = (new Date()).getTime(); + System.out.println("timestamp>>>" + timestamp); + String signature = MD5Utils.MD5("appkey=" + appkey + "×tamp=" + timestamp + "&random_str=" + random_str + "&key=" + secret); + JgInitDTO jgInitDTO = new JgInitDTO(timestamp, signature, random_str, appkey); + return ResultDTO.requstSuccess(jgInitDTO); + } + + /*@ApiOperation(value = ImjgApi.Register.METHOD_API_NAME, notes = ImjgApi.Register.METHOD_API_NOTE)*/ + @PostMapping("/register") + public ResultDTO register( + @ApiParam(ImjgApi.Register.METHOD_API_USERNAME) @RequestParam(name = "username") String username, + @ApiParam(ImjgApi.Register.METHOD_API_PASSWORD) @RequestParam(name = "password") String password, + HttpServletRequest request) { + return ResultDTO.requstSuccess(JiGuangIMUtils.doRegister(address + "/v1/admins/", JsonUtils.objectToJson(new JgUserDTO(username, password)), appkey, secret)); + } + + @ApiOperation(value = ImjgApi.GetUserMessages.METHOD_API_NAME, notes = ImjgApi.GetUserMessages.METHOD_API_NOTE) + @GetMapping("/getUserMessages") + public ResultDTO> getUserMessages(@ApiParam(ImjgApi.GetUserMessages.METHOD_API_DATAID) @RequestParam(value = "dataId") String dataId, + @ApiParam(ImjgApi.GetUserMessages.METHOD_API_USERID) @RequestParam(value = "userId") String userId, + @ApiParam(ImjgApi.GetUserMessages.METHOD_API_APPID) @RequestParam(value = "appId", required = false, defaultValue = "") String appId, + @ApiParam(ImjgApi.GetUserMessages.METHOD_API_TOKEN) @RequestParam(value = "token", required = false, defaultValue = "") String token, + @ApiParam(ImjgApi.GetUserMessages.METHOD_API_ORDER) @RequestParam(value = "order", defaultValue = "gmt_create asc ", required = false) String order, + @ApiParam(ImjgApi.GetUserMessages.METHOD_API_PAGE) @RequestParam(value = "page", defaultValue = "1", required = false) int page, + @ApiParam(ImjgApi.GetUserMessages.METHOD_API_SIZE) @RequestParam(value = "size", defaultValue = "100", required = false) int size, + HttpServletRequest request) { + userId = SSOHelper.getUserId(redisTemplate, request); + List logList = imjgMessageLogService.list(p_type, dataId, userId, order, page, size); + return ResultDTO.requstSuccess(logList); + } + + + @ApiOperation(value = ImjgApi.SaveMessageLog.METHOD_API_NAME, notes = ImjgApi.SaveMessageLog.METHOD_API_NOTE) + @PostMapping("/saveMessageLog") + public ResultDTO saveMessageLog( + @ApiParam(ImjgApi.SaveMessageLog.METHOD_API_DATAID) @RequestParam(value = "dataId") String dataId, + @ApiParam(ImjgApi.SaveMessageLog.METHOD_API_TARGETUSERID) @RequestParam(value = "targetUserId") String targetUserId, + @ApiParam(ImjgApi.SaveMessageLog.METHOD_API_FROMUSERID) @RequestParam(value = "fromUserId") String fromUserId, + @ApiParam(ImjgApi.SaveMessageLog.METHOD_API_MSGTYPE) @RequestParam(value = "msgType", defaultValue = "text") String msgType, + @ApiParam(ImjgApi.SaveMessageLog.METHOD_API_MSGBODY) @RequestParam(value = "msgBody") String msgBody, + @ApiParam(ImjgApi.SaveMessageLog.METHOD_API_NODECUE) @RequestParam(value = "nodeCue") int nodeCue, + HttpServletRequest request) { + fromUserId = SSOHelper.getUserId(redisTemplate, request); + imjgMessageLogService.save(p_type, dataId, fromUserId, targetUserId, msgType, msgBody, nodeCue); + return ResultDTO.requstSuccess(); + } + + + @ApiOperation(value = ImjgApi.GetJGAccount.METHOD_API_NAME, notes = ImjgApi.GetJGAccount.METHOD_API_NOTE) + @GetMapping("/getJGAccount") + public ResultDTO getJGAccount(HttpServletRequest request, + @ApiParam(ImjgApi.GetJGAccount.METHOD_API_USERID) @RequestParam(value = "userId", required = false, defaultValue = "") String userId, + @ApiParam(ImjgApi.GetJGAccount.METHOD_API_APPID) @RequestParam(value = "appId", required = false, defaultValue = "") String appId, + @ApiParam(ImjgApi.GetJGAccount.METHOD_API_TOKEN) @RequestParam(value = "token", required = false, defaultValue = "") String token) { + + if ("".equals(userId)) { + userId = SSOHelper.getUserId(redisTemplate, request); + return ResultDTO.requstSuccess(imjgMessageLogService.getJGAccount(userId)); + } else { + return ResultDTO.requstSuccess(imjgMessageLogService.getJGAccount(userId)); + } + + } + + + @GetMapping("/getResource") + @ApiOperation(value = ImjgApi.GetResource.METHOD_API_NAME, notes = ImjgApi.GetResource.METHOD_API_NOTE) + private ResultDTO getResource(@ApiParam(ImjgApi.GetResource.METHOD_API_MEDIA_ID) @RequestParam(value = "mediaId") String mediaId) { + Map params = new HashMap(); + params.put("mediaId", mediaId); + String response = HttpUtils.doJGGet(address + "/v1/resource", params, JiGuangIMUtils.getAuthString(appkey, secret)); + JgResourceDTO resourceDTO = JsonUtils.jsonToPojo(response, JgResourceDTO.class); + return ResultDTO.requstSuccess(resourceDTO); + } + + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/controller/ImjgUserController.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/controller/ImjgUserController.java new file mode 100644 index 0000000..8b14214 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/controller/ImjgUserController.java @@ -0,0 +1,16 @@ +package com.blockchain.server.imjg.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + + +public class ImjgUserController { + + + + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/controller/api/ImjgApi.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/controller/api/ImjgApi.java new file mode 100644 index 0000000..3cc2c06 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/controller/api/ImjgApi.java @@ -0,0 +1,80 @@ +package com.blockchain.server.imjg.controller.api; + +public class ImjgApi { + + public static final String JGIM_CONTROLLER_API = "极光IM控制器"; + + + public static class GetSignature{ + public static final String METHOD_API_NAME = "获取极光签名接口"; + public static final String METHOD_API_NOTE = "获取极光初始化的一些相关参数"; + + } + + public static class Register{ + public static final String METHOD_API_NAME = "极光IM注册接口"; + public static final String METHOD_API_NOTE = "注册用户到极光IM"; + public static final String METHOD_API_USERNAME="用户名"; + public static final String METHOD_API_PASSWORD="密码"; + } + + public static class GetUserMessages{ + public static final String METHOD_API_NAME = "获取用户的消息记录接口"; + public static final String METHOD_API_NOTE = "根据业务ID(订单ID)获取当前用户消息记录,一次20条"; + public static final String METHOD_API_DATAID = "业务ID"; + public static final String METHOD_API_USERID = "当前用户ID"; + + public static final String METHOD_API_ORDER = "排序方式"; + public static final String METHOD_API_PAGE = "页码"; + public static final String METHOD_API_SIZE = "每页大小"; + + public static final String METHOD_API_APPID = "appid"; + public static final String METHOD_API_TOKEN = "token"; + + } + + public static class SaveMessageLog{ + public static final String METHOD_API_NAME = "保存消息接口"; + public static final String METHOD_API_NOTE = "在页面发送极光消息成功后,保存消息到服务端"; + public static final String METHOD_API_DATAID = "业务ID"; + public static final String METHOD_API_TARGETUSERID = "消息接收方UserId"; + public static final String METHOD_API_FROMUSERID = "消息发送方UserId"; + public static final String METHOD_API_MSGTYPE = "消息类型"; + public static final String METHOD_API_MSGBODY = "消息体"; + public static final String METHOD_API_NODECUE = "是否是节点提示词(0:不是节点提示词,1:两边均可见的提示词,2:接收方可见的节点提示词)"; + } + + public static class GetJGAccount{ + public static final String METHOD_API_NAME = "获取用户极光的用户名"; + public static final String METHOD_API_NOTE = "获取当前用户的极光用户名"; + public static final String METHOD_API_USERID = "用户ID"; + public static final String METHOD_API_APPID = "appid"; + public static final String METHOD_API_TOKEN = "token"; + } + + public static class MsgBody{ + public static final String METHOD_API_NAME = "消息体具体字段(加*代表必传)"; + public static final String METHOD_API_NOTE = "发送消息记录中的消息体字段说明"; + + public static final String METHOD_API_MSGTYPE = "消息类型(text,voice,image,file,video,location,custom)-*"; + + public static final String METHOD_API_TEXT = "消息类型为text时,文本内容"; + public static final String METHOD_API_MEDIA_ID = "发送图片、音视频、文件时的链接返回的资源ID"; + public static final String METHOD_API_FORMAT = "文件后缀"; + public static final String METHOD_API_FSIZE = "文件大小"; + public static final String METHOD_API_FNAME = "文件名"; + public static final String METHOD_API_WIDTH = "图片宽度"; + public static final String METHOD_API_HEIGHT = "图片高度"; + + } + + public static class GetResource{ + public static final String METHOD_API_NAME = "获取资源的访问路径"; + public static final String METHOD_API_NOTE = "根据极光提供的mediaId访问资源"; + + public static final String METHOD_API_MEDIA_ID = "发送图片、音视频、文件时的链接返回的资源ID"; + } + + + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgError.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgError.java new file mode 100644 index 0000000..758c544 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgError.java @@ -0,0 +1,14 @@ +package com.blockchain.server.imjg.dto; + +import com.blockchain.common.base.dto.BaseDTO; +import lombok.Data; + +@Data +public class JgError extends BaseDTO { + + + private int code; + + private String message; + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgInitDTO.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgInitDTO.java new file mode 100644 index 0000000..93337e5 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgInitDTO.java @@ -0,0 +1,25 @@ +package com.blockchain.server.imjg.dto; + +import com.blockchain.common.base.dto.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +@ApiModel("极光初始化参数值") +public class JgInitDTO extends BaseDTO { + + @ApiModelProperty("毫秒级时间戳") + private long timestamp; + + @ApiModelProperty("签名") + private String signature; + + @ApiModelProperty("随机字符串") + private String random_str; + + @ApiModelProperty("极光账户的AppKey") + private String appkey; +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgMessageBodyDTO.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgMessageBodyDTO.java new file mode 100644 index 0000000..570e3e9 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgMessageBodyDTO.java @@ -0,0 +1,64 @@ +package com.blockchain.server.imjg.dto; + +import com.blockchain.common.base.dto.BaseDTO; +import com.blockchain.server.imjg.entity.ImjgMessage; +import lombok.Data; + +@Data +public class JgMessageBodyDTO extends BaseDTO { + + private int id; + + private String extras; + + private String msg_type; + + private String media_id; + + private String media_crc32; + + private int duration; + + private String format; + + private int fsize; + + private int width; + + private int height; + + private String fname; + + private String text; + + + public JgMessageBodyDTO(){} + + public JgMessageBodyDTO(String text){ + this.text = text; + } + + public ImjgMessage toImjgMessage(){ + ImjgMessage imjgMessage = new ImjgMessage(); + imjgMessage.setMsgType(this.msg_type); + imjgMessage.setDuration(this.duration); + imjgMessage.setExtras(this.extras); + imjgMessage.setFname(this.fname); + imjgMessage.setText(this.text); + + imjgMessage.setFormat(this.format); + imjgMessage.setFsize(this.fsize); + imjgMessage.setWidth(this.width); + imjgMessage.setHeight(this.height); + + imjgMessage.setMediaId(this.media_id); + imjgMessage.setMediaCrc32(this.media_crc32); + return imjgMessage; + } + + //private int id; + + + + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgMessageDTO.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgMessageDTO.java new file mode 100644 index 0000000..5b24f06 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgMessageDTO.java @@ -0,0 +1,68 @@ +package com.blockchain.server.imjg.dto; + +import com.blockchain.common.base.dto.BaseDTO; +import lombok.Data; + +@Data +public class JgMessageDTO extends BaseDTO { + + //版本号 目前是1 (必填) + private int version; + + // 发送目标类型 single - 个人,group - 群组 chatroom - 聊天室(必填) + private String target_type; + + //目标id single填username group 填Group id chatroom 填chatroomid(必填) + private String target_id; + + //发送消息者身份 当前只限admin用户,必须先注册admin用户 (必填) + private String from_type; + + + //发送者的username (必填) + private String from_id; + + //发消息类型 text - 文本,image - 图片, custom - 自定义消息(msg_body为json对象即可,服务端不做校验)voice - 语音 (必填) + private String msg_type; + + + private JgMessageBodyDTO msg_body; + + private String target_name; + + private String from_name; + + private String from_platform; + + private String from_appkey; + + private String target_appkey; + + private long create_time; + + private long msgid; + + private int msg_level; + + private long msg_ctime; + + private long sui_mtime; + + private int set_from_name; + + + //simple + public JgMessageDTO(String msg_type,String from_id,String target_id,JgMessageBodyDTO msg_body){ + + this.version = 1; + this.target_type = "single"; + this.target_id = target_id; + this.from_type = "admin"; + this.from_id = from_id; + this.msg_type = msg_type; + this.msg_body = msg_body; + + } + + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgMessageLogDTO.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgMessageLogDTO.java new file mode 100644 index 0000000..9a5f3a0 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgMessageLogDTO.java @@ -0,0 +1,19 @@ +package com.blockchain.server.imjg.dto; + +import com.blockchain.common.base.dto.BaseDTO; +import lombok.Data; + +import java.util.List; + +@Data +public class JgMessageLogDTO extends BaseDTO { + + + private int total; + + private int count; + + private String cursor; + + private List messages; +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgResourceDTO.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgResourceDTO.java new file mode 100644 index 0000000..c166b71 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgResourceDTO.java @@ -0,0 +1,16 @@ +package com.blockchain.server.imjg.dto; + +import com.blockchain.common.base.dto.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("资源路径") +public class JgResourceDTO extends BaseDTO { + + @ApiModelProperty("资源路径") + private String url; + + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgResponse.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgResponse.java new file mode 100644 index 0000000..b91f055 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgResponse.java @@ -0,0 +1,10 @@ +package com.blockchain.server.imjg.dto; + +import lombok.Data; + +@Data +public class JgResponse { + + private JgError error; + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgUserDTO.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgUserDTO.java new file mode 100644 index 0000000..767f0d7 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgUserDTO.java @@ -0,0 +1,37 @@ +package com.blockchain.server.imjg.dto; + +import com.blockchain.common.base.dto.BaseDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("极光用户") +public class JgUserDTO extends BaseDTO { + + @ApiModelProperty("用户名登录的惟一的") + private String username; + + @ApiModelProperty("密码") + private String password; + + @ApiModelProperty("应用APPkey") + private String appkey; + + @ApiModelProperty("签名") + private String signature; + + @ApiModelProperty("本系统的用户ID") + private String userId; + + @ApiModelProperty("本系统的用户昵称") + private String nickName; + + public JgUserDTO(){ + + } + public JgUserDTO(String username,String password){ + this.username = username; + this.password = password; + } +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgUserInfoDTO.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgUserInfoDTO.java new file mode 100644 index 0000000..4e352f0 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgUserInfoDTO.java @@ -0,0 +1,33 @@ +package com.blockchain.server.imjg.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("用户实体") +public class JgUserInfoDTO { + + @ApiModelProperty("用户ID") + private String id;//用户id + + @ApiModelProperty("手机号") + private String mobilePhone;//手机号 + + @ApiModelProperty("昵称") + private String nickName;//昵称 + + @ApiModelProperty("头像") + private String avatar;//头像 + + public JgUserInfoDTO(UserDTO userDTO){ + + if(userDTO != null){ + this.id = userDTO.getId(); + this.mobilePhone = userDTO.getMobilePhone(); + this.nickName = userDTO.getNickName(); + this.avatar = userDTO.getAvatar(); + } + } + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgUserStatDTO.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgUserStatDTO.java new file mode 100644 index 0000000..56b406c --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/JgUserStatDTO.java @@ -0,0 +1,14 @@ +package com.blockchain.server.imjg.dto; + +import com.blockchain.common.base.dto.BaseDTO; +import lombok.Data; + +@Data +public class JgUserStatDTO extends BaseDTO { + + private Boolean login; + + private Boolean online; + + private String platform; +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/MessageDTO.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/MessageDTO.java new file mode 100644 index 0000000..0574090 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/MessageDTO.java @@ -0,0 +1,62 @@ +package com.blockchain.server.imjg.dto; + +import com.blockchain.common.base.dto.BaseDTO; +import com.blockchain.server.imjg.entity.ImjgMessage; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("消息实体") +public class MessageDTO extends BaseDTO { + + @ApiModelProperty("消息ID") + private int id; + + @ApiModelProperty("消息额外字段JSON串") + private String extras; + + @ApiModelProperty("消息类型") + private String msgType; + + @ApiModelProperty("聊天类型为text时,消息内容") + private String text; + + @ApiModelProperty("聊天类型为image、文件时,发送消息后极光返回的media_id,下面的字段可以不传") + private String mediaId; + + private String mediaCrc32; + + private int duration; + + private String format; + + private int fsize; + + private int width; + + private int height; + + private String fname; + + public MessageDTO(){} + + public MessageDTO(ImjgMessage imjgMessage){ + if(imjgMessage != null){ + this.fname = imjgMessage.getFname(); + this.height = imjgMessage.getHeight(); + this.width = imjgMessage.getWidth(); + this.fsize = imjgMessage.getFsize(); + this.format = imjgMessage.getFormat(); + this.duration = imjgMessage.getDuration(); + this.mediaCrc32 = imjgMessage.getMediaCrc32(); + this.mediaId = imjgMessage.getMediaId(); + this.text = imjgMessage.getText(); + this.msgType = imjgMessage.getMsgType(); + this.extras = imjgMessage.getExtras(); + this.id = imjgMessage.getId(); + } + } + + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/MessageLogDTO.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/MessageLogDTO.java new file mode 100644 index 0000000..7124532 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/MessageLogDTO.java @@ -0,0 +1,69 @@ +package com.blockchain.server.imjg.dto; + +import com.blockchain.common.base.dto.BaseDTO; +import com.blockchain.server.imjg.entity.ImjgMessageLog; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +@ApiModel("消息记录") +public class MessageLogDTO extends BaseDTO { + + + @ApiModelProperty("消息记录Id") + private int id; + + @ApiModelProperty("模块名") + private String pType; + + @ApiModelProperty("业务ID") + private String dataId; + + @ApiModelProperty("消息类型(text,voice,image,file,video,location,custom)") + private String msgType; + + @ApiModelProperty("发送目标的用户ID") + private String targetId; + + @ApiModelProperty("发送方的用户ID") + private String fromId; + + @ApiModelProperty("消息ID") + private int msgId; + + @ApiModelProperty("节点提示词(0:不是节点提示词,1:两边均可见的提示词,2:接收方可见的节点提示词)") + private int nodeCue; + + @ApiModelProperty("消息状态(NORMAL:正常,CANCEL:已删除)") + private String status; + + @ApiModelProperty("消息发送时间") + private String gmtCreate; + + private String gmtModified; + + public MessageLogDTO(ImjgMessageLog imjgMessageLog){ + if(imjgMessageLog != null){ + this.id = imjgMessageLog.getId(); + this.pType = imjgMessageLog.getPType(); + this.dataId = imjgMessageLog.getDataId(); + this.msgType = imjgMessageLog.getMsgType(); + this.targetId = imjgMessageLog.getTargetId(); + this.fromId = imjgMessageLog.getFromId(); + this.msgId = imjgMessageLog.getMsgId(); + this.nodeCue = imjgMessageLog.getNodeCue(); + this.status = imjgMessageLog.getStatus(); + this.gmtCreate = imjgMessageLog.getGmtCreate(); + } + } + + @ApiModelProperty("消息体实体") + private MessageDTO messageDTO; + + @ApiModelProperty("消息发送方实体") + private JgUserInfoDTO fromUser; + + + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/UserDTO.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/UserDTO.java new file mode 100644 index 0000000..5677aaa --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/dto/UserDTO.java @@ -0,0 +1,47 @@ +package com.blockchain.server.imjg.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +@Data +@ApiModel("用户实体") +public class UserDTO { + @ApiModelProperty("用户ID") + private String id;//用户id + + @ApiModelProperty("手机号") + private String mobilePhone;//手机号 + + @ApiModelProperty("昵称") + private String nickName;//昵称 + + @ApiModelProperty("头像") + private String avatar;//头像 + + @ApiModelProperty("email") + private String email;//email + + @ApiModelProperty("低级认证") + private String lowAuth;//低级认证 + + @ApiModelProperty("高级认证") + private String highAuth;//高级认证 + + @ApiModelProperty("用户等级") + private String grade;//用户等级 + + @ApiModelProperty("是否设置了登录密码") + private boolean hasPassword;//是否设置了登录密码 + + @ApiModelProperty("token信息") + private String token;//token信息 + + @ApiModelProperty("邀请码") + private String invitationCode;//邀请码 + + @ApiModelProperty("谷歌验证器") + private boolean bindGoogleAuth;//谷歌验证器 +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/entity/ImjgMessage.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/entity/ImjgMessage.java new file mode 100644 index 0000000..f8a2f19 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/entity/ImjgMessage.java @@ -0,0 +1,54 @@ +package com.blockchain.server.imjg.entity; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +@Table(name = "imjg_message") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ImjgMessage { + + @Id + @Column(name = "id") + private int id; + @Column(name = "extras") + private String extras; + @Column(name = "msg_type") + private String msgType; + @Column(name = "text") + private String text; + + @Column(name = "media_id") + private String mediaId; + @Column(name = "media_crc32") + private String mediaCrc32; + @Column(name = "duration") + private int duration; + @Column(name = "format") + private String format; + + @Column(name = "fsize") + private int fsize; + @Column(name = "width") + private int width; + @Column(name = "height") + private int height; + @Column(name = "fname") + private String fname; + +/* @Column(name = "latitude") + private int latitude; + @Column(name = "longitude") + private int longitude; + @Column(name = "scale") + private int scale; + @Column(name = "lable") + private String lable;*/ +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/entity/ImjgMessageLog.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/entity/ImjgMessageLog.java new file mode 100644 index 0000000..31fff36 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/entity/ImjgMessageLog.java @@ -0,0 +1,54 @@ +package com.blockchain.server.imjg.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +@Table(name = "imjg_message_log") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ImjgMessageLog { + + @Id + @Column(name = "id") + private int id; + + @Column(name = "p_type") + private String pType; + + @Column(name = "data_id") + private String dataId; + + @Column(name = "msg_type") + private String msgType; + @Column(name = "target_id") + private String targetId; + + + @Column(name = "from_id") + private String fromId; + + + @Column(name = "msg_id") + private int msgId; + + @Column(name = "node_cue") + private int nodeCue; + + @Column(name = "status") + private String status; + + @Column(name = "gmt_create") + private String gmtCreate; + + @Column(name = "gmt_modified") + private String gmtModified; + + + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/entity/ImjgUser.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/entity/ImjgUser.java new file mode 100644 index 0000000..58e62a2 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/entity/ImjgUser.java @@ -0,0 +1,35 @@ +package com.blockchain.server.imjg.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; + +import javax.persistence.Column; +import javax.persistence.Table; + +@Table(name = "imjg_user") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ImjgUser { + + @Id + @Column(name = "id") + private int id; + + @Column(name = "user_id") + private String userId; + + @Column(name = "jg_username") + private String jgUsername; + + @Column(name = "jg_password") + private String jgPassword; + + @Column(name = "gmt_create") + private String gmtCreate; + + @Column(name = "gmt_modified") + private String gmtModified; +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/feign/UserFeign.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/feign/UserFeign.java new file mode 100644 index 0000000..b150b3c --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/feign/UserFeign.java @@ -0,0 +1,20 @@ +package com.blockchain.server.imjg.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.imjg.dto.UserDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient("dapp-user-server") +public interface UserFeign { + String CONTENT_PATH = "/inner"; + + /*** + * 根据id查询用户信息 + * @param userId + * @return + */ + @GetMapping(CONTENT_PATH + "/selectUserInfoById") + ResultDTO getUserById(@RequestParam("userId") String userId); +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/inner/ImjgInnerController.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/inner/ImjgInnerController.java new file mode 100644 index 0000000..57ed74b --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/inner/ImjgInnerController.java @@ -0,0 +1,192 @@ +package com.blockchain.server.imjg.inner; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.JsonUtils; +import com.blockchain.server.imjg.common.util.HttpUtils; +import com.blockchain.server.imjg.common.util.JiGuangIMUtils; +import com.blockchain.server.imjg.controller.api.ImjgApi; +import com.blockchain.server.imjg.dto.*; +import com.blockchain.server.imjg.entity.ImjgUser; +import com.blockchain.server.imjg.inner.api.ImjgInnerApi; +import com.blockchain.server.imjg.service.ImjgMessageLogService; +import com.blockchain.server.imjg.service.ImjgUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Api(ImjgInnerApi.IMJG_INNER_API) +@RestController +@RequestMapping("/inner") +public class ImjgInnerController { + + + @Value("${JiGuang.appkey}") + private String appkey; + + @Value("${JiGuang.secret}") + private String secret; + + @Value("${JiGuang.api.address}") + private String address; + + @Value("${JiGuang.random_str}") + private String random_str; + + @Value("${JiGuang.api.address_v2}") + private String address_v2; + + @Value("${JiGuang.p_type}") + private String p_type; + + @Autowired + private ImjgMessageLogService imjgMessageLogService; + + @Autowired + private ImjgUserService imjgUserService; + + @ApiOperation(value = ImjgInnerApi.SendSingleMsg.METHOD_API_NAME, notes = ImjgInnerApi.SendSingleMsg.METHOD_API_NOTE) + @PostMapping("/sendSingleMsg") + public ResultDTO sendSingleMsg(@ApiParam(ImjgInnerApi.SendSingleMsg.METHOD_API_PTYPE)@RequestParam(value = "pType") String pType, + @ApiParam(ImjgInnerApi.SendSingleMsg.METHOD_API_DATAID)@RequestParam(value = "dataId") String dataId, + @ApiParam(ImjgInnerApi.SendSingleMsg.METHOD_API_MSGTYPE)@RequestParam(value = "msgType") String msgType, + @ApiParam(ImjgInnerApi.SendSingleMsg.METHOD_API_FUSERID)@RequestParam(value = "fuserId") String fuserId, + @ApiParam(ImjgInnerApi.SendSingleMsg.METHOD_API_TUSERID)@RequestParam(value = "tuserId") String tuserId, + @ApiParam(ImjgInnerApi.SendSingleMsg.METHOD_API_MSGBODYJSON)@RequestParam(value = "msgBodyJson") String msgBodyJson, + @ApiParam(ImjgInnerApi.SendSingleMsg.METHOD_API_NODECUE)@RequestParam(value = "nodeCue") int nodeCue){ + + try{ + JgMessageBodyDTO jgMessageBodyDTO = JsonUtils.jsonToPojo(msgBodyJson,JgMessageBodyDTO.class); + String fId = imjgMessageLogService.getJGAccount(fuserId).getUsername(); + String tId = imjgMessageLogService.getJGAccount(tuserId).getUsername(); + JgMessageDTO jgMessageDTO = new JgMessageDTO(msgType,fId,tId,jgMessageBodyDTO); + jgMessageDTO.setFrom_name("nodeCue"); + String json = JsonUtils.objectToJson(jgMessageDTO); + String result = HttpUtils.doJiGuangPost(address+"/v1/messages",json, JiGuangIMUtils.getAuthString(appkey,secret)); + imjgMessageLogService.save(pType,dataId,fId,tId,msgType,JsonUtils.objectToJson(jgMessageBodyDTO.toImjgMessage()),nodeCue); + return ResultDTO.requstSuccess(result); + }catch (Exception e){ + return ResultDTO.requstSuccess(); + } + + } + + + @ApiOperation(value = ImjgInnerApi.GetUserStatFromJG.METHOD_API_NAME, notes = ImjgInnerApi.GetUserStatFromJG.METHOD_API_NOTE) + @GetMapping("/getUserStatFromJG") + public ResultDTO getUserStatFromJG(@ApiParam(ImjgInnerApi.GetUserStatFromJG.METHOD_API_USERID)@RequestParam(value = "userId") String userId){ + JgUserDTO jgUserDTO = imjgMessageLogService.getJGAccount(userId); + String userName = jgUserDTO.getUsername(); + Map params = new HashMap(); + String result = HttpUtils.doJGGet(address+"/v1/users/"+userName+"/userstat",params, JiGuangIMUtils.getAuthString(appkey,secret)); + JgUserStatDTO jgUserStatDTO = JsonUtils.jsonToPojo(result, JgUserStatDTO.class); + return ResultDTO.requstSuccess(jgUserStatDTO); + } + + @ApiOperation(value = ImjgInnerApi.GetJGAccount.METHOD_API_NAME, notes = ImjgInnerApi.GetJGAccount.METHOD_API_NOTE) + @GetMapping("/getJGAccount") + public ResultDTO getJGAccount(HttpServletRequest request, + @ApiParam(ImjgInnerApi.GetJGAccount.METHOD_API_USERID)@RequestParam(value = "userId",required = false,defaultValue = "") String userId) { + + if("".equals(userId)){ + return ResultDTO.requstSuccess(imjgMessageLogService.getJGAccount(userId)); + }else{ + return ResultDTO.requstSuccess(imjgMessageLogService.getJGAccount(userId)); + } + + } + + @ApiOperation(value = ImjgInnerApi.GetUserMessages.METHOD_API_NAME, notes = ImjgInnerApi.GetUserMessages.METHOD_API_NOTE) + @GetMapping("/getUserMessages") + public ResultDTO> getUserMessages(@ApiParam(ImjgInnerApi.GetUserMessages.METHOD_API_DATAID)@RequestParam(value = "dataId") String dataId, + @ApiParam(ImjgInnerApi.GetUserMessages.METHOD_API_PTYPE)@RequestParam(value = "pType") String pType, + + @ApiParam(ImjgInnerApi.GetUserMessages.METHOD_API_USERID)@RequestParam(value = "userId") String userId, + @ApiParam(ImjgInnerApi.GetUserMessages.METHOD_API_ORDER)@RequestParam(value = "order",defaultValue = "gmt_create asc ",required = false) String order, + @ApiParam(ImjgInnerApi.GetUserMessages.METHOD_API_PAGE)@RequestParam(value = "page",defaultValue = "1",required = false) int page, + @ApiParam(ImjgInnerApi.GetUserMessages.METHOD_API_SIZE)@RequestParam(value = "size",defaultValue = "20",required = false) int size, + HttpServletRequest request) { + List logList = imjgMessageLogService.list(pType,dataId,userId,order,page,size); + return ResultDTO.requstSuccess(logList); + } + + @ApiOperation(value = ImjgInnerApi.GetUserMessagesFromJG.METHOD_API_NAME, notes = ImjgInnerApi.GetUserMessagesFromJG.METHOD_API_NOTE) + @GetMapping("/getUserMessagesFromJG") + public ResultDTO getUserMessagesFromJG(@ApiParam(ImjgInnerApi.GetUserMessagesFromJG.METHOD_API_USERID)@RequestParam(value = "userId") String userId, + @ApiParam(ImjgInnerApi.GetUserMessagesFromJG.METHOD_API_BEGIN_TIME)@RequestParam(value = "begin_time",required = false,defaultValue = "") String begin_time, + @ApiParam(ImjgInnerApi.GetUserMessagesFromJG.METHOD_API_END_TIME)@RequestParam(value = "end_time",required = false,defaultValue = "") String end_time, + @ApiParam(ImjgInnerApi.GetUserMessagesFromJG.METHOD_API_CURSOR)@RequestParam(value = "cursor",required = false,defaultValue = "") String cursor)throws Exception{ + JgUserDTO jgUserDTO = imjgMessageLogService.getJGAccount(userId); + String userName = jgUserDTO.getUsername(); + Map params = new HashMap(); + String result = ""; + end_time = URLEncoder.encode(end_time,"utf-8"); + begin_time = URLEncoder.encode(begin_time,"utf-8"); + if("".equals(cursor)){ + result = HttpUtils.doJGGet(address_v2+"/users/"+userName+"/messages?count=1000&begin_time="+begin_time+"&end_time="+end_time,params,JiGuangIMUtils.getAuthString(appkey,secret)); + }else{ + result = HttpUtils.doJGGet(address_v2+"/users/"+userName+"/messages?cursor="+cursor,params,JiGuangIMUtils.getAuthString(appkey,secret)); + } + JgMessageLogDTO jgMessageLogDTO = JsonUtils.jsonToPojo(result,JgMessageLogDTO.class); + if(jgMessageLogDTO == null){ + return ResultDTO.requstSuccess(JsonUtils.jsonToPojo(result,JgResponse.class)); + } + return ResultDTO.requstSuccess(jgMessageLogDTO); + } + + @ApiOperation(value = ImjgInnerApi.GetGroupMessagesFromJG.METHOD_API_NAME, notes = ImjgInnerApi.GetGroupMessagesFromJG.METHOD_API_NOTE) + @GetMapping("/getGroupMessagesFromJG") + public ResultDTO getGroupMessagesFromJG(@ApiParam(ImjgInnerApi.GetGroupMessagesFromJG.METHOD_API_GID)@RequestParam(value = "gId") long gId, + @ApiParam(ImjgInnerApi.GetGroupMessagesFromJG.METHOD_API_BEGIN_TIME)@RequestParam(value = "begin_time",required = false,defaultValue = "") String begin_time, + @ApiParam(ImjgInnerApi.GetGroupMessagesFromJG.METHOD_API_END_TIME)@RequestParam(value = "end_time",required = false,defaultValue = "") String end_time, + @ApiParam(ImjgInnerApi.GetGroupMessagesFromJG.METHOD_API_CURSOR)@RequestParam(value = "cursor",required = false,defaultValue = "") String cursor)throws Exception{ + + Map params = new HashMap(); + String result = ""; + end_time = URLEncoder.encode(end_time,"utf-8"); + begin_time = URLEncoder.encode(begin_time,"utf-8"); + if("".equals(cursor)){ + result = HttpUtils.doJGGet(address_v2+"/groups/"+gId+"/messages?count=1000&begin_time="+begin_time+"&end_time="+end_time,params,JiGuangIMUtils.getAuthString(appkey,secret)); + }else{ + result = HttpUtils.doJGGet(address_v2+"/groups/"+gId+"/messages?cursor="+cursor,params,JiGuangIMUtils.getAuthString(appkey,secret)); + } + JgMessageLogDTO jgMessageLogDTO = JsonUtils.jsonToPojo(result,JgMessageLogDTO.class); + if(jgMessageLogDTO == null){ + return ResultDTO.requstSuccess(JsonUtils.jsonToPojo(result,JgResponse.class)); + } + return ResultDTO.requstSuccess(jgMessageLogDTO); + } + + + @ApiOperation(value = ImjgInnerApi.GetRoomMessagesFromJG.METHOD_API_NAME, notes = ImjgInnerApi.GetRoomMessagesFromJG.METHOD_API_NOTE) + @GetMapping("/getRoomMessagesFromJG") + public ResultDTO getRoomMessagesFromJG(@ApiParam(ImjgInnerApi.GetRoomMessagesFromJG.METHOD_API_CHATROOMID)@RequestParam(value = "chatroomid") long chatroomid, + @ApiParam(ImjgInnerApi.GetRoomMessagesFromJG.METHOD_API_BEGIN_TIME)@RequestParam(value = "begin_time",required = false,defaultValue = "") String begin_time, + @ApiParam(ImjgInnerApi.GetRoomMessagesFromJG.METHOD_API_END_TIME)@RequestParam(value = "end_time",required = false,defaultValue = "") String end_time, + @ApiParam(ImjgInnerApi.GetRoomMessagesFromJG.METHOD_API_CURSOR)@RequestParam(value = "cursor",required = false,defaultValue = "") String cursor)throws Exception{ + String result = ""; + end_time = URLEncoder.encode(end_time,"utf-8"); + begin_time = URLEncoder.encode(begin_time,"utf-8"); + if("".equals(cursor)){ + result = HttpUtils.doJGGet(address_v2+"/chatrooms/"+chatroomid+"/messages?count=1000&begin_time="+begin_time+"&end_time="+end_time,null,JiGuangIMUtils.getAuthString(appkey,secret)); + }else{ + result = HttpUtils.doJGGet(address_v2+"/chatrooms/"+chatroomid+"/messages?cursor="+cursor,null,JiGuangIMUtils.getAuthString(appkey,secret)); + } + JgMessageLogDTO jgMessageLogDTO = JsonUtils.jsonToPojo(result,JgMessageLogDTO.class); + if(jgMessageLogDTO == null){ + return ResultDTO.requstSuccess(JsonUtils.jsonToPojo(result,JgResponse.class)); + } + return ResultDTO.requstSuccess(jgMessageLogDTO); + } + + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/inner/api/ImjgInnerApi.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/inner/api/ImjgInnerApi.java new file mode 100644 index 0000000..4d7b763 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/inner/api/ImjgInnerApi.java @@ -0,0 +1,78 @@ +package com.blockchain.server.imjg.inner.api; + +public class ImjgInnerApi { + + public static final String IMJG_INNER_API = "极光IM服务接口"; + + public static class SendSingleMsg{ + public static final String METHOD_API_PTYPE = "服务模块名"; + public static final String METHOD_API_NAME = "发送单聊消息接口"; + public static final String METHOD_API_NOTE = "发送单聊信息并保存消息记录"; + public static final String METHOD_API_DATAID = "业务ID"; + public static final String METHOD_API_MSGTYPE = "消息类型"; + public static final String METHOD_API_FUSERID = "发送方用户ID"; + public static final String METHOD_API_TUSERID = "接收方用户ID"; + public static final String METHOD_API_MSGBODYJSON = "消息DTO类的JSON串"; + /*0:默认,不是节点消息;1:双方可见的节点消息;2:接收方可见的节点消息*/ + public static final String METHOD_API_NODECUE = "节点消息标志状态"; + + } + + public static class GetUserStatFromJG{ + public static final String METHOD_API_NAME = "获取用户状态接口"; + public static final String METHOD_API_NOTE = "根据系统UserId,获取用户在极光的登录状态"; + public static final String METHOD_API_USERID = "系统用户ID"; + + + } + + public static class GetJGAccount{ + public static final String METHOD_API_NAME = "获取用户极光的用户名"; + public static final String METHOD_API_NOTE = "获取当前用户的极光用户名"; + public static final String METHOD_API_USERID = "用户ID"; + } + + public static class GetUserMessages{ + + public static final String METHOD_API_NAME = "获取保存在系统的用户的消息记录接口"; + public static final String METHOD_API_NOTE = "根据业务ID(订单ID)获取当前用户消息记录,一次20条"; + + public static final String METHOD_API_PTYPE = "服务模块名"; + public static final String METHOD_API_DATAID = "业务ID"; + public static final String METHOD_API_USERID = "当前用户ID"; + + public static final String METHOD_API_ORDER = "排序方式"; + public static final String METHOD_API_PAGE = "页码"; + public static final String METHOD_API_SIZE = "每页大小"; + } + + public static class GetUserMessagesFromJG{ + public static final String METHOD_API_NAME = "获取保存在极光的用户消息记录(15天内)"; + public static final String METHOD_API_NOTE = "获取保存在极光的用户消息记录(15天内)"; + public static final String METHOD_API_USERID = "系统用户ID"; + public static final String METHOD_API_BEGIN_TIME = "消息开始时间"; + public static final String METHOD_API_END_TIME = "消息截止时间(7天内)"; + public static final String METHOD_API_CURSOR = "获取下一次查询"; + } + + public static class GetGroupMessagesFromJG{ + public static final String METHOD_API_NAME = "获取保存在极光的群消息记录(15天内)"; + public static final String METHOD_API_NOTE = "获取保存在极光的群消息记录(15天内)"; + public static final String METHOD_API_GID = "群ID"; + public static final String METHOD_API_BEGIN_TIME = "消息开始时间"; + public static final String METHOD_API_END_TIME = "消息截止时间(7天内)"; + public static final String METHOD_API_CURSOR = "获取下一次查询"; + } + + public static class GetRoomMessagesFromJG{ + public static final String METHOD_API_NAME = "获取保存在极光的聊天室消息记录(15天内)"; + public static final String METHOD_API_NOTE = "获取保存在极光的聊天室消息记录(15天内)"; + public static final String METHOD_API_CHATROOMID = "聊天室ID"; + public static final String METHOD_API_BEGIN_TIME = "消息开始时间"; + public static final String METHOD_API_END_TIME = "消息截止时间(7天内)"; + public static final String METHOD_API_CURSOR = "获取下一次查询"; + } + +} + + diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/mapper/ImjgMessageLogMapper.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/mapper/ImjgMessageLogMapper.java new file mode 100644 index 0000000..b05a8f8 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/mapper/ImjgMessageLogMapper.java @@ -0,0 +1,18 @@ +package com.blockchain.server.imjg.mapper; + +import com.blockchain.server.imjg.entity.ImjgMessageLog; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; +import java.util.Map; + +@Repository +public interface ImjgMessageLogMapper extends Mapper { + + List listBy(Map params); + + int insertOne(ImjgMessageLog imjgMessageLog); + + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/mapper/ImjgMessageMapper.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/mapper/ImjgMessageMapper.java new file mode 100644 index 0000000..f40820f --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/mapper/ImjgMessageMapper.java @@ -0,0 +1,11 @@ +package com.blockchain.server.imjg.mapper; + +import com.blockchain.server.imjg.entity.ImjgMessage; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +@Repository +public interface ImjgMessageMapper extends Mapper { + + void insertOne(ImjgMessage imjgMessage); +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/mapper/ImjgUserMapper.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/mapper/ImjgUserMapper.java new file mode 100644 index 0000000..c640516 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/mapper/ImjgUserMapper.java @@ -0,0 +1,13 @@ +package com.blockchain.server.imjg.mapper; + +import com.blockchain.server.imjg.entity.ImjgUser; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +@Repository +public interface ImjgUserMapper extends Mapper { + + ImjgUser selectByUserId(String userId); + + void insertOne(ImjgUser imjgUser); +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/ImjgMessageLogService.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/ImjgMessageLogService.java new file mode 100644 index 0000000..eeb6288 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/ImjgMessageLogService.java @@ -0,0 +1,54 @@ +package com.blockchain.server.imjg.service; + + +import com.blockchain.server.imjg.dto.JgUserDTO; +import com.blockchain.server.imjg.dto.MessageLogDTO; +import com.blockchain.server.imjg.entity.ImjgMessageLog; + +import java.util.List; +/** + * @author: rui + * @date: 2019/5/5 0005 09:52 + * @description: Im消息服务 + */ +public interface ImjgMessageLogService { + + /** + * 保存一条消息记录 + * @param imjgMessageLog + */ + void save(ImjgMessageLog imjgMessageLog); + + /** + * 保存一条消息体,和一条消息记录 + * @param pType 服务模块名称 + * @param dataId 业务ID + * @param fromUserId 发送方用户ID + * @param targetUserId 接收方用户ID + * @param msgType 消息类型 + * @param msgBody 消息体内容 + * @param nodeCue 节点消息标志 + */ + void save(String pType,String dataId,String fromUserId,String targetUserId,String msgType,String msgBody,int nodeCue); + + /** + * 按照条件获取消息记录 + * @param pType + * @param dataId + * @param userId + * @param order + * @param page + * @param size + * @return + */ + List list(String pType, String dataId, String userId, String order, int page, int size); + + /** + * 根据系统用户ID获取极光账号、密码 + * @param userId + * @return + */ + JgUserDTO getJGAccount(String userId); + + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/ImjgUserService.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/ImjgUserService.java new file mode 100644 index 0000000..b477e0b --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/ImjgUserService.java @@ -0,0 +1,30 @@ +package com.blockchain.server.imjg.service; + +import com.blockchain.server.imjg.entity.ImjgUser; + +public interface ImjgUserService { + + /** + * 根据系统userId获取极光账号 + * @param userId + * @return + */ + ImjgUser get(String userId); + + /** + * 根据Id获取极光账号 + * @param id + * @return + */ + ImjgUser get(int id); + + /** + * 保存一条系统账号和极光关联的信息 + * @param imjgUser + * @return + */ + int save(ImjgUser imjgUser); + + + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/impl/ImjgMessageLogServiceImpl.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/impl/ImjgMessageLogServiceImpl.java new file mode 100644 index 0000000..a2789ce --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/impl/ImjgMessageLogServiceImpl.java @@ -0,0 +1,157 @@ +package com.blockchain.server.imjg.service.impl; + +import com.blockchain.common.base.util.DateTimeUtils; +import com.blockchain.common.base.util.JsonUtils; +import com.blockchain.server.imjg.common.enums.MessageLogEnums; +import com.blockchain.server.imjg.common.util.JiGuangIMUtils; +import com.blockchain.server.imjg.dto.*; +import com.blockchain.server.imjg.entity.ImjgMessage; +import com.blockchain.server.imjg.entity.ImjgMessageLog; +import com.blockchain.server.imjg.entity.ImjgUser; +import com.blockchain.server.imjg.feign.UserFeign; +import com.blockchain.server.imjg.mapper.ImjgMessageLogMapper; +import com.blockchain.server.imjg.mapper.ImjgMessageMapper; +import com.blockchain.server.imjg.service.ImjgMessageLogService; +import com.blockchain.server.imjg.service.ImjgUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +@Service +public class ImjgMessageLogServiceImpl implements ImjgMessageLogService { + + @Autowired + private ImjgMessageLogMapper imjgMessageLogMapper; + + @Autowired + private ImjgMessageMapper imjgMessageMapper; + + @Autowired + private ImjgUserService imjgUserService; + + @Autowired + private UserFeign userFeign; + + @Value("${JiGuang.appkey}") + private String appkey; + + @Value("${JiGuang.secret}") + private String secret; + + @Value("${JiGuang.api.address}") + private String address; + + @Value("${JiGuang.random_str}") + private String random_str; + + @Value("${JiGuang.api.address_v2}") + private String address_v2; + + @Value("${JiGuang.p_type}") + private String p_type; + + private static int USER_EXIST = 899001; + + + @Override + public void save(ImjgMessageLog imjgMessageLog) { + + imjgMessageLogMapper.insert(imjgMessageLog); + } + + @Override + public List list(String pType, String dataId, String userId, String order, int page, int size) { + Map params = new HashMap(); + params.put("pType",pType); + params.put("dataId",dataId); + params.put("userId",userId); + params.put("status","NORMAL"); + params.put("order",order); + params.put("start",(page-1)*size); + params.put("size",size); + + params.put("chatLog",1); + List logList = imjgMessageLogMapper.listBy(params); + + List messageLogDTOS = new ArrayList<>(); + + for(ImjgMessageLog log:logList){ + MessageLogDTO messageLogDTO = new MessageLogDTO(log); + ImjgMessage imjgMessageDB = imjgMessageMapper.selectByPrimaryKey(log.getMsgId()); + System.out.println(imjgMessageDB.getText()); + messageLogDTO.setMessageDTO(new MessageDTO(imjgMessageDB)); + System.out.println(log.getFromId()); + UserDTO fromUser = userFeign.getUserById(log.getFromId()).getData(); + System.out.println("userDTO>>>"+fromUser); + + messageLogDTO.setFromUser(new JgUserInfoDTO(fromUser)); + + Date date = DateTimeUtils.parse(log.getGmtCreate()); + messageLogDTO.setGmtCreate(DateTimeUtils.format(date,DateTimeUtils.DATE_TIME_FORMAT)); + messageLogDTOS.add(messageLogDTO); + + } + + return messageLogDTOS; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(String pType,String dataId,String fromUserId,String targetUserId,String msgType,String msgBody,int nodeCue) { + + ImjgMessage imjgMessage = JsonUtils.jsonToPojo(msgBody, ImjgMessage.class); + imjgMessageMapper.insertOne(imjgMessage); + + ImjgMessageLog imjgMessageLog = new ImjgMessageLog(); + imjgMessageLog.setPType(pType); + imjgMessageLog.setDataId(dataId); + imjgMessageLog.setFromId(fromUserId); + imjgMessageLog.setTargetId(targetUserId); + imjgMessageLog.setMsgType(msgType); + imjgMessageLog.setNodeCue(nodeCue); + imjgMessageLog.setMsgId(imjgMessage.getId()); + imjgMessageLog.setStatus(MessageLogEnums.STATUS_NORMAL.getCode()); + + imjgMessageLogMapper.insertOne(imjgMessageLog); + } + + @Override + public JgUserDTO getJGAccount(String userId) { + if(userId == null&&"".equals(userId)){ + return null; + } + ImjgUser imjgUser = imjgUserService.get(userId); + /*该用户在系统不存在极光账号*/ + if(imjgUser == null){ + String response = JiGuangIMUtils.doRegister(address+"/v1/admins/",JsonUtils.objectToJson(new JgUserDTO(userId,userId)),appkey,secret); + if("".equals(response)){ + imjgUserService.save(new ImjgUser(0,userId,userId,userId,null,null)); + JgUserDTO jgUserDTO = new JgUserDTO(userId,userId); + jgUserDTO.setUserId(userId); + return jgUserDTO; + }else{ + JgResponse jgResponse = JsonUtils.jsonToPojo(response,JgResponse.class); + //user exist + if(jgResponse.getError().getCode() == USER_EXIST){ + String rs = UUID.randomUUID().toString(); + JiGuangIMUtils.doRegister(address+"/v1/admins/",JsonUtils.objectToJson(new JgUserDTO(rs,userId)),appkey,secret); + imjgUserService.save(new ImjgUser(0,userId,rs,userId,null,null)); + JgUserDTO jgUserDTO = new JgUserDTO(rs,userId); + jgUserDTO.setUserId(userId); + return jgUserDTO; + } + return null; + } + + }else{/*存在账号*/ + JgUserDTO jgUserDTO = new JgUserDTO(imjgUser.getJgUsername(), imjgUser.getJgPassword()); + jgUserDTO.setUserId(userId); + return jgUserDTO; + } + } + + +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/impl/ImjgUserServiceImpl.java b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/impl/ImjgUserServiceImpl.java new file mode 100644 index 0000000..44413e6 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/java/com/blockchain/server/imjg/service/impl/ImjgUserServiceImpl.java @@ -0,0 +1,33 @@ +package com.blockchain.server.imjg.service.impl; + +import com.blockchain.server.imjg.entity.ImjgUser; +import com.blockchain.server.imjg.mapper.ImjgUserMapper; +import com.blockchain.server.imjg.service.ImjgUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class ImjgUserServiceImpl implements ImjgUserService { + + + @Autowired + private ImjgUserMapper imjgUserMapper; + + @Override + public ImjgUser get(String userId) { + return imjgUserMapper.selectByUserId(userId); + } + + @Override + public ImjgUser get(int id) { + ImjgUser imjgUser = new ImjgUser(); + imjgUser.setId(id); + return imjgUserMapper.selectOne(imjgUser); + } + + @Override + public int save(ImjgUser imjgUser) { + imjgUserMapper.insertOne(imjgUser); + return imjgUser.getId(); + } +} diff --git a/blockchain-server/blockchain-server-imJg/src/main/resources/application.yml b/blockchain-server/blockchain-server-imJg/src/main/resources/application.yml new file mode 100644 index 0000000..95aad49 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/resources/application.yml @@ -0,0 +1,3 @@ +#日志配置路径 +logging: + config: classpath:logback/logback-${spring.cloud.config.profile}.xml \ No newline at end of file diff --git a/blockchain-server/blockchain-server-imJg/src/main/resources/bootstrap.yml b/blockchain-server/blockchain-server-imJg/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..ca3f99d --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/resources/bootstrap.yml @@ -0,0 +1,22 @@ +server: + port: 8801 +#注册中心 +eureka: + client: + service-url: + defaultZone: http://eureka:8001/eureka/ + instance: + prefer-ip-address: true + instance-id: ${spring.cloud.client.ip-address}:${server.port} + hostname: ${spring.cloud.client.ip-address} +spring: + cloud: + #配置中心 + config: + discovery: + service-id: dapp-config-server + enabled: true + profile: dev + name: springconf,springcloudconf,redisconf,dbconf,txconf,xssconf,eosconf,ipconf,fileconf,authconfig,imjgconf + application: + name: dapp-imjg-server \ No newline at end of file diff --git a/blockchain-server/blockchain-server-imJg/src/main/resources/logback/logback-dev.xml b/blockchain-server/blockchain-server-imJg/src/main/resources/logback/logback-dev.xml new file mode 100644 index 0000000..9a0e324 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/resources/logback/logback-dev.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-imJg/src/main/resources/logback/logback-pro.xml b/blockchain-server/blockchain-server-imJg/src/main/resources/logback/logback-pro.xml new file mode 100644 index 0000000..5b55059 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/resources/logback/logback-pro.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-imJg/src/main/resources/mapper/ImjgMessageLogMapper.xml b/blockchain-server/blockchain-server-imJg/src/main/resources/mapper/ImjgMessageLogMapper.xml new file mode 100644 index 0000000..15a84c5 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/resources/mapper/ImjgMessageLogMapper.xml @@ -0,0 +1,57 @@ + + + + imjg_message_log + + + + + + + and b.id in (${ids}) + + + and (from_id=#{userId}||target_id=#{userId}) + + + + and data_id=#{dataId} + + + and p_type=#{pType} + + + and status='NORMAL' || status='SHOW' + + + + + + + + insert into imjg_message_log (p_type,data_id,msg_type,target_id,from_id,msg_id,node_cue,STATUS,gmt_create,gmt_modified) + values (#{pType},#{dataId},#{msgType},#{targetId},#{fromId},#{msgId},#{nodeCue},#{status},now(),now()) + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-imJg/src/main/resources/mapper/ImjgMessageMapper.xml b/blockchain-server/blockchain-server-imJg/src/main/resources/mapper/ImjgMessageMapper.xml new file mode 100644 index 0000000..499ed81 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/resources/mapper/ImjgMessageMapper.xml @@ -0,0 +1,58 @@ + + + + imjg_message + + + + + + + + + + + + + + + + + + + + insert into imjg_message(extras,msg_type,text,media_id,media_crc32,duration,format,fsize,width,height,fname) + values (#{extras},#{msgType},#{text},#{mediaId},#{mediaCrc32},#{duration},#{format},#{fsize},#{width},#{height},#{fname}) + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-imJg/src/main/resources/mapper/ImjgUserMapper.xml b/blockchain-server/blockchain-server-imJg/src/main/resources/mapper/ImjgUserMapper.xml new file mode 100644 index 0000000..651a6d4 --- /dev/null +++ b/blockchain-server/blockchain-server-imJg/src/main/resources/mapper/ImjgUserMapper.xml @@ -0,0 +1,60 @@ + + + + imjg_user + + + + + + + and b.id in (${ids}) + + + and a.type = #{type} + + + and a.tenant_id = #{tenantId} + + + and b.status = #{status} + + + and a.course_id in (${courseIds}) + + + and b.id =#{id} + + + and b.theme like concat(#{theme},'%') + + + and b.speaker_names like concat(#{speakerName},'%') + + + and b.status != 5 + + + + + + + + + + + + + + + insert into imjg_user(user_id,jg_username,jg_password,gmt_create,gmt_modified) + values (#{userId},#{jgUsername},#{jgPassword},now(),now()) + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/pom.xml b/blockchain-server/blockchain-server-otc/pom.xml new file mode 100644 index 0000000..2af6440 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/pom.xml @@ -0,0 +1,35 @@ + + + + blockchain-server + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + blockchain-server-otc + + + com.blockchain + blockchain-server-base + 1.0-SNAPSHOT + + + com.blockchain + blockchain-common-tx + 1.0-SNAPSHOT + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/OtcApplication.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/OtcApplication.java new file mode 100644 index 0000000..1062893 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/OtcApplication.java @@ -0,0 +1,12 @@ +package com.blockchain.server.otc; + +import com.blockchain.server.base.BaseConf; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication(scanBasePackageClasses = {BaseConf.class, OtcApplication.class}) +public class OtcApplication { + public static void main(String[] args) { + SpringApplication.run(OtcApplication.class); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/AdConstants.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/AdConstants.java new file mode 100644 index 0000000..63aa37a --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/AdConstants.java @@ -0,0 +1,16 @@ +package com.blockchain.server.otc.common.constant; + +/*** + * 广告表常量 + */ +public class AdConstants { + + /*** + * 广告状态 + */ + public static final String DEFAULT = "DEFAULT"; //挂单中 + public static final String UNDERWAY = "UNDERWAY"; //交易中 + public static final String PENDING = "PENDING"; //待处理:当订单需要下架、修改时变成待处理 + public static final String FINISH = "FINISH"; //已完成 + public static final String CANCEL = "CANCEL"; //已撤销 +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/AppealConstants.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/AppealConstants.java new file mode 100644 index 0000000..765ad12 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/AppealConstants.java @@ -0,0 +1,31 @@ +package com.blockchain.server.otc.common.constant; + +/*** + * 申诉表(appeal、appealDetail、appealImg)常量 + */ +public class AppealConstants { + + /*** + * 申诉记录状态 + */ + public static final String NEW = "NEW"; + public static final String HANDLE = "HANDLE"; + + /*** + * 申诉方角色类型 + */ + public static final String AD = "AD"; //广告方申诉 + public static final String ORDER = "ORDER"; //订单方申诉 + + /*** + * 申诉处理备注 + */ + public static final String RECEIPT_REMARK = "卖家确认收款,自动取消申诉"; //确认付款时的申诉处理日志备注 + public static final String RECEIPT_IP_ADDR = "127.0.0.1"; //确认付款时的申诉处理日志的IP地址 + public static final String RECEIPT_SYS_USER_ID = "admin"; //确认付款时的申诉处理日志的管理员Id + + /*** + * 申诉图片 + */ + public static final String APPEAL_URL = "/appeal"; //申诉图片上传路径 +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/BillConstants.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/BillConstants.java new file mode 100644 index 0000000..1ab9e4c --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/BillConstants.java @@ -0,0 +1,19 @@ +package com.blockchain.server.otc.common.constant; + +/*** + * 资金对账表常量 + */ +public class BillConstants { + + /*** + * 资金变动类型 + */ + public static final String PUBLISH = "PUBLISH"; //发布广告 + public static final String DEAL = "DEAL"; //买入或卖出 + public static final String MARK = "MARK"; //成交 + public static final String CANCEL = "CANCEL"; //撤销 + public static final String AUTO_CANCEL = "AUTO_CANCEL"; //自动撤销 + public static final String MARKET_USER = "MARKET_USER";//成为市商 + public static final String CANCEL_MARKET_USER = "CANCEL_MARKET_USER";//取消市商 + +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/CommonConstans.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/CommonConstans.java new file mode 100644 index 0000000..5a71c70 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/CommonConstans.java @@ -0,0 +1,24 @@ +package com.blockchain.server.otc.common.constant; + +import java.math.BigDecimal; + +/*** + * 公共常量 + */ +public class CommonConstans { + + /*** + * 公共状态、类型 + */ + public static final String YES = "Y"; //可用状态 + public static final String NO = "N"; //禁用状态 + + public static final String BUY = "BUY"; //广告、订单:买入类型 + public static final String SELL = "SELL"; //广告、订单:卖出类型 + + public static final String DESC = "DESC"; //排序-倒序 + public static final String ASC = "ASC"; //排序-升序 + + public static final BigDecimal MINUS_ONE = new BigDecimal("-1"); //BigDecimal类型的-1 + public static final int LIMIT_CNY_DECIMAL = 2;//交易限额CNY保留小数位长度 +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/ConfigConstants.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/ConfigConstants.java new file mode 100644 index 0000000..9a6e9f5 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/ConfigConstants.java @@ -0,0 +1,20 @@ +package com.blockchain.server.otc.common.constant; + +/*** + * 配置表常量 + */ +public class ConfigConstants { + + /*** + * 配置信息key + */ + public static final String AD_SERVICE_CHARGE = "ad_service_charge"; //广告方是否收取手续费 + public static final String ORDER_SERVICE_CHARGE = "order_service_charge"; //订单方是否收取手续费 + public static final String AUTO_CANCEL = "auto_cancel"; //自动撤单是否开启 + public static final String AUTO_CANCEL_INTERVAL = "auto_cancel_interval"; //自动撤单时间间隔 + public static final String SELL_SERVICE_CHARGE = "sell_service_charge"; //卖家是否收取手续费 + public static final String MARKET_SELL_COUNT = "market_sell_ad_count"; //市商可发布多少卖单 + public static final String MARKET_BUY_COUNT = "market_buy_ad_count"; //市商可发布多少买单 + public static final String MARKET_FREEZE_COIN = "market_freeze_coin"; //市商押金代币 + public static final String MARKET_FREEZE_AMOUNT = "market_freeze_amount"; //市商押金数量 +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/MarketApplyConstants.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/MarketApplyConstants.java new file mode 100644 index 0000000..5c8b080 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/MarketApplyConstants.java @@ -0,0 +1,20 @@ +package com.blockchain.server.otc.common.constant; + +/*** + * 市商申请表常量 + */ +public class MarketApplyConstants { + + /*** + * 申请类型 + */ + public static final String MARKET = "MARKET"; //申请市商 + public static final String CANCEL = "CANCEL"; //申请取消市商 + + /*** + * 申请记录状态 + */ + public static final String NEW = "NEW"; //新建、待处理 + public static final String AGREE = "AGREE"; //同意 + public static final String REJECT = "REJECT"; //驳回 +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/MarketUserConstants.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/MarketUserConstants.java new file mode 100644 index 0000000..e935f87 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/MarketUserConstants.java @@ -0,0 +1,14 @@ +package com.blockchain.server.otc.common.constant; + +/*** + * 市商用户表常量 + */ +public class MarketUserConstants { + + /*** + * 市商状态 + */ + public static final String NOTMARKET = "NOTMARKET"; //未认证 + public static final String MARKET = "MARKET"; //认证 + public static final String CANCELING = "CANCELING"; //取消中 +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/OrderConstants.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/OrderConstants.java new file mode 100644 index 0000000..9a809b9 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/OrderConstants.java @@ -0,0 +1,17 @@ +package com.blockchain.server.otc.common.constant; + +/*** + * 订单表常量 + */ +public class OrderConstants { + + /*** + * 订单状态和用户操作状态 + */ + public static final String NEW = "NEW"; //订单状态和用户操作状态:新建 + public static final String UNDERWAY = "UNDERWAY"; //订单状态:进行中 + public static final String FINISH = "FINISH"; //订单状态:已完成 + public static final String CANCEL = "CANCEL"; //订单状态和用户操作状态:已撤销 + public static final String APPEAL = "APPEAL"; //订单状态和用户操作状态:申诉 + public static final String CONFIRM = "CONFIRM"; //用户操作状态:已确认 +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/UserHandleConstants.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/UserHandleConstants.java new file mode 100644 index 0000000..a52e144 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/UserHandleConstants.java @@ -0,0 +1,25 @@ +package com.blockchain.server.otc.common.constant; + +/*** + * 用户操作日志表常量 + */ +public class UserHandleConstants { + + /*** + * 用户操作类型 + */ + public static final String PUBLISH = "PUBLISH"; //发布广告 + public static final String DEAL = "DEAL"; //买入或出售 + public static final String PAY = "PAY"; //确认付款 + public static final String RECEIPT = "RECEIPT"; //确认收款 + public static final String CANCEL = "CANCEL"; //撤销 + public static final String APPEAL = "APPEAL"; //申诉 + public static final String PENDING = "PENDING"; //下架广告 + public static final String DEFAULT = "DEFAULT"; //上架广告 + + /*** + * 用户操作的记录类型 + */ + public static final String AD = "AD"; //广告类型 + public static final String ORDER = "ORDER"; //订单类型 +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/UserPayConstants.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/UserPayConstants.java new file mode 100644 index 0000000..7f7810b --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/constant/UserPayConstants.java @@ -0,0 +1,20 @@ +package com.blockchain.server.otc.common.constant; + +/*** + * 用户支付信息表常量 + */ +public class UserPayConstants { + + /*** + * 用户支付类型 + */ + public static final String BANK = "BANK"; //银行卡 + public static final String WX = "WX"; //微信 + public static final String ZFB = "ZFB"; //支付宝 + + /*** + * 收款图片上传路径 + */ + public static final String WX_URL = "/userpay/wx"; //微信图片上传路径 + public static final String ZFB_URL = "/userpay/zfb"; //支付宝图片上传路径 +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/enums/JgMsgEnums.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/enums/JgMsgEnums.java new file mode 100644 index 0000000..9eead32 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/enums/JgMsgEnums.java @@ -0,0 +1,48 @@ +package com.blockchain.server.otc.common.enums; + +import lombok.Getter; + +/** + * @author :zz + * @date :Created in 2019/4/25 15:32 + * @description:消息推送枚举 + * @modified By: + * @version: 1.0$ + */ +public enum JgMsgEnums { + FIRSTINIT_BUY("FIRSTINIT_BUY", "订单已生成,请您及时付款", null), + FIRSTINIT_SELL("FIRSTINIT_SELL", "订单已生成,请您耐心等待买家付款", null), + CONFIRM_BUYER_BUY("CONFIRM_BUY", "您已确认付款,请等待卖家确认收款和放币", null), + CONFIRM_BUYER_SELL("CONFIRM_SELL", "买家已确认付款,请您检查到账情况并确认付款", null), + CONFIRM_SELLER_BUY("CONFIRM_SELLER_BUY", "交易成功", null), + CONFIRM_SELLER_SELL("CONFIRM_SELLER_SELL", "交易成功", null), + APPEAL("APPEAL", "订单已进行申诉状态,买卖双方可多次提交交易资料或凭证!", null), + REPEAL_BUY("REPEAL_BUY", "订单已撤销", null), + REPEAL_SELL("REPEAL_SELL", "买家已撤销订单,如有疑问请联系客服!", null), + AUTO_REPEAL("AUTO_REPEAL", "买家超过规定时间未付款,订单自动撤销!", null), + ; + + @Getter + private String code; + @Getter + private String name;//值 + @Getter + private String value;//值 string类型 + + JgMsgEnums(String code, String name, String value) { + this.code = code; + this.name = name; + this.value = value; + } + + public String code() { + return code; + } + + +// @Override +// public String name(){ +// return name; +// } + +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/enums/OtcEnums.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/enums/OtcEnums.java new file mode 100644 index 0000000..6915203 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/enums/OtcEnums.java @@ -0,0 +1,84 @@ +package com.blockchain.server.otc.common.enums; + +import lombok.Getter; + +public enum OtcEnums { + PASS_NULL(8701, "请输入密码!", "Please enter password!"), + USERID_NULL(8702, "用户id为空!", "User id is empty!"), + PUBLISH_AD_COIN_NULL(8703, "请选择交易货币!", "Please select the currency of exchange!"), + PUBLISH_AD_UNIT_NULL(8704, "请选择交易单位!", "Please choose trading unit!"), + PUBLISH_AD_MINLIMIT_NULL(8705, "请输入交易最小限额!", "Please enter minimum trading quota!"), + PUBLISH_AD_MINLIMIT_ERROR(8706, "交易最小限额不合法!", "Trading the minimum limit is illegal!"), + PUBLISH_AD_MINLIMIT_TOO_HIGH(8707, "交易最小限额过高!", "Trading minimum is too high!"), + PUBLISH_AD_PRICE_NULL(8708, "请输入单价!", "Please enter unit price!"), + PUBLISH_AD_PRICE_ERROR(8709, "单价小数长度不合法!", "Unit price decimal length illegal!"), + PUBLISH_AD_TOTALNUM_ERROR(8710, "数量小数长度不合法!", "Illegal decimal length!"), + PUBLISH_AD_TOTALNUM_NULL(8711, "请输入数量!", "Please enter quantity!"), + PUBLISH_AD_PAYTYPE_NULL(8712, "请选择收款方式!", "Please choose payment method!"), + PUBLISH_AD_PAY_ERROR(8713, "收款方式信息异常!", "Payment method information abnormal!"), + PUBLISH_AD_PAY_BANK_NULL(8714, "您还未绑定银行卡收款信息!", "You have not bound the bank card collection information!"), + PUBLISH_AD_PAY_WX_NULL(8715, "您还未绑定微信收款信息!", "You have not bound WeChat collection information!"), + PUBLISH_AD_PAY_ZFB_NULL(8716, "您还未绑定支付宝收款信息!", "You have not bound alipay collection information!"), + CANCEL_AD_ORDERS_UNFINISHED(8721, "操作失败,当前广告还有未完成订单!", "Operation failed, the current advertisement still has unfinished order!"), + USER_PAY_INSERT_EXIST(8722, "操作失败,已存在相同收款信息!", "Operation failed, the same collection information already exists!"), + ORDER_AD_NULL(8723, "操作失败,交易广告不存在!", "Operation failed, trade advertisement does not exist!"), + ORDER_ID_NULL(8724, "操作失败,订单id为空!", "Operation failed, order id is empty!"), + ORDER_USERID_EQUALS_AD_USERID(8725, "请勿与自己发布的广告交易!", "Do not trade with your own advertising!"), + ORDER_NULL(8726, "操作失败,订单不存在!", "Operation failed, order does not exist!"), + ORDER_BUY_STATUS_CHANGES(8727, "买家状态已修改,请刷新重试!", "Buyer status has been modified, please refresh and try again!"), + ORDER_SELL_STATUS_CHANGES(8728, "卖家状态已修改,请刷新重试!", "Seller status has been modified, please refresh and try again!"), + ORDER_STATUS_CHANGES(8729, "订单状态已修改,请刷新重试!", "Order status has been modified, please refresh and try again!"), + ORDER_DEAL_ADID_NULL(8730, "操作失败,广告id为空!", "Operation failed, advertisement id is empty!"), + ORDER_DEAL_AD_TYPE_ERROR(8731, "操作失败,广告类型异常!", "Operation failed, advertisement type is abnormal!"), + ORDER_DEAL_AMOUNT_NULL(8732, "请输入交易数量!", "Please enter transaction quantity!"), + ORDER_DEAL_PRICE_NULL(8733, "单价为空!", "Unit price is empty!"), + ORDER_DEAL_TURNOVER_NULL(8734, "交易额为空!", "Trading volume is empty!"), + ORDER_DEAL_AD_STATUS_ERROR(8735, "广告状态已改变,请刷新重试!", "The advertising status has changed, please refresh and try again!"), + ORDER_DEAL_AD_NUM_ZERO(8736, "广告数量不足,请刷新重新!", "The advertisement quantity is insufficient, please refresh again!"), + ORDER_DEAL_AMOUNT_DECIMAL_ERROR(8737, "交易数量小数长度不合法!", "Illegal to trade decimal length!"), + ORDER_DEAL_TURNOVER_DECIMAL_ERROR(8738, "交易金额小数长度不合法!", "Transaction amount decimal length illegal!"), + ORDER_DEAL_PRICE_NOT_REAL_TIME(8739, "广告单价已修改,请刷新重试!", "The advertising unit price has been modified, please refresh and try again!"), + ORDER_DEAL_AD_PAY_NOT_BINGDING(8740, "您需激活至少一种买方的收款方式,才可进行出售!", "You need to activate at least one buyer's payment method before you can sell!"), + ORDER_PAY_PAYTYPE_NULL(8741, "请选择其中一种支付方式!", "Please choose one of the payment methods!"), + ORDER_PAY_USERID_NOT_EQUALS(8742, "操作失败,付款用户和下单用户id不一致!", "Operation failed, the id of the paying user and ordering user is inconsistent!"), + ORDER_PAY_ERROR(8743, "支付方式非法,请选择广告设置的支付方式!", "Illegal payment method, please choose the payment method set by the advertisement!"), + ORDER_CANCEL_USERID_NOT_EQUALS(8744, "操作失败,撤单用户和下单用户id不一致!", "Operation failed, the user id of reorder and reorder is inconsistent!"), + ORDER_CANCEL_TYPE_NOT_BUY(8745, "操作失败,只有买单可以撤单!", "Operation failure, only the bill can be removed!"), + ORDER_APPEAL_USERID_NOT_EQUALS(8746, "操作失败,申诉用户和订单用户id不一致!", "Operation failed, appeal user and order user id are not consistent!"), + USER_PAY_ACCOUNTINFO_NULL(8747, "请输入收款账户!", "Please enter the collection account!"), + USER_PAY_CODE_URL_NULL(8748, "请上传收款码!", "Please upload payment code!"), + USER_PAY_BANK_NUMBER_NULL(8749, "请输入银行卡号!", "Please enter bank card number!"), + USER_PAY_BANK_USER_NAME_NULL(8750, "请输入持卡人!", "Please enter cardholder!"), + USER_PAY_BANK_TYPE_NULL(8751, "请输入开户银行!", "Please enter the bank of deposit!"), + COIN_NULL(8752, "当前平台不支持该货币交易!", "The current platform does not support this currency transaction!"), + WALLET_COIN_NET_ERROR(8753, "操作失败,钱包异常!", "Operation failed, wallet abnormal!"), + UPLOAD_ERROR(8754, "图片上传失败,请刷新重试!", "Image upload failed, please refresh and try again!"), + BUY_OR_SELL_TYPE_ERROR(8755, "买卖类型异常!", "Abnormal trading type!"), + BUY_USER_OR_SELL_USER_ERROR(8756, "用户角色识别异常!", "User role recognition exception!"), + TEMPORARILY_NOT_OPENED(8757, "暂停交易!", "Suspended trading"), + AD_ID_NULL(8758, "操作失败,广告Id为空!", "Operation failed, advertisement Id is empty!"), + AD_USERID_NOT_EQUALS(8759, "操作失败,操作用户和发布用户id不一致!", "Operation failed, operation user and published user id are inconsistent!"), + AD_STATUS_ERROR(8760, "操作失败,广告状态以改变,请刷新重试!", "Operation failed, advertisement status changed, please refresh and try again!"), + PUBLISH_AD_EXCEED_THE_LIMIT(8761, "发布失败,未完成广告超过发布数量限制!", "Launch failed, incomplete ads exceed release limit!"), + AD_LAST_NUM_LESS_THAN_LIMIT(8762, "操作失败,剩余数量不足以进行交易!", "Operation failed, not enough left to trade!"), + MARKET_APPLY_HAS_MORE(8763, "操作失败,已存在未处理的申请记录!", "Operation failed, there are already unprocessed application records!"), + MARKET_FREEZE_NULL(8764, "您还不是商家,无法申请取消!", "You are not a marketer and cannot apply for cancellation!"), + USER_NOT_MARKET(8765, "您还不是商家,无法发布广告!", "You are not a marketer and cannot advertise!"), + MARKET_USER_NULL(8766, "您还不是商家,无法申请取消!", "You are not a marketer and cannot apply for cancellation!"), + CANCEL_MARKET_USER_STATUS_ERROR(8767, "当前商家状态无法取消!", "The current market status cannot be cancelled!"), + USER_IS_MRAKET(8768, "您已是商家,请勿重复操作!", "You are already a merchant, do not repeat the operation!"), + ; + + @Getter + private int code; + @Getter + private String cnmsg; + @Getter + private String enMsg; + + OtcEnums(int code, String cnmsg, String enMsg) { + this.code = code; + this.cnmsg = cnmsg; + this.enMsg = enMsg; + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/exception/OtcException.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/exception/OtcException.java new file mode 100644 index 0000000..2fe2d6b --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/exception/OtcException.java @@ -0,0 +1,35 @@ +package com.blockchain.server.otc.common.exception; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.otc.common.enums.OtcEnums; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +public class OtcException extends BaseException { + public OtcException(OtcEnums rs) { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + //可能是定时器调用,避免获取request空指针 + if (servletRequestAttributes == null) { + this.code = rs.getCode(); + this.msg = rs.getCnmsg(); + } else { + HttpServletRequest request = servletRequestAttributes.getRequest(); + String userLocale = HttpRequestUtil.getUserLocale(request); + String msg = ""; + switch (userLocale) { + case BaseConstant.USER_LOCALE_EN_US: + msg = rs.getEnMsg(); + break; + default: + msg = rs.getCnmsg(); + break; + } + this.code = rs.getCode(); + this.msg = msg; + } + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/interceptor/InterceptorConf.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/interceptor/InterceptorConf.java new file mode 100644 index 0000000..11e95db --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/interceptor/InterceptorConf.java @@ -0,0 +1,61 @@ +package com.blockchain.server.otc.common.interceptor; + +import com.blockchain.server.base.interceptor.LoginInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class InterceptorConf implements WebMvcConfigurer { + @Bean + public LoginInterceptor loginInterceptor() { + return new LoginInterceptor(); + } + + @Bean + public TradingInterceptor tradingInterceptor() { + return new TradingInterceptor(); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(loginInterceptor()) + .addPathPatterns("/**") + .excludePathPatterns("/coin/**") + .excludePathPatterns("/ad/listBuyAd") + .excludePathPatterns("/ad/listSellAd") + .excludePathPatterns("/inner/**") + .excludePathPatterns("/webjars/**") + .excludePathPatterns("/swagger-ui.html") + .excludePathPatterns("/swagger-resources/**") + .excludePathPatterns("/v2/api-docs/**"); + +// registry.addInterceptor(tradingInterceptor()) +// .addPathPatterns("/ad/publishBuyAd") +// .addPathPatterns("/ad/publishSellAd") +// .addPathPatterns("/ad/cancelAd") +// .addPathPatterns("/order/buyOrder") +// .addPathPatterns("/order/sellOrder") +// .addPathPatterns("/order/cancelBuyOrder") +// .addPathPatterns("/order/receipt") +// .addPathPatterns("/order/pay") +// .addPathPatterns("/appeal/**"); + } + + @Bean + public CorsFilter corsFilter() { + CorsConfiguration config = new CorsConfiguration(); + config.addAllowedOrigin("*"); + config.setAllowCredentials(true); + config.addAllowedMethod("*"); + config.addAllowedHeader("*"); + UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource(); + configSource.registerCorsConfiguration("/**", config); + return new CorsFilter(configSource); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/interceptor/TradingInterceptor.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/interceptor/TradingInterceptor.java new file mode 100644 index 0000000..bcb4005 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/interceptor/TradingInterceptor.java @@ -0,0 +1,16 @@ +package com.blockchain.server.otc.common.interceptor; + +import com.blockchain.server.otc.common.enums.OtcEnums; +import com.blockchain.server.otc.common.exception.OtcException; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class TradingInterceptor extends HandlerInterceptorAdapter { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + throw new OtcException(OtcEnums.TEMPORARILY_NOT_OPENED); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/listener/RedisListenerBean.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/listener/RedisListenerBean.java new file mode 100644 index 0000000..a30745a --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/listener/RedisListenerBean.java @@ -0,0 +1,55 @@ +package com.blockchain.server.otc.common.listener; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.listener.PatternTopic; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; + +@Configuration +public class RedisListenerBean { + + /*** + * '__keyevent@0__:expired' //@后面指定使用redis db库 + * redisKey失效触发的监听事件 + */ + @Value("${spring.redis.expired.listener}") + private String redisExpiredListenerKey; + + @Bean + RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, + MessageListenerAdapter listenerAdapter) { + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + //监听过期的redisKey + container.addMessageListener(listenerAdapter, new PatternTopic(redisExpiredListenerKey)); + //container可以添加多个messageListener + return container; + } + + /*** + * 消息监听器适配器,绑定消息处理,利用反射调用消息处理器的业务方法 + * + * @param receiver + * @return + */ + @Bean + MessageListenerAdapter listenerAdapter(RedisReceiver receiver) { + //传入一个消息接收处理,利用反射调用receiveMessage方法 + return new MessageListenerAdapter(receiver, "receiveMessage"); + } + + /*** + * 使用默认的工厂初始化redis操作模板 + * @param connectionFactory + * @return + */ + @Bean + StringRedisTemplate template(RedisConnectionFactory connectionFactory) { + return new StringRedisTemplate(connectionFactory); + } + +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/listener/RedisReceiver.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/listener/RedisReceiver.java new file mode 100644 index 0000000..bfc84cb --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/listener/RedisReceiver.java @@ -0,0 +1,31 @@ +package com.blockchain.server.otc.common.listener; + +import com.blockchain.server.otc.redis.OrderCache; +import com.blockchain.server.otc.service.OrderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class RedisReceiver { + + @Autowired + private OrderService orderService; + + /** + * redisKey过期后,会通过反射调用此方法 + * + * @param message + */ + public void receiveMessage(String message) { + //订单前缀key + String newOrderKey = OrderCache.getNewOrderPre(); + //判断Key的前缀,如果是订单key的前缀进行撤单 + if (message.startsWith(newOrderKey)) { + //获取订单id + String orderId = message.substring(newOrderKey.length()); + //撤销订单 + orderService.autoCancelOrder(orderId); + } + } + +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/util/CheckDecimalUtil.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/util/CheckDecimalUtil.java new file mode 100644 index 0000000..bcd8a7e --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/util/CheckDecimalUtil.java @@ -0,0 +1,27 @@ +package com.blockchain.server.otc.common.util; + +import com.blockchain.server.otc.common.enums.OtcEnums; +import com.blockchain.server.otc.common.exception.OtcException; + +import java.math.BigDecimal; + +public class CheckDecimalUtil { + + /*** + * 检查数据小数长度是否符合 + * @param checkBigDecimal + * @param decimal + * @param enums + */ + public static void checkDecimal(BigDecimal checkBigDecimal, Integer decimal, OtcEnums enums) { + //转为String类型 + String str = checkBigDecimal.toPlainString(); + //存在小数点 + if (str.indexOf(".") != -1) { + //判断小数位长度是否超出限制 + if ((str.length() - str.indexOf(".") - 1) > decimal) { + throw new OtcException(enums); + } + } + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/util/ImUtil.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/util/ImUtil.java new file mode 100644 index 0000000..eac55eb --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/common/util/ImUtil.java @@ -0,0 +1,55 @@ +package com.blockchain.server.otc.common.util; + +import com.alibaba.fastjson.JSON; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.otc.dto.jgim.JgMassageParam; +import com.blockchain.server.otc.dto.jgim.JgMessageBodyDTO; +import com.blockchain.server.otc.feign.IMJGFeign; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * @author :zz + * @date :Created in 2019/4/25 17:01 + * @description:Im工具接口 + * @modified By: + * @version: 1.0$ + */ +@Component +public class ImUtil { + @Autowired + private IMJGFeign imjgFeign; + + String PTYPE = "OTC"; + + /*** + * + * @param content 发送内容 + * @param sendUser 发送方 + * @param recUser 接收方 + * @param dataId 业务id + * @return + */ + public ResultDTO postMsgIMSelf(String content, String sendUser, String recUser, String dataId) { + JgMassageParam param = new JgMassageParam(); + JgMessageBodyDTO jgMessageBodyDTO = new JgMessageBodyDTO(); + jgMessageBodyDTO.setText(content); + jgMessageBodyDTO.setMsg_type(JgMassageParam.MsgTypeEnums.text.getCode()); + param.setPType(PTYPE); + param.setDataId(dataId); + param.setMsgType(JgMassageParam.MsgTypeEnums.text.getCode()); + param.setMsgBodyJson(JSON.toJSONString(jgMessageBodyDTO)); + param.setNodeCue(2); + param.setFuserId(sendUser); + param.setTuserId(recUser); + return imjgFeign.sendSingleMsg( + param.getPType(), + param.getDataId(), + param.getMsgType(), + param.getFuserId(), + param.getTuserId(), + param.getMsgBodyJson(), + param.getNodeCue()); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/AdController.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/AdController.java new file mode 100644 index 0000000..153e930 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/AdController.java @@ -0,0 +1,222 @@ +package com.blockchain.server.otc.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.base.controller.BaseController; +import com.blockchain.server.otc.common.constant.AdConstants; +import com.blockchain.server.otc.common.constant.CommonConstans; +import com.blockchain.server.otc.controller.api.AdApi; +import com.blockchain.server.otc.dto.ad.ListAdDTO; +import com.blockchain.server.otc.dto.ad.ListUserAdDTO; +import com.blockchain.server.otc.dto.ad.PublishAdParamDTO; +import com.blockchain.server.otc.entity.Ad; +import com.blockchain.server.otc.service.AdService; +import com.github.pagehelper.PageHelper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +@Api(AdApi.AD_API) +@RestController +@RequestMapping("/ad") +public class AdController extends BaseController { + + @Autowired + private AdService adService; + @Autowired + private RedisTemplate redisTemplate; + + @ApiOperation(value = AdApi.publishBuyAd.METHOD_TITLE_NAME, + notes = AdApi.publishBuyAd.METHOD_TITLE_NOTE) + @PostMapping("/publishBuyAd") + public ResultDTO publishBuyAd(@ApiParam(AdApi.publishBuyAd.METHOD_API_PARAM) PublishAdParamDTO param, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + param.setUserId(userId); + adService.publishBuyAd(param); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = AdApi.publishSellAd.METHOD_TITLE_NAME, + notes = AdApi.publishSellAd.METHOD_TITLE_NOTE) + @PostMapping("/publishSellAd") + public ResultDTO publishSellAd(@ApiParam(AdApi.publishSellAd.METHOD_API_PARAM) PublishAdParamDTO param, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + param.setUserId(userId); + adService.publishSellAd(param); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = AdApi.cancelAd.METHOD_TITLE_NAME, + notes = AdApi.cancelAd.METHOD_TITLE_NOTE) + @PostMapping("/cancelAd") + public ResultDTO cancelAd(@ApiParam(AdApi.cancelAd.METHOD_API_ADID) @RequestParam("adId") String adId, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + adService.cancelAd(userId, adId); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = AdApi.defaultAd.METHOD_TITLE_NAME, + notes = AdApi.defaultAd.METHOD_TITLE_NOTE) + @PostMapping("/defaultAd") + public ResultDTO defaultAd(@ApiParam(AdApi.defaultAd.METHOD_API_ADID) @RequestParam("adId") String adId, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + adService.setAdToDefault(userId, adId); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = AdApi.pendingAd.METHOD_TITLE_NAME, + notes = AdApi.pendingAd.METHOD_TITLE_NOTE) + @PostMapping("/pendingAd") + public ResultDTO pendingAd(@ApiParam(AdApi.pendingAd.METHOD_API_ADID) @RequestParam("adId") String adId, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + adService.setAdToPending(userId, adId); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = AdApi.listBuyAd.METHOD_TITLE_NAME, + notes = AdApi.listBuyAd.METHOD_TITLE_NOTE) + @GetMapping("/listBuyAd") + public ResultDTO listBuyAd(@ApiParam(AdApi.METHOD_API_COIN_NAME) @RequestParam("coinName") String coinName, + @ApiParam(AdApi.METHOD_API_UNIT_NAME) @RequestParam("unitName") String unitName, + @ApiParam(AdApi.METHOD_API_BEGIN_TIME) @RequestParam(value = "beginTime", required = false) String beginTime, + @ApiParam(AdApi.METHOD_API_END_TIME) @RequestParam(value = "endTime", required = false) String endTime, + @ApiParam(AdApi.METHOD_API_PAGE_NUM) @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, + @ApiParam(AdApi.METHOD_API_PAGE_SIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, + HttpServletRequest request) { + String userId = SSOHelper.getUserIdIsExits(redisTemplate, request); + List buyAdList = listAd(userId, CommonConstans.BUY, coinName, unitName, CommonConstans.DESC, beginTime, endTime, pageNum, pageSize); + return ResultDTO.requstSuccess(buyAdList); + } + + @ApiOperation(value = AdApi.listSellAd.METHOD_TITLE_NAME, + notes = AdApi.listSellAd.METHOD_TITLE_NOTE) + @GetMapping("/listSellAd") + public ResultDTO listSellAd(@ApiParam(AdApi.METHOD_API_COIN_NAME) @RequestParam("coinName") String coinName, + @ApiParam(AdApi.METHOD_API_UNIT_NAME) @RequestParam("unitName") String unitName, + @ApiParam(AdApi.METHOD_API_BEGIN_TIME) @RequestParam(value = "beginTime", required = false) String beginTime, + @ApiParam(AdApi.METHOD_API_END_TIME) @RequestParam(value = "endTime", required = false) String endTime, + @ApiParam(AdApi.METHOD_API_PAGE_NUM) @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, + @ApiParam(AdApi.METHOD_API_PAGE_SIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, + HttpServletRequest request) { + String userId = SSOHelper.getUserIdIsExits(redisTemplate, request); + List sellAdList = listAd(userId, CommonConstans.SELL, coinName, unitName, CommonConstans.ASC, beginTime, endTime, pageNum, pageSize); + return ResultDTO.requstSuccess(sellAdList); + } + + @ApiOperation(value = AdApi.listUserAd.METHOD_TITLE_NAME, + notes = AdApi.listUserAd.METHOD_TITLE_NOTE) + @GetMapping("/listUserAd") + public ResultDTO listUserAd(@ApiParam(AdApi.METHOD_API_COIN_NAME) @RequestParam(value = "coinName", required = false) String coinName, + @ApiParam(AdApi.METHOD_API_UNIT_NAME) @RequestParam(value = "unitName", required = false) String unitName, + @ApiParam(AdApi.listUserAd.METHOD_API_AD_TYPE) @RequestParam(value = "adType", required = false) String adType, + @ApiParam(AdApi.listUserAd.METHOD_API_AD_STATUS) @RequestParam(value = "adStatus", required = false) String adStatus, + @ApiParam(AdApi.METHOD_API_BEGIN_TIME) @RequestParam(value = "beginTime", required = false) String beginTime, + @ApiParam(AdApi.METHOD_API_END_TIME) @RequestParam(value = "endTime", required = false) String endTime, + @ApiParam(AdApi.METHOD_API_PAGE_NUM) @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, + @ApiParam(AdApi.METHOD_API_PAGE_SIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + PageHelper.startPage(pageNum, pageSize); + List userAdList = adService.listUserAd(userId, adType, coinName, unitName, + adStatus, beginTime, endTime); + return ResultDTO.requstSuccess(userAdList); + } + + @ApiOperation(value = AdApi.selectById.METHOD_TITLE_NAME, + notes = AdApi.selectById.METHOD_TITLE_NOTE) + @GetMapping("/selectById") + public ResultDTO selectById(@ApiParam(AdApi.selectById.METHOD_API_AD_ID) @RequestParam("adId") String adId, + HttpServletRequest request) { + //验证下是否已登录 + SSOHelper.getUser(redisTemplate, request); + Ad ad = adService.selectById(adId); + return ResultDTO.requstSuccess(ad); + } + + /****************************pc分页接口****************************/ + + @ApiOperation(value = AdApi.pcListBuyAd.METHOD_TITLE_NAME, + notes = AdApi.pcListBuyAd.METHOD_TITLE_NOTE) + @GetMapping("/pcListBuyAd") + public ResultDTO pcListBuyAd(@ApiParam(AdApi.METHOD_API_COIN_NAME) @RequestParam("coinName") String coinName, + @ApiParam(AdApi.METHOD_API_UNIT_NAME) @RequestParam("unitName") String unitName, + @ApiParam(AdApi.METHOD_API_BEGIN_TIME) @RequestParam(value = "beginTime", required = false) String beginTime, + @ApiParam(AdApi.METHOD_API_END_TIME) @RequestParam(value = "endTime", required = false) String endTime, + @ApiParam(AdApi.METHOD_API_PAGE_NUM) @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, + @ApiParam(AdApi.METHOD_API_PAGE_SIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, + HttpServletRequest request) { + String userId = SSOHelper.getUserIdIsExits(redisTemplate, request); + List buyAdList = listAd(userId, CommonConstans.BUY, coinName, unitName, CommonConstans.DESC, beginTime, endTime, pageNum, pageSize); + return generatePage(buyAdList); + } + + @ApiOperation(value = AdApi.pcListSellAd.METHOD_TITLE_NAME, + notes = AdApi.pcListSellAd.METHOD_TITLE_NOTE) + @GetMapping("/pcListSellAd") + public ResultDTO pcListSellAd(@ApiParam(AdApi.METHOD_API_COIN_NAME) @RequestParam("coinName") String coinName, + @ApiParam(AdApi.METHOD_API_UNIT_NAME) @RequestParam("unitName") String unitName, + @ApiParam(AdApi.METHOD_API_BEGIN_TIME) @RequestParam(value = "beginTime", required = false) String beginTime, + @ApiParam(AdApi.METHOD_API_END_TIME) @RequestParam(value = "endTime", required = false) String endTime, + @ApiParam(AdApi.METHOD_API_PAGE_NUM) @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, + @ApiParam(AdApi.METHOD_API_PAGE_SIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, + HttpServletRequest request) { + String userId = SSOHelper.getUserIdIsExits(redisTemplate, request); + List sellAdList = listAd(userId, CommonConstans.SELL, coinName, unitName, CommonConstans.ASC, beginTime, endTime, pageNum, pageSize); + return generatePage(sellAdList); + } + + @ApiOperation(value = AdApi.pcListUserAd.METHOD_TITLE_NAME, + notes = AdApi.pcListUserAd.METHOD_TITLE_NOTE) + @GetMapping("/pcListUserAd") + public ResultDTO pcListUserAd(@ApiParam(AdApi.METHOD_API_COIN_NAME) @RequestParam("coinName") String coinName, + @ApiParam(AdApi.METHOD_API_UNIT_NAME) @RequestParam("unitName") String unitName, + @ApiParam(AdApi.pcListUserAd.METHOD_API_AD_TYPE) @RequestParam(value = "adType", required = false) String adType, + @ApiParam(AdApi.pcListUserAd.METHOD_API_AD_STATUS) @RequestParam(value = "adStatus", required = false) String adStatus, + @ApiParam(AdApi.METHOD_API_BEGIN_TIME) @RequestParam(value = "beginTime", required = false) String beginTime, + @ApiParam(AdApi.METHOD_API_END_TIME) @RequestParam(value = "endTime", required = false) String endTime, + @ApiParam(AdApi.METHOD_API_PAGE_NUM) @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, + @ApiParam(AdApi.METHOD_API_PAGE_SIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + PageHelper.startPage(pageNum, pageSize); + List userAdList = adService.listUserAd(userId, adType, coinName, unitName, adStatus, beginTime, endTime); + return generatePage(userAdList); + } + + /*** + * 查询广告列表 + * @param userId + * @param adType + * @param coinName + * @param unitName + * @param priceSort + * @param beginTime + * @param endTime + * @param pageNum + * @param pageSize + * @return + */ + private List listAd(String userId, String adType, String coinName, String unitName, String priceSort, + String beginTime, String endTime, Integer pageNum, Integer pageSize) { + //查询的广告状态 + String[] adStatus = {AdConstants.DEFAULT, AdConstants.UNDERWAY}; + //分页 + PageHelper.startPage(pageNum, pageSize); + //查询广告列表 + List adList = adService.listAd(userId, adType, coinName, + unitName, adStatus, priceSort, beginTime, endTime); + return adList; + } + +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/AppealController.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/AppealController.java new file mode 100644 index 0000000..3834cd3 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/AppealController.java @@ -0,0 +1,88 @@ +package com.blockchain.server.otc.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.FileUploadHelper; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.otc.common.constant.AppealConstants; +import com.blockchain.server.otc.common.enums.OtcEnums; +import com.blockchain.server.otc.common.exception.OtcException; +import com.blockchain.server.otc.controller.api.AppealApi; +import com.blockchain.server.otc.dto.appeal.AppealHandleLogDTO; +import com.blockchain.server.otc.service.AppealHandleLogService; +import com.blockchain.server.otc.service.AppealService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +@Api(AppealApi.APPEAL_API) +@RestController +@RequestMapping("/appeal") +public class AppealController extends FileUploadHelper { + + @Autowired + private AppealService appealService; + @Autowired + private AppealHandleLogService appealHandleLogService; + @Autowired + private RedisTemplate redisTemplate; + @Value("${FILES_DIR.ROOT}") + private String FILE_ROOT_PATH;//文件上传根目录 + + @ApiOperation(value = AppealApi.handleAppeal.METHOD_TITLE_NAME, + notes = AppealApi.handleAppeal.METHOD_TITLE_NOTE) + @PostMapping("/handleAppeal") + public ResultDTO handleAppeal(@ApiParam(AppealApi.handleAppeal.METHOD_API_ORDER_ID) @RequestParam("orderId") String orderId, + @ApiParam(AppealApi.handleAppeal.METHOD_API_URLS) @RequestParam("urls") String[] urls, + @ApiParam(AppealApi.handleAppeal.METHOD_API_REMARK) @RequestParam("remark") String remark, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + appealService.appeal(userId, orderId, urls, remark); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = AppealApi.uploadAppealFile.METHOD_TITLE_NAME, + notes = AppealApi.uploadAppealFile.METHOD_TITLE_NOTE) + @PostMapping("/uploadAppealFile") + public ResultDTO uploadAppealFile(@ApiParam(AppealApi.uploadAppealFile.METHOD_API_APPEAL_FILE) @RequestParam("appealFile") MultipartFile file, + HttpServletRequest request) { + //认证用户是否有登录 + SSOHelper.getUser(redisTemplate, request); + String filePath = saveFile(file); + return ResultDTO.requstSuccess(filePath); + } + + @ApiOperation(value = AppealApi.selectAppealHandleLog.METHOD_TITLE_NAME, + notes = AppealApi.selectAppealHandleLog.METHOD_TITLE_NOTE) + @GetMapping("/selectAppealHandleLog") + public ResultDTO selectAppealHandleLog(@ApiParam(AppealApi.selectAppealHandleLog.METHOD_API_ORDER_NUMBER) @RequestParam("orderNumber") String orderNumber, + HttpServletRequest request) { + //认证用户是否有登录 + SSOHelper.getUser(redisTemplate, request); + AppealHandleLogDTO appealHandleLogDTO = appealHandleLogService.selectByOrderNumber(orderNumber); + return ResultDTO.requstSuccess(appealHandleLogDTO); + } + + /*** + * 保存图片 + * @param file + * @return + */ + private String saveFile(MultipartFile file) { + try { + //保存图片,返回保存图片的路径 + return saveFile(file, FILE_ROOT_PATH, AppealConstants.APPEAL_URL); + } catch (IOException e) { + e.printStackTrace(); + throw new OtcException(OtcEnums.UPLOAD_ERROR); + } + } + +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/CoinController.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/CoinController.java new file mode 100644 index 0000000..d133bd6 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/CoinController.java @@ -0,0 +1,42 @@ +package com.blockchain.server.otc.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.otc.common.constant.CommonConstans; +import com.blockchain.server.otc.controller.api.CoinApi; +import com.blockchain.server.otc.dto.coin.CoinDTO; +import com.blockchain.server.otc.dto.coin.CoinServiceChargeDTO; +import com.blockchain.server.otc.service.CoinService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Api(CoinApi.COIN_API) +@RestController +@RequestMapping("/coin") +public class CoinController { + + @Autowired + private CoinService coinService; + + @ApiOperation(value = CoinApi.listCoin.METHOD_TITLE_NAME, + notes = CoinApi.listCoin.METHOD_TITLE_NOTE) + @GetMapping("/listCoin") + public ResultDTO listCoin() { + List coinList = coinService.listCoin(CommonConstans.YES); + return ResultDTO.requstSuccess(coinList); + } + + @ApiOperation(value = CoinApi.listCoinServiceCharge.METHOD_TITLE_NAME, + notes = CoinApi.listCoinServiceCharge.METHOD_TITLE_NOTE) + @GetMapping("/listCoinServiceCharge") + public ResultDTO listCoinServiceCharge() { + List coinList = coinService.listCoinServiceCharge(); + return ResultDTO.requstSuccess(coinList); + } + +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/ConfigController.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/ConfigController.java new file mode 100644 index 0000000..949e341 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/ConfigController.java @@ -0,0 +1,48 @@ +package com.blockchain.server.otc.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.base.controller.BaseController; +import com.blockchain.server.otc.controller.api.ConfigApi; +import com.blockchain.server.otc.service.ConfigService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; + +@Api(ConfigApi.CONFIG_API) +@RestController +@RequestMapping("/config") +public class ConfigController extends BaseController { + + @Autowired + private ConfigService configService; + @Autowired + private RedisTemplate redisTemplate; + + @ApiOperation(value = ConfigApi.SelectAutoCancelInterval.METHOD_TITLE_NAME, + notes = ConfigApi.SelectAutoCancelInterval.METHOD_TITLE_NOTE) + @GetMapping("/selectAutoCancelInterval") + public ResultDTO selectAutoCancelInterval() { + return ResultDTO.requstSuccess(configService.selectAutoCancelInterval()); + } + + @ApiOperation(value = ConfigApi.SelectMarketFreezeCoin.METHOD_TITLE_NAME, + notes = ConfigApi.SelectMarketFreezeCoin.METHOD_TITLE_NOTE) + @GetMapping("/selectMarketFreezeCoin") + public ResultDTO selectMarketFreezeCoin(HttpServletRequest request) { + SSOHelper.getUser(redisTemplate, request); + return ResultDTO.requstSuccess(configService.selectMarketFreezeCoin()); + } + + @ApiOperation(value = ConfigApi.SelectMarketFreezeAmount.METHOD_TITLE_NAME, + notes = ConfigApi.SelectMarketFreezeAmount.METHOD_TITLE_NOTE) + @GetMapping("/selectMarketFreezeAmount") + public ResultDTO selectMarketFreezeAmount(HttpServletRequest request) { + SSOHelper.getUser(redisTemplate, request); + return ResultDTO.requstSuccess(configService.selectMarketFreezeAmount()); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/MarketApplyController.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/MarketApplyController.java new file mode 100644 index 0000000..5ae310f --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/MarketApplyController.java @@ -0,0 +1,55 @@ +package com.blockchain.server.otc.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.otc.common.constant.MarketApplyConstants; +import com.blockchain.server.otc.controller.api.MarketApplyApi; +import com.blockchain.server.otc.service.MarketApplyService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +@Api(MarketApplyApi.MARKET_APPLY_API) +@RestController +@RequestMapping("/marketApply") +public class MarketApplyController { + + @Autowired + private MarketApplyService marketApplyService; + @Autowired + private RedisTemplate redisTemplate; + + @ApiOperation(value = MarketApplyApi.CancelApply.METHOD_TITLE_NAME, + notes = MarketApplyApi.CancelApply.METHOD_TITLE_NOTE) + @PostMapping("/cancelApply") + public ResultDTO cancelApply(HttpServletRequest request) { + String userId = getUserId(request); + marketApplyService.applyMarket(userId, MarketApplyConstants.CANCEL); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = MarketApplyApi.MarketApply.METHOD_TITLE_NAME, + notes = MarketApplyApi.MarketApply.METHOD_TITLE_NOTE) + @PostMapping("/marketApply") + public ResultDTO marketApply(HttpServletRequest request) { + String userId = getUserId(request); + marketApplyService.applyMarket(userId, MarketApplyConstants.MARKET); + return ResultDTO.requstSuccess(); + } + + /*** + * 获取用户id + * @param request + * @return + */ + private String getUserId(HttpServletRequest request) { + return SSOHelper.getUserId(redisTemplate, request); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/MarketUserController.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/MarketUserController.java new file mode 100644 index 0000000..b4af9d6 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/MarketUserController.java @@ -0,0 +1,35 @@ +package com.blockchain.server.otc.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.otc.controller.api.MarketUserApi; +import com.blockchain.server.otc.service.MarketUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +@Api(MarketUserApi.MARKET_USER_API) +@RestController +@RequestMapping("/marketUser") +public class MarketUserController { + + @Autowired + private MarketUserService marketUserService; + @Autowired + private RedisTemplate redisTemplate; + + @ApiOperation(value = MarketUserApi.GetStatusByUserId.METHOD_TITLE_NAME, + notes = MarketUserApi.GetStatusByUserId.METHOD_TITLE_NOTE) + @GetMapping("/getStatusByUserId") + public ResultDTO getStatusByUserId(HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + String marketUserStatus = marketUserService.getMarketUserStatus(userId); + return ResultDTO.requstSuccess(marketUserStatus); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/OrderController.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/OrderController.java new file mode 100644 index 0000000..5899f53 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/OrderController.java @@ -0,0 +1,135 @@ +package com.blockchain.server.otc.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.base.controller.BaseController; +import com.blockchain.server.otc.controller.api.OrderApi; +import com.blockchain.server.otc.dto.order.OrderDTO; +import com.blockchain.server.otc.service.OrderService; +import com.github.pagehelper.PageHelper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.math.BigDecimal; +import java.util.List; + +@Api(OrderApi.ORDER_API) +@RestController +@RequestMapping("/order") +public class OrderController extends BaseController { + + @Autowired + private OrderService orderService; + @Autowired + private RedisTemplate redisTemplate; + + @ApiOperation(value = OrderApi.buyOrder.METHOD_TITLE_NAME, + notes = OrderApi.buyOrder.METHOD_TITLE_NOTE) + @PostMapping("/buyOrder") + public ResultDTO buyOrder(@ApiParam(OrderApi.buyOrder.METHOD_TITLE_AD_ID) @RequestParam("adId") String adId, + @ApiParam(OrderApi.buyOrder.METHOD_TITLE_AMOUNT) @RequestParam("amount") BigDecimal amount, + @ApiParam(OrderApi.buyOrder.METHOD_TITLE_PRICE) @RequestParam("price") BigDecimal price, + @ApiParam(OrderApi.buyOrder.METHOD_TITLE_TURNOVER) @RequestParam("turnover") BigDecimal turnover, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + String orderId = orderService.buyOrder(userId, adId, amount, price, turnover); + return ResultDTO.requstSuccess(orderId); + } + + @ApiOperation(value = OrderApi.sellOrder.METHOD_TITLE_NAME, + notes = OrderApi.sellOrder.METHOD_TITLE_NOTE) + @PostMapping("/sellOrder") + public ResultDTO sellOrder(@ApiParam(OrderApi.sellOrder.METHOD_TITLE_AD_ID) @RequestParam("adId") String adId, + @ApiParam(OrderApi.sellOrder.METHOD_TITLE_AMOUNT) @RequestParam("amount") BigDecimal amount, + @ApiParam(OrderApi.sellOrder.METHOD_TITLE_PRICE) @RequestParam("price") BigDecimal price, + @ApiParam(OrderApi.sellOrder.METHOD_TITLE_TURNOVER) @RequestParam("turnover") BigDecimal turnover, + @ApiParam(OrderApi.sellOrder.METHOD_TITLE_PASS) @RequestParam("pass") String pass, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + String orderId = orderService.sellOrder(userId, adId, amount, price, turnover, pass); + return ResultDTO.requstSuccess(orderId); + } + + @ApiOperation(value = OrderApi.cancelBuyOrder.METHOD_TITLE_NAME, + notes = OrderApi.cancelBuyOrder.METHOD_TITLE_NOTE) + @PostMapping("/cancelBuyOrder") + public ResultDTO cancelBuyOrder(@ApiParam(OrderApi.cancelBuyOrder.METHOD_TITLE_ORDER_ID) @RequestParam("orderId") String orderId, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + orderService.cancelBuyOrder(userId, orderId); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = OrderApi.pay.METHOD_TITLE_NAME, + notes = OrderApi.pay.METHOD_TITLE_NOTE) + @PostMapping("/pay") + public ResultDTO pay(@ApiParam(OrderApi.pay.METHOD_TITLE_ORDER_ID) @RequestParam("orderId") String orderId, + @ApiParam(OrderApi.pay.METHOD_TITLE_PAY_TYPE) @RequestParam("payType") String payType, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + orderService.pay(userId, orderId, payType); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = OrderApi.receipt.METHOD_TITLE_NAME, + notes = OrderApi.receipt.METHOD_TITLE_NOTE) + @PostMapping("/receipt") + public ResultDTO receipt(@ApiParam(OrderApi.receipt.METHOD_TITLE_ORDER_ID) @RequestParam("orderId") String orderId, + @ApiParam(OrderApi.receipt.METHOD_TITLE_PASS) @RequestParam("pass") String pass, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + orderService.receipt(userId, orderId, pass); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = OrderApi.listUserOrder.METHOD_TITLE_NAME, + notes = OrderApi.listUserOrder.METHOD_TITLE_NOTE) + @GetMapping("/listUserOrder") + public ResultDTO listUserOrder(@ApiParam(OrderApi.listUserOrder.METHOD_TITLE_COIN_NAME) @RequestParam(value = "coinName", required = false) String coinName, + @ApiParam(OrderApi.listUserOrder.METHOD_TITLE_UNIT_NAME) @RequestParam(value = "unitName", required = false) String unitName, + @ApiParam(OrderApi.listUserOrder.METHOD_TITLE_ORDER_TYPE) @RequestParam(value = "orderType", required = false) String orderType, + @ApiParam(OrderApi.listUserOrder.METHOD_TITLE_ORDER_STATUS) @RequestParam(value = "orderStatus", required = false) String orderStatus, + @ApiParam(OrderApi.listUserOrder.METHOD_TITLE_PAY_TYPE) @RequestParam(value = "payType", required = false) String payType, + @ApiParam(OrderApi.listUserOrder.METHOD_TITLE_PAGE_NUM) @RequestParam(value = "pageNum", defaultValue = "1", required = false) Integer pageNum, + @ApiParam(OrderApi.listUserOrder.METHOD_TITLE_PAGE_SIZE) @RequestParam(value = "pageSize", defaultValue = "10", required = false) Integer pageSize, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + PageHelper.startPage(pageNum, pageSize); + List userOrderlist = orderService.listUserOrder(userId, coinName, unitName, orderType, orderStatus, payType); + return ResultDTO.requstSuccess(userOrderlist); + } + + @ApiOperation(value = OrderApi.selectByUserIdAndId.METHOD_TITLE_NAME, + notes = OrderApi.selectByUserIdAndId.METHOD_TITLE_NOTE) + @GetMapping("/selectByUserIdAndId") + public ResultDTO selectByUserIdAndId(@ApiParam(OrderApi.selectByUserIdAndId.METHOD_TITLE_ORDER_ID) @RequestParam("orderId") String orderId, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + OrderDTO order = orderService.selectByUserIdAndOrderId(userId, orderId); + return ResultDTO.requstSuccess(order); + } + + /****************************pc分页接口****************************/ + + @ApiOperation(value = OrderApi.pcListUserOrder.METHOD_TITLE_NAME, + notes = OrderApi.pcListUserOrder.METHOD_TITLE_NOTE) + @GetMapping("/pcListUserOrder") + public ResultDTO pcListUserOrder(@ApiParam(OrderApi.pcListUserOrder.METHOD_TITLE_COIN_NAME) @RequestParam(value = "coinName", required = false) String coinName, + @ApiParam(OrderApi.pcListUserOrder.METHOD_TITLE_UNIT_NAME) @RequestParam(value = "unitName", required = false) String unitName, + @ApiParam(OrderApi.pcListUserOrder.METHOD_TITLE_ORDER_TYPE) @RequestParam(value = "orderType", required = false) String orderType, + @ApiParam(OrderApi.pcListUserOrder.METHOD_TITLE_ORDER_STATUS) @RequestParam(value = "orderStatus", required = false) String orderStatus, + @ApiParam(OrderApi.pcListUserOrder.METHOD_TITLE_PAY_TYPE) @RequestParam(value = "payType", required = false) String payType, + @ApiParam(OrderApi.pcListUserOrder.METHOD_TITLE_PAGE_NUM) @RequestParam(value = "pageNum", required = false, defaultValue = "1") Integer pageNum, + @ApiParam(OrderApi.pcListUserOrder.METHOD_TITLE_PAGE_SIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + PageHelper.startPage(pageNum, pageSize); + List userOrderlist = orderService.listUserOrder(userId, coinName, unitName, orderType, orderStatus, payType); + return generatePage(userOrderlist); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/UserPayInfoController.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/UserPayInfoController.java new file mode 100644 index 0000000..a96413c --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/UserPayInfoController.java @@ -0,0 +1,181 @@ +package com.blockchain.server.otc.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.FileUploadHelper; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.otc.common.constant.UserPayConstants; +import com.blockchain.server.otc.common.enums.OtcEnums; +import com.blockchain.server.otc.common.exception.OtcException; +import com.blockchain.server.otc.controller.api.UserPayApi; +import com.blockchain.server.otc.entity.UserPayInfo; +import com.blockchain.server.otc.service.UserPayInfoService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.List; + +@Api(UserPayApi.USER_PAY_API) +@RestController +@RequestMapping("/userPay") +public class UserPayInfoController extends FileUploadHelper { + + @Autowired + private UserPayInfoService userPayInfoService; + @Autowired + private RedisTemplate redisTemplate; + @Value("${FILES_DIR.ROOT}") + private String FILE_ROOT_PATH; //文件上传路径 + + @ApiOperation(value = UserPayApi.listUserPay.METHOD_TITLE_NAME, + notes = UserPayApi.listUserPay.METHOD_TITLE_NOTE) + @GetMapping("/listUserPay") + public ResultDTO listUserPay( + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + List userPayInfos = userPayInfoService.listUserPay(userId); + return ResultDTO.requstSuccess(userPayInfos); + } + + @ApiOperation(value = UserPayApi.selectByAdPayType.METHOD_TITLE_NAME, + notes = UserPayApi.selectByAdPayType.METHOD_TITLE_NOTE) + @GetMapping("/selectByAdPayType") + public ResultDTO selectByAdPayType(@ApiParam(UserPayApi.selectByAdPayType.METHOD_API_ORDER_ID) @RequestParam("orderId") String orderId, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + List userPayInfos = userPayInfoService.selectSellUserPayInfoByAdPayType(userId, orderId); + return ResultDTO.requstSuccess(userPayInfos); + } + + @ApiOperation(value = UserPayApi.selectByPayType.METHOD_TITLE_NAME, + notes = UserPayApi.selectByPayType.METHOD_TITLE_NOTE) + @GetMapping("/selectByPayType") + public ResultDTO selectByPayType(@ApiParam(UserPayApi.selectByPayType.METHOD_API_PAP_TYPE) @RequestParam("payType") String payType, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + UserPayInfo userPayInfo = userPayInfoService.selectByUserIdAndPayType(userId, payType); + return ResultDTO.requstSuccess(userPayInfo); + } + + @ApiOperation(value = UserPayApi.insertWX.METHOD_TITLE_NAME, + notes = UserPayApi.insertWX.METHOD_TITLE_NOTE) + @PostMapping("/insertWX") + public ResultDTO insertWX(@ApiParam(UserPayApi.insertWX.METHOD_API_ACCOUNT_INFO) @RequestParam("accountInfo") String accountInfo, + @ApiParam(UserPayApi.insertWX.METHOD_API_CODE_URL) @RequestParam("codeUrl") String codeUrl, + @ApiParam(UserPayApi.insertWX.METHOD_API_PASS) @RequestParam("pass") String pass, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + userPayInfoService.insertWXorZFB(userId, UserPayConstants.WX, accountInfo, codeUrl, pass); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = UserPayApi.insertZFB.METHOD_TITLE_NAME, + notes = UserPayApi.insertZFB.METHOD_TITLE_NOTE) + @PostMapping("/insertZFB") + public ResultDTO insertZFB(@ApiParam(UserPayApi.insertZFB.METHOD_API_ACCOUNT_INFO) @RequestParam("accountInfo") String accountInfo, + @ApiParam(UserPayApi.insertZFB.METHOD_API_CODE_URL) @RequestParam("codeUrl") String codeUrl, + @ApiParam(UserPayApi.insertZFB.METHOD_API_PASS) @RequestParam("pass") String pass, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + userPayInfoService.insertWXorZFB(userId, UserPayConstants.ZFB, accountInfo, codeUrl, pass); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = UserPayApi.insertBank.METHOD_TITLE_NAME, + notes = UserPayApi.insertBank.METHOD_TITLE_NOTE) + @PostMapping("/insertBank") + public ResultDTO insertBank(@ApiParam(UserPayApi.insertBank.METHOD_API_BANK_NUMBER) @RequestParam("bankNumber") String bankNumber, + @ApiParam(UserPayApi.insertBank.METHOD_API_BANK_USER_NAME) @RequestParam("bankUserName") String bankUserName, + @ApiParam(UserPayApi.insertBank.METHOD_API_BANK_TYPE) @RequestParam("bankType") String bankType, + @ApiParam(UserPayApi.insertBank.METHOD_API_PASS) @RequestParam("pass") String pass, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + userPayInfoService.insertBank(userId, bankNumber, bankUserName, bankType, pass); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = UserPayApi.updateWX.METHOD_TITLE_NAME, + notes = UserPayApi.updateWX.METHOD_TITLE_NOTE) + @PostMapping("/updateWX") + public ResultDTO updateWX(@ApiParam(UserPayApi.updateWX.METHOD_API_ACCOUNT_INFO) @RequestParam("accountInfo") String accountInfo, + @ApiParam(UserPayApi.updateWX.METHOD_API_CODE_URL) @RequestParam("codeUrl") String codeUrl, + @ApiParam(UserPayApi.updateWX.METHOD_API_PASS) @RequestParam("pass") String pass, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + userPayInfoService.updateWXorZFB(userId, UserPayConstants.WX, accountInfo, codeUrl, pass); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = UserPayApi.updateZFB.METHOD_TITLE_NAME, + notes = UserPayApi.updateZFB.METHOD_TITLE_NOTE) + @PostMapping("/updateZFB") + public ResultDTO updateZFB(@ApiParam(UserPayApi.updateZFB.METHOD_API_ACCOUNT_INFO) @RequestParam("accountInfo") String accountInfo, + @ApiParam(UserPayApi.updateZFB.METHOD_API_CODE_URL) @RequestParam("codeUrl") String codeUrl, + @ApiParam(UserPayApi.updateZFB.METHOD_API_PASS) @RequestParam("pass") String pass, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + userPayInfoService.updateWXorZFB(userId, UserPayConstants.ZFB, accountInfo, codeUrl, pass); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = UserPayApi.updateBank.METHOD_TITLE_NAME, + notes = UserPayApi.updateBank.METHOD_TITLE_NOTE) + @PostMapping("/updateBank") + public ResultDTO updateBank(@ApiParam(UserPayApi.updateBank.METHOD_API_BANK_NUMBER) @RequestParam("bankNumber") String bankNumber, + @ApiParam(UserPayApi.updateBank.METHOD_API_BANK_USER_NAME) @RequestParam("bankUserName") String bankUserName, + @ApiParam(UserPayApi.updateBank.METHOD_API_BANK_TYPE) @RequestParam("bankType") String bankType, + @ApiParam(UserPayApi.updateBank.METHOD_API_PASS) @RequestParam("pass") String pass, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + userPayInfoService.updateBank(userId, bankNumber, bankUserName, bankType, pass); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = UserPayApi.uploadWX.METHOD_TITLE_NAME, + notes = UserPayApi.uploadWX.METHOD_TITLE_NOTE) + @PostMapping("/uploadWX") + public ResultDTO uploadWX(@ApiParam(UserPayApi.uploadWX.METHOD_API_PAY_FILE) @RequestParam("payFile") MultipartFile file, + HttpServletRequest request) { + //认证用户是否有登录 + SSOHelper.getUserId(redisTemplate, request); + //保存图片,返回保存图片的路径 + String filePath = saveFile(file, UserPayConstants.WX_URL); + return ResultDTO.requstSuccess(filePath); + } + + @ApiOperation(value = UserPayApi.uploadZFB.METHOD_TITLE_NAME, + notes = UserPayApi.uploadZFB.METHOD_TITLE_NOTE) + @PostMapping("/uploadZFB") + public ResultDTO uploadZFB(@ApiParam(UserPayApi.uploadZFB.METHOD_API_PAY_FILE) @RequestParam("payFile") MultipartFile file, + HttpServletRequest request) { + //认证用户是否有登录 + SSOHelper.getUserId(redisTemplate, request); + //保存图片,返回保存图片的路径 + String filePath = saveFile(file, UserPayConstants.ZFB_URL); + return ResultDTO.requstSuccess(filePath); + } + + /** + * 保存文件 + * + * @param file 文件 + * @param relativeDir 文件相对目录 + * @return 文件相对路径 + * @throws IOException + */ + private String saveFile(MultipartFile file, String relativeDir) { + try { + return saveFile(file, FILE_ROOT_PATH, relativeDir); + } catch (IOException e) { + e.printStackTrace(); + throw new OtcException(OtcEnums.UPLOAD_ERROR); + } + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/AdApi.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/AdApi.java new file mode 100644 index 0000000..00cd880 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/AdApi.java @@ -0,0 +1,81 @@ +package com.blockchain.server.otc.controller.api; + +public class AdApi { + public static final String AD_API = "广告控制器"; + public static final String METHOD_API_PAGE_NUM = "页码"; + public static final String METHOD_API_PAGE_SIZE = "页数"; + public static final String METHOD_API_COIN_NAME = "基本货币"; + public static final String METHOD_API_UNIT_NAME = "二级货币"; + public static final String METHOD_API_BEGIN_TIME = "开始时间"; + public static final String METHOD_API_END_TIME = "结束时间"; + + public static class publishBuyAd { + public static final String METHOD_TITLE_NAME = "发布买入广告"; + public static final String METHOD_TITLE_NOTE = "发布买入广告"; + public static final String METHOD_API_PARAM = "发布参数"; + } + + public static class publishSellAd { + public static final String METHOD_TITLE_NAME = "发布卖出广告"; + public static final String METHOD_TITLE_NOTE = "发布卖出广告"; + public static final String METHOD_API_PARAM = "发布参数"; + } + + public static class cancelAd { + public static final String METHOD_TITLE_NAME = "撤销广告"; + public static final String METHOD_TITLE_NOTE = "撤销广告"; + public static final String METHOD_API_ADID = "广告id"; + } + + public static class defaultAd { + public static final String METHOD_TITLE_NAME = "上架广告"; + public static final String METHOD_TITLE_NOTE = "上架广告"; + public static final String METHOD_API_ADID = "广告id"; + } + + public static class pendingAd { + public static final String METHOD_TITLE_NAME = "下架广告"; + public static final String METHOD_TITLE_NOTE = "下架广告"; + public static final String METHOD_API_ADID = "广告id"; + } + + public static class listBuyAd { + public static final String METHOD_TITLE_NAME = "查询买入广告(交易大厅)"; + public static final String METHOD_TITLE_NOTE = "查询买入广告(交易大厅)"; + } + + public static class listSellAd { + public static final String METHOD_TITLE_NAME = "查询卖出广告(交易大厅)"; + public static final String METHOD_TITLE_NOTE = "查询卖出广告(交易大厅)"; + } + + public static class listUserAd { + public static final String METHOD_TITLE_NAME = "查询用户发布的广告"; + public static final String METHOD_TITLE_NOTE = "查询用户发布的广告"; + public static final String METHOD_API_AD_TYPE = "广告类型"; + public static final String METHOD_API_AD_STATUS = "广告类型"; + } + + public static class selectById { + public static final String METHOD_TITLE_NAME = "根据id查询广告"; + public static final String METHOD_TITLE_NOTE = "根据id查询广告"; + public static final String METHOD_API_AD_ID = "广告ID"; + } + + public static class pcListBuyAd { + public static final String METHOD_TITLE_NAME = "pc端查询买入广告(交易大厅)"; + public static final String METHOD_TITLE_NOTE = "pc端查询买入广告(交易大厅)"; + } + + public static class pcListSellAd { + public static final String METHOD_TITLE_NAME = "pc端查询卖出广告(交易大厅)"; + public static final String METHOD_TITLE_NOTE = "pc端查询卖出广告(交易大厅)"; + } + + public static class pcListUserAd { + public static final String METHOD_TITLE_NAME = "pc端查询用户发布的广告"; + public static final String METHOD_TITLE_NOTE = "pc端查询用户发布的广告"; + public static final String METHOD_API_AD_TYPE = "广告类型"; + public static final String METHOD_API_AD_STATUS = "广告类型"; + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/AppealApi.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/AppealApi.java new file mode 100644 index 0000000..5ca3ddd --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/AppealApi.java @@ -0,0 +1,25 @@ +package com.blockchain.server.otc.controller.api; + +public class AppealApi { + public static final String APPEAL_API = "申诉控制器"; + + public static class handleAppeal { + public static final String METHOD_TITLE_NAME = "申诉"; + public static final String METHOD_TITLE_NOTE = "申诉"; + public static final String METHOD_API_ORDER_ID = "订单id"; + public static final String METHOD_API_URLS = "上传图片"; + public static final String METHOD_API_REMARK = "申诉说明"; + } + + public static class uploadAppealFile { + public static final String METHOD_TITLE_NAME = "上传申诉图片"; + public static final String METHOD_TITLE_NOTE = "上传申诉图片"; + public static final String METHOD_API_APPEAL_FILE = "申诉图片文件"; + } + + public static class selectAppealHandleLog { + public static final String METHOD_TITLE_NAME = "查询申诉处理日志"; + public static final String METHOD_TITLE_NOTE = "查询申诉处理日志"; + public static final String METHOD_API_ORDER_NUMBER = "申诉的订单流水"; + } +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/CoinApi.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/CoinApi.java new file mode 100644 index 0000000..3658563 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/CoinApi.java @@ -0,0 +1,15 @@ +package com.blockchain.server.otc.controller.api; + +public class CoinApi { + public static final String COIN_API = "币种控制器"; + + public static class listCoin { + public static final String METHOD_TITLE_NAME = "查询可用币种列表"; + public static final String METHOD_TITLE_NOTE = "查询可用币种列表"; + } + + public static class listCoinServiceCharge { + public static final String METHOD_TITLE_NAME = "查询币种手续费列表"; + public static final String METHOD_TITLE_NOTE = "查询币种手续费列表"; + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/ConfigApi.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/ConfigApi.java new file mode 100644 index 0000000..b7e9f11 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/ConfigApi.java @@ -0,0 +1,20 @@ +package com.blockchain.server.otc.controller.api; + +public class ConfigApi { + public static final String CONFIG_API = "配置控制器"; + + public static class SelectAutoCancelInterval { + public static final String METHOD_TITLE_NAME = "查询自动撤单间隔时间"; + public static final String METHOD_TITLE_NOTE = "查询自动撤单间隔时间"; + } + + public class SelectMarketFreezeCoin { + public static final String METHOD_TITLE_NOTE = "查询市商保证金代币"; + public static final String METHOD_TITLE_NAME = "查询市商保证金代币"; + } + + public class SelectMarketFreezeAmount { + public static final String METHOD_TITLE_NAME = "查询市商保证金数量"; + public static final String METHOD_TITLE_NOTE = "查询市商保证金数量"; + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/MarketApplyApi.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/MarketApplyApi.java new file mode 100644 index 0000000..8c0b620 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/MarketApplyApi.java @@ -0,0 +1,15 @@ +package com.blockchain.server.otc.controller.api; + +public class MarketApplyApi { + public static final String MARKET_APPLY_API = "市商申请控制器"; + + public static class CancelApply { + public static final String METHOD_TITLE_NAME = "申请取消市商"; + public static final String METHOD_TITLE_NOTE = "申请取消市商"; + } + + public static class MarketApply { + public static final String METHOD_TITLE_NAME = "申请市商"; + public static final String METHOD_TITLE_NOTE = "申请市商"; + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/MarketUserApi.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/MarketUserApi.java new file mode 100644 index 0000000..9189855 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/MarketUserApi.java @@ -0,0 +1,10 @@ +package com.blockchain.server.otc.controller.api; + +public class MarketUserApi { + public static final String MARKET_USER_API = "市商用户控制器"; + + public static class GetStatusByUserId { + public static final String METHOD_TITLE_NAME = "根据用户查询市商状态"; + public static final String METHOD_TITLE_NOTE = "根据用户查询市商状态"; + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/OrderApi.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/OrderApi.java new file mode 100644 index 0000000..fc924ef --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/OrderApi.java @@ -0,0 +1,81 @@ +package com.blockchain.server.otc.controller.api; + +public class OrderApi { + public static final String ORDER_API = "订单控制器"; + public static final String METHOD_API_PAGE_NUM = "页码"; + public static final String METHOD_API_PAGE_SIZE = "页数"; + public static final String METHOD_API_COIN_NAME = "基本货币"; + public static final String METHOD_API_UNIT_NAME = "二级货币"; + public static final String METHOD_API_BEGIN_TIME = "开始时间"; + public static final String METHOD_API_END_TIME = "结束时间"; + + public static class buyOrder { + public static final String METHOD_TITLE_NAME = "买入"; + public static final String METHOD_TITLE_NOTE = "买入"; + public static final String METHOD_TITLE_AD_ID = "广告id"; + public static final String METHOD_TITLE_AMOUNT = "下单数量"; + public static final String METHOD_TITLE_PRICE = "下单单价"; + public static final String METHOD_TITLE_TURNOVER = "下单交易额"; + } + + public static class sellOrder { + public static final String METHOD_TITLE_NAME = "卖出"; + public static final String METHOD_TITLE_NOTE = "卖出"; + public static final String METHOD_TITLE_AD_ID = "广告id"; + public static final String METHOD_TITLE_AMOUNT = "下单数量"; + public static final String METHOD_TITLE_PRICE = "下单单价"; + public static final String METHOD_TITLE_PASS = "支付密码"; + public static final String METHOD_TITLE_TURNOVER = "下单交易额"; + } + + public static class cancelBuyOrder { + public static final String METHOD_TITLE_NAME = "撤销买入订单"; + public static final String METHOD_TITLE_NOTE = "撤销买入订单"; + public static final String METHOD_TITLE_ORDER_ID = "订单id"; + } + + public static class pay { + public static final String METHOD_TITLE_NAME = "确认支付"; + public static final String METHOD_TITLE_NOTE = "确认支付"; + public static final String METHOD_TITLE_ORDER_ID = "订单id"; + public static final String METHOD_TITLE_PAY_TYPE = "支付类型"; + } + + public static class receipt { + public static final String METHOD_TITLE_NAME = "确认收款"; + public static final String METHOD_TITLE_NOTE = "确认收款"; + public static final String METHOD_TITLE_ORDER_ID = "订单id"; + public static final String METHOD_TITLE_PASS = "支付密码"; + } + + public static class listUserOrder { + public static final String METHOD_TITLE_NAME = "查询用户订单"; + public static final String METHOD_TITLE_NOTE = "查询用户订单"; + public static final String METHOD_TITLE_COIN_NAME = "基本货币"; + public static final String METHOD_TITLE_UNIT_NAME = "二级货币"; + public static final String METHOD_TITLE_ORDER_TYPE = "订单类型"; + public static final String METHOD_TITLE_ORDER_STATUS = "订单状态"; + public static final String METHOD_TITLE_PAY_TYPE = "支付类型"; + public static final String METHOD_TITLE_PAGE_NUM = "页码"; + public static final String METHOD_TITLE_PAGE_SIZE = "每页条数"; + } + + public static class selectByUserIdAndId { + public static final String METHOD_TITLE_NAME = "根据用户id和订单id查询订单"; + public static final String METHOD_TITLE_NOTE = "根据用户id和订单id查询订单"; + public static final String METHOD_TITLE_ORDER_ID = "订单id"; + } + + public static class pcListUserOrder { + public static final String METHOD_TITLE_NAME = "查询用户订单"; + public static final String METHOD_TITLE_NOTE = "查询用户订单"; + public static final String METHOD_TITLE_COIN_NAME = "基本货币"; + public static final String METHOD_TITLE_UNIT_NAME = "二级货币"; + public static final String METHOD_TITLE_ORDER_TYPE = "订单类型"; + public static final String METHOD_TITLE_ORDER_STATUS = "订单状态"; + public static final String METHOD_TITLE_PAY_TYPE = "支付类型"; + public static final String METHOD_TITLE_PAGE_NUM = "页码"; + public static final String METHOD_TITLE_PAGE_SIZE = "每页条数"; + } + +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/UserPayApi.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/UserPayApi.java new file mode 100644 index 0000000..68e89c2 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/controller/api/UserPayApi.java @@ -0,0 +1,84 @@ +package com.blockchain.server.otc.controller.api; + +public class UserPayApi { + public static final String USER_PAY_API = "用户支付信息控制器"; + + public static class listUserPay { + public static final String METHOD_TITLE_NAME = "查询用户支付信息"; + public static final String METHOD_TITLE_NOTE = "查询用户支付信息"; + } + + public static class selectByAdPayType { + public static final String METHOD_TITLE_NAME = "根据广告支付类型查询用户支付信息"; + public static final String METHOD_TITLE_NOTE = "根据订单id查询广告,然后获取广告支付类型查询订单的卖家支付信息"; + public static final String METHOD_API_ORDER_ID = "订单id"; + } + + public static class selectByPayType { + public static final String METHOD_TITLE_NAME = "根据支付类型查询用户支付信息"; + public static final String METHOD_TITLE_NOTE = "根据支付类型查询用户支付信息"; + public static final String METHOD_API_PAP_TYPE = "支付类型"; + } + + public static class insertWX { + public static final String METHOD_TITLE_NAME = "新增微信支付信息"; + public static final String METHOD_TITLE_NOTE = "新增微信支付信息"; + public static final String METHOD_API_ACCOUNT_INFO = "账号"; + public static final String METHOD_API_CODE_URL = "图片路径"; + public static final String METHOD_API_PASS = "资金密码"; + } + + public static class insertZFB { + public static final String METHOD_TITLE_NAME = "新增支付宝支付信息"; + public static final String METHOD_TITLE_NOTE = "新增支付宝支付信息"; + public static final String METHOD_API_ACCOUNT_INFO = "账号"; + public static final String METHOD_API_CODE_URL = "图片路径"; + public static final String METHOD_API_PASS = "资金密码"; + } + + public static class insertBank { + public static final String METHOD_TITLE_NAME = "新增银行卡支付信息"; + public static final String METHOD_TITLE_NOTE = "新增银行卡支付信息"; + public static final String METHOD_API_BANK_NUMBER = "银行卡号"; + public static final String METHOD_API_BANK_USER_NAME = "持卡人姓名"; + public static final String METHOD_API_BANK_TYPE = "开户银行"; + public static final String METHOD_API_PASS = "资金密码"; + } + + public static class updateWX { + public static final String METHOD_TITLE_NAME = "更新微信支付信息"; + public static final String METHOD_TITLE_NOTE = "更新微信支付信息"; + public static final String METHOD_API_ACCOUNT_INFO = "账号"; + public static final String METHOD_API_CODE_URL = "图片路径"; + public static final String METHOD_API_PASS = "资金密码"; + } + + public static class updateZFB { + public static final String METHOD_TITLE_NAME = "更新支付宝支付信息"; + public static final String METHOD_TITLE_NOTE = "更新支付宝支付信息"; + public static final String METHOD_API_ACCOUNT_INFO = "账号"; + public static final String METHOD_API_CODE_URL = "图片路径"; + public static final String METHOD_API_PASS = "资金密码"; + } + + public static class updateBank { + public static final String METHOD_TITLE_NAME = "更新银行卡支付信息"; + public static final String METHOD_TITLE_NOTE = "更新银行卡支付信息"; + public static final String METHOD_API_BANK_NUMBER = "银行卡号"; + public static final String METHOD_API_BANK_USER_NAME = "持卡人姓名"; + public static final String METHOD_API_BANK_TYPE = "开户银行"; + public static final String METHOD_API_PASS = "资金密码"; + } + + public static class uploadWX { + public static final String METHOD_TITLE_NAME = "上传微信支付信息"; + public static final String METHOD_TITLE_NOTE = "上传微信支付信息"; + public static final String METHOD_API_PAY_FILE = "上传的文件"; + } + + public static class uploadZFB { + public static final String METHOD_TITLE_NAME = "上传微信支付信息"; + public static final String METHOD_TITLE_NOTE = "上传微信支付信息"; + public static final String METHOD_API_PAY_FILE = "上传的文件"; + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/ad/ListAdDTO.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/ad/ListAdDTO.java new file mode 100644 index 0000000..d34fde9 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/ad/ListAdDTO.java @@ -0,0 +1,31 @@ +package com.blockchain.server.otc.dto.ad; + +import com.blockchain.common.base.dto.BaseDTO; +import lombok.Data; +import lombok.ToString; + +import java.math.BigDecimal; + +@Data +public class ListAdDTO extends BaseDTO { + private String id; + private String adNumber; + private String userId; + private String username; + private String nickname; + private String userImg; + private Integer adTransNum; + private Integer adMarkNum; + private BigDecimal price; + private BigDecimal totalNum; + private BigDecimal lastNum; + private String coinName; + private String unitName; + private BigDecimal minLimit; + private BigDecimal maxLimit; + private String adPay; + private String adType; + private String adRemark; + private java.util.Date createTime; + private java.util.Date modifyTime; +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/ad/ListUserAdDTO.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/ad/ListUserAdDTO.java new file mode 100644 index 0000000..b3b2a9c --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/ad/ListUserAdDTO.java @@ -0,0 +1,27 @@ +package com.blockchain.server.otc.dto.ad; + +import com.blockchain.common.base.dto.BaseDTO; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ListUserAdDTO extends BaseDTO { + private String id; + private String adNumber; + private String userId; + private BigDecimal price; + private BigDecimal totalNum; + private BigDecimal lastNum; + private String coinName; + private String unitName; + private BigDecimal minLimit; + private BigDecimal maxLimit; + private String adPay; + private String adType; + private String adStatus; + private String adRemark; + private java.util.Date createTime; + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/ad/PublishAdParamDTO.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/ad/PublishAdParamDTO.java new file mode 100644 index 0000000..797e9ae --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/ad/PublishAdParamDTO.java @@ -0,0 +1,20 @@ +package com.blockchain.server.otc.dto.ad; + +import com.blockchain.common.base.dto.BaseDTO; +import com.blockchain.common.base.entity.BaseModel; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class PublishAdParamDTO extends BaseDTO { + private String pass; + private String userId; + private BigDecimal price; + private BigDecimal totalNum; + private String coinName; + private String unitName; + private BigDecimal minLimit; + private String[] payType; + private String remark; +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/appeal/AppealHandleLogDTO.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/appeal/AppealHandleLogDTO.java new file mode 100644 index 0000000..bb9c036 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/appeal/AppealHandleLogDTO.java @@ -0,0 +1,11 @@ +package com.blockchain.server.otc.dto.appeal; + +import lombok.Data; +import lombok.ToString; + +@Data +public class AppealHandleLogDTO { + private String afterStatus; + private String remark; + private java.util.Date createTime; +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/coin/CoinDTO.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/coin/CoinDTO.java new file mode 100644 index 0000000..00ede71 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/coin/CoinDTO.java @@ -0,0 +1,11 @@ +package com.blockchain.server.otc.dto.coin; + +import lombok.Data; + +@Data +public class CoinDTO { + private String coinName; + private String unitName; + private Integer coinDecimal; + private Integer unitDecimal; +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/coin/CoinServiceChargeDTO.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/coin/CoinServiceChargeDTO.java new file mode 100644 index 0000000..12f5442 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/coin/CoinServiceChargeDTO.java @@ -0,0 +1,13 @@ +package com.blockchain.server.otc.dto.coin; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class CoinServiceChargeDTO { + private String coinName; + private String unitName; + private BigDecimal coinServiceCharge; + private String rank; +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/config/ConfigDTO.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/config/ConfigDTO.java new file mode 100644 index 0000000..b502775 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/config/ConfigDTO.java @@ -0,0 +1,11 @@ +package com.blockchain.server.otc.dto.config; + +import lombok.Data; + +@Data +public class ConfigDTO { + private String dataKey; + private String dataValue; + private String status; + private java.util.Date modifyTime; +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/jgim/ImjgMessage.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/jgim/ImjgMessage.java new file mode 100644 index 0000000..fe5ccfe --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/jgim/ImjgMessage.java @@ -0,0 +1,54 @@ +package com.blockchain.server.otc.dto.jgim; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +@Table(name = "imjg_message") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ImjgMessage { + + @Id + @Column(name = "id") + private int id; + @Column(name = "extras") + private String extras; + @Column(name = "msg_type") + private String msgType; + @Column(name = "text") + private String text; + + @Column(name = "media_id") + private String mediaId; + @Column(name = "media_crc32") + private String mediaCrc32; + @Column(name = "duration") + private int duration; + @Column(name = "format") + private String format; + + @Column(name = "fsize") + private int fsize; + @Column(name = "width") + private int width; + @Column(name = "height") + private int height; + @Column(name = "fname") + private String fname; + +/* @Column(name = "latitude") + private int latitude; + @Column(name = "longitude") + private int longitude; + @Column(name = "scale") + private int scale; + @Column(name = "lable") + private String lable;*/ +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/jgim/JgMassageParam.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/jgim/JgMassageParam.java new file mode 100644 index 0000000..10f1b9c --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/jgim/JgMassageParam.java @@ -0,0 +1,77 @@ +package com.blockchain.server.otc.dto.jgim; + +import com.blockchain.common.base.dto.BaseDTO; +import lombok.Data; +import lombok.Getter; + +/** + * @author :zz + * @date :Created in 2019/4/25 15:11 + * @description:极光消息推送 + * @modified By: + * @version: 1.0$ + */ +@Data +public class JgMassageParam extends BaseDTO { + + /** + * 服务模块名 + **/ + public String pType; + /** + * 业务ID + **/ + public String dataId; + /** + * 发消息类型 + * text - 文本, + * image - 图片, + * custom - 自定义消息(msg_body为json对象即可,服务端不做校验) + * voice - 语音 (必填) + **/ + public String msgType; + /** + * 发送方用户ID + **/ + public String fuserId; + /** + * 接收方用户ID + **/ + public String tuserId; + /** + * 消息DTO类的JSON串 + **/ + public String msgBodyJson; + /** + * "节点消息标志状态:" + + * // "0:默认,不是节点消息;1:双方可见的节点消息;2:接收方可见的节点消息" + **/ + public int nodeCue; + + + public enum MsgTypeEnums { + + text("text", "文本", null), + image("image", "图片", null), + custom("custom", "自定义消息", null), + voice("voice", "语音", null); + + @Getter + private String code; + @Getter + private String name;//值 int类型,默认0 + @Getter + private String value;//值 string类型 + + MsgTypeEnums(String code, String name, String value) { + this.code = code; + this.name = name; + this.value = value; + } + + public String code() { + return code; + } + } + +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/jgim/JgMessageBodyDTO.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/jgim/JgMessageBodyDTO.java new file mode 100644 index 0000000..81b6c12 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/jgim/JgMessageBodyDTO.java @@ -0,0 +1,63 @@ +package com.blockchain.server.otc.dto.jgim; + +import com.blockchain.common.base.dto.BaseDTO; +import lombok.Data; + +@Data +public class JgMessageBodyDTO extends BaseDTO { + + private int id; + + private String extras; + + private String msg_type; + + private String media_id; + + private String media_crc32; + + private int duration; + + private String format; + + private int fsize; + + private int width; + + private int height; + + private String fname; + + private String text; + + + public JgMessageBodyDTO(){} + + public JgMessageBodyDTO(String text){ + this.text = text; + } + + public ImjgMessage toImjgMessage(){ + ImjgMessage imjgMessage = new ImjgMessage(); + imjgMessage.setMsgType(this.msg_type); + imjgMessage.setDuration(this.duration); + imjgMessage.setExtras(this.extras); + imjgMessage.setFname(this.fname); + imjgMessage.setText(this.text); + + imjgMessage.setFormat(this.format); + imjgMessage.setFsize(this.fsize); + imjgMessage.setWidth(this.width); + imjgMessage.setHeight(this.height); + + imjgMessage.setMediaId(this.media_id); + imjgMessage.setMediaCrc32(this.media_crc32); + return imjgMessage; + } + + //private int id; + + + + +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/order/OrderDTO.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/order/OrderDTO.java new file mode 100644 index 0000000..79b2090 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/order/OrderDTO.java @@ -0,0 +1,34 @@ +package com.blockchain.server.otc.dto.order; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class OrderDTO { + private String id; + private String orderNumber; + private String adId; + private String coinName; + private String unitName; + private String buyUserId; + private String buyStatus; + private String buyUserName; + private String buyNickName; + private String buyAvatar; + private String sellUserId; + private String sellStatus; + private String sellUserName; + private String sellNickName; + private String sellAvatar; + private BigDecimal amount; + private BigDecimal price; + private BigDecimal turnover; + private BigDecimal chargeRatio; + private String orderType; + private String orderStatus; + private String orderPayType; + private String role; + private java.util.Date createTime; + private java.util.Date modifyTime; +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/user/UserBaseDTO.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/user/UserBaseDTO.java new file mode 100644 index 0000000..7ed35db --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/dto/user/UserBaseDTO.java @@ -0,0 +1,24 @@ +package com.blockchain.server.otc.dto.user; + +import lombok.Data; + +/** + * 用户基本信息 + */ +@Data +public class UserBaseDTO { + private String id;//用户id + private String mobilePhone;//手机号 + private String nickName;//昵称 + private String avatar;//头像 + private String email;//email + private String lowAuth;//低级认证 + private String highAuth;//高级认证 + private String grade;//用户等级 + + + private boolean hasPassword;//是否设置了登录密码 + private String token;//token信息 + private String invitationCode;//邀请码 + private boolean bindGoogleAuth;//谷歌验证器 +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Ad.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Ad.java new file mode 100644 index 0000000..c40f614 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Ad.java @@ -0,0 +1,60 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * ad 数据传输类 + * + * @version 1.0 + * @date 2019-04-15 10:37:31 + */ +@Table(name = "otc_ad") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Ad extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "ad_number") + private String adNumber; + @Column(name = "user_id") + private String userId; + @Column(name = "price") + private BigDecimal price; + @Column(name = "total_num") + private BigDecimal totalNum; + @Column(name = "last_num") + private BigDecimal lastNum; + @Column(name = "coin_name") + private String coinName; + @Column(name = "unit_name") + private String unitName; + @Column(name = "min_limit") + private BigDecimal minLimit; + @Column(name = "max_limit") + private BigDecimal maxLimit; + @Column(name = "charge_ratio") + private BigDecimal chargeRatio; + @Column(name = "ad_pay") + private String adPay; + @Column(name = "ad_type") + private String adType; + @Column(name = "ad_status") + private String adStatus; + @Column(name = "ad_remark") + private String adRemark; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Appeal.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Appeal.java new file mode 100644 index 0000000..787babc --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Appeal.java @@ -0,0 +1,35 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * Appeal 数据传输类 + * + * @version 1.0 + * @date 2019-04-18 15:54:26 + */ +@Table(name = "otc_appeal") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Appeal extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "order_number") + private String orderNumber; + @Column(name = "status") + private String status; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/AppealDetail.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/AppealDetail.java new file mode 100644 index 0000000..9f8ec96 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/AppealDetail.java @@ -0,0 +1,37 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * AppealDetail 数据传输类 + * + * @version 1.0 + * @date 2019-04-18 15:54:26 + */ +@Table(name = "otc_appeal_detail") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AppealDetail extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "appeal_id") + private String appealId; + @Column(name = "user_id") + private String userId; + @Column(name = "appeal_role") + private String appealRole; + @Column(name = "remark") + private String remark; + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/AppealHandleLog.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/AppealHandleLog.java new file mode 100644 index 0000000..55481ae --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/AppealHandleLog.java @@ -0,0 +1,39 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * AppealHandleLog 数据传输类 + * + * @version 1.0 + * @date 2019-04-18 15:54:26 + */ +@Table(name = "otc_appeal_handle_log") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AppealHandleLog extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "order_number") + private String orderNubmer; + @Column(name = "sys_user_id") + private String sysUserId; + @Column(name = "ip_address") + private String ipAddress; + @Column(name = "after_status") + private String afterStatus; + @Column(name = "remark") + private String remark; + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/AppealImg.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/AppealImg.java new file mode 100644 index 0000000..dd015e0 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/AppealImg.java @@ -0,0 +1,33 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * AppealImg 数据传输类 + * + * @version 1.0 + * @date 2019-04-19 17:14:29 + */ +@Table(name = "otc_appeal_img") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AppealImg extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "appeal_detail_id") + private String appealDetailId; + @Column(name = "appeal_img_url") + private String appealImgUrl; + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Bill.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Bill.java new file mode 100644 index 0000000..f248ae7 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Bill.java @@ -0,0 +1,42 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * Bill 数据传输类 + * + * @version 1.0 + * @date 2019-04-15 10:37:31 + */ +@Table(name = "otc_bill") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Bill extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "record_number") + private String recordNumber; + @Column(name = "user_id") + private String userId; + @Column(name = "freezebalance") + private BigDecimal freezebalance; + @Column(name = "freebalance") + private BigDecimal freebalance; + @Column(name = "bill_type") + private String billType; + @Column(name = "coin_name") + private String coinName; + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Coin.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Coin.java new file mode 100644 index 0000000..b4af47b --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Coin.java @@ -0,0 +1,48 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * Coin 数据传输类 + * + * @version 1.0 + * @date 2019-04-15 10:37:31 + */ +@Table(name = "otc_coin") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Coin extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "coin_name") + private String coinName; + @Column(name = "unit_name") + private String unitName; + @Column(name = "coin_net") + private String coinNet; + @Column(name = "coin_decimal") + private Integer coinDecimal; + @Column(name = "unit_decimal") + private Integer unitDecimal; + @Column(name = "coin_service_charge") + private BigDecimal coinServiceCharge; + @Column(name = "status") + private String status; + @Column(name = "rank") + private String rank; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Config.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Config.java new file mode 100644 index 0000000..0e71aef --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Config.java @@ -0,0 +1,37 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * Config 数据传输类 + * + * @version 1.0 + * @date 2019-04-15 10:37:31 + */ +@Table(name = "otc_config") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Config extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "data_key") + private String dataKey; + @Column(name = "data_value") + private String dataValue; + @Column(name = "status") + private String status; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/DealStats.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/DealStats.java new file mode 100644 index 0000000..71e7b75 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/DealStats.java @@ -0,0 +1,39 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * DealStats 数据传输类 + * + * @version 1.0 + * @date 2019-04-15 10:37:31 + */ +@Table(name = "otc_deal_stats") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class DealStats extends BaseModel { + @Id + @Column(name = "user_id") + private String userId; + @Column(name = "ad_trans_num") + private Integer adTransNum; + @Column(name = "ad_mark_num") + private Integer adMarkNum; + @Column(name = "order_sell_num") + private Integer orderSellNum; + @Column(name = "order_buy_num") + private Integer orderBuyNum; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketApply.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketApply.java new file mode 100644 index 0000000..793a481 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketApply.java @@ -0,0 +1,42 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * MarketApply 数据传输类 + * + * @version 1.0 + * @date 2019-07-13 11:48:11 + */ +@Table(name = "otc_market_apply") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MarketApply extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "user_id") + private String userId; + @Column(name = "coin_name") + private String coinName; + @Column(name = "amount") + private BigDecimal amount; + @Column(name = "apply_type") + private String applyType; + @Column(name = "status") + private String status; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketApplyHandleLog.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketApplyHandleLog.java new file mode 100644 index 0000000..1f9c540 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketApplyHandleLog.java @@ -0,0 +1,38 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * MarketApplyHandleLog 数据传输类 + * @date 2019-07-13 11:48:11 + * @version 1.0 + */ +@Table(name = "otc_market_apply_handle_log") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MarketApplyHandleLog extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "market_apply_id") + private String marketApplyId; + @Column(name = "sys_user_id") + private String sysUserId; + @Column(name = "ip_address") + private String ipAddress; + @Column(name = "before_status") + private String beforeStatus; + @Column(name = "after_status") + private String afterStatus; + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketFreeze.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketFreeze.java new file mode 100644 index 0000000..25db91c --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketFreeze.java @@ -0,0 +1,37 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * MarketFreeze 数据传输类 + * + * @version 1.0 + * @date 2019-07-13 11:48:11 + */ +@Table(name = "otc_market_freeze") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MarketFreeze extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "user_id") + private String userId; + @Column(name = "market_apply_id") + private String marketApplyId; + @Column(name = "coin_name") + private String coinName; + @Column(name = "amount") + private BigDecimal amount; + @Column(name = "create_time") + private java.util.Date createTime; +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketUser.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketUser.java new file mode 100644 index 0000000..259a7ca --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketUser.java @@ -0,0 +1,34 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * MarketUser 数据传输类 + * @date 2019-07-13 11:48:11 + * @version 1.0 + */ +@Table(name = "otc_market_user") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MarketUser extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "user_id") + private String userId; + @Column(name = "status") + private String status; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketUserHandleLog.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketUserHandleLog.java new file mode 100644 index 0000000..7f3c45a --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/MarketUserHandleLog.java @@ -0,0 +1,38 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * MarketUserHandleLog 数据传输类 + * @date 2019-07-13 11:48:11 + * @version 1.0 + */ +@Table(name = "otc_market_user_handle_log") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MarketUserHandleLog extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "user_id") + private String userId; + @Column(name = "sys_user_id") + private String sysUserId; + @Column(name = "ip_address") + private String ipAddress; + @Column(name = "before_status") + private String beforeStatus; + @Column(name = "after_staus") + private String afterStatus; + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Order.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Order.java new file mode 100644 index 0000000..f2ffa46 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/Order.java @@ -0,0 +1,62 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * Order 数据传输类 + * + * @version 1.0 + * @date 2019-04-15 10:37:32 + */ +@Table(name = "otc_order") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Order extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "order_number") + private String orderNumber; + @Column(name = "ad_id") + private String adId; + @Column(name = "coin_name") + private String coinName; + @Column(name = "unit_name") + private String unitName; + @Column(name = "buy_user_id") + private String buyUserId; + @Column(name = "buy_status") + private String buyStatus; + @Column(name = "sell_user_id") + private String sellUserId; + @Column(name = "sell_status") + private String sellStatus; + @Column(name = "amount") + private BigDecimal amount; + @Column(name = "price") + private BigDecimal price; + @Column(name = "turnover") + private BigDecimal turnover; + @Column(name = "charge_ratio") + private BigDecimal chargeRatio; + @Column(name = "order_type") + private String orderType; + @Column(name = "order_status") + private String orderStatus; + @Column(name = "order_pay_type") + private String orderPayType; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/UserHandleLog.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/UserHandleLog.java new file mode 100644 index 0000000..f10eb11 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/UserHandleLog.java @@ -0,0 +1,37 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * UserHandleLog 数据传输类 + * + * @version 1.0 + * @date 2019-04-15 10:37:32 + */ +@Table(name = "otc_user_handle_log") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UserHandleLog extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "user_id") + private String userId; + @Column(name = "handle_number") + private String handleNumber; + @Column(name = "handle_type") + private String handleType; + @Column(name = "handle_number_type") + private String handleNumberType; + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/UserPayInfo.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/UserPayInfo.java new file mode 100644 index 0000000..51602d4 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/entity/UserPayInfo.java @@ -0,0 +1,45 @@ +package com.blockchain.server.otc.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * UserPayInfo 数据传输类 + * + * @version 1.0 + * @date 2019-04-15 14:35:49 + */ +@Table(name = "otc_user_pay_info") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UserPayInfo extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "user_id") + private String userId; + @Column(name = "pay_type") + private String payType; + @Column(name = "account_info") + private String accountInfo; + @Column(name = "collection_code_url") + private String collectionCodeUrl; + @Column(name = "bank_number") + private String bankNumber; + @Column(name = "bank_user_name") + private String bankUserName; + @Column(name = "bank_type") + private String bankType; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/BTCFeign.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/BTCFeign.java new file mode 100644 index 0000000..dc13e1f --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/BTCFeign.java @@ -0,0 +1,30 @@ +package com.blockchain.server.otc.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@FeignClient("dapp-btc-server") +public interface BTCFeign { + //请求路径 + String CONTENT_PATH = "/inner/walletTx"; + + /*** + * 冻结余额 + * @param orderDTO + * @return + */ + @PostMapping(CONTENT_PATH + "/order") + ResultDTO order(@RequestBody WalletOrderDTO orderDTO); + + /*** + * 扣减、增加总余额 + * @param changeDTO + * @return + */ + @PostMapping(CONTENT_PATH + "/change") + ResultDTO change(@RequestBody WalletChangeDTO changeDTO); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/EOSFeign.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/EOSFeign.java new file mode 100644 index 0000000..f34e8b1 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/EOSFeign.java @@ -0,0 +1,30 @@ +package com.blockchain.server.otc.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@FeignClient("dapp-eos-server") +public interface EOSFeign { + //请求路径 + String CONTENT_PATH = "/inner/walletTx"; + + /*** + * 冻结余额 + * @param orderDTO + * @return + */ + @PostMapping(CONTENT_PATH + "/order") + ResultDTO order(@RequestBody WalletOrderDTO orderDTO); + + /*** + * 扣减、增加总余额 + * @param changeDTO + * @return + */ + @PostMapping(CONTENT_PATH + "/change") + ResultDTO change(@RequestBody WalletChangeDTO changeDTO); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/ETHFeign.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/ETHFeign.java new file mode 100644 index 0000000..0986651 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/ETHFeign.java @@ -0,0 +1,44 @@ +package com.blockchain.server.otc.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient("dapp-eth-server") +public interface ETHFeign { + //钱包公共接口请求路径 + String WALLET_PATH = "/inner/wallet"; + //钱包交易费用接口请求路径 + String TRANS_PATH = "/inner/walletTx"; + + /*** + * 冻结余额 + * @param orderDTO + * @return + */ + @PostMapping(TRANS_PATH + "/order") + ResultDTO order(@RequestBody WalletOrderDTO orderDTO); + + /*** + * 扣减、增加总余额 + * @param changeDTO + * @return + */ + @PostMapping(TRANS_PATH + "/change") + ResultDTO change(@RequestBody WalletChangeDTO changeDTO); + + /*** + * 校验密码 + * @param pass + * @return + */ + @GetMapping(WALLET_PATH + "/isPassword") + ResultDTO isPassword(@RequestParam(name = "password") String pass); + + +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/IMJGFeign.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/IMJGFeign.java new file mode 100644 index 0000000..b3e5807 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/IMJGFeign.java @@ -0,0 +1,34 @@ +package com.blockchain.server.otc.feign; + + +import com.blockchain.common.base.dto.ResultDTO; +import io.swagger.annotations.ApiParam; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient("dapp-imjg-server") +public interface IMJGFeign { + String CONTENT_PATH = "/inner"; + + /*** + * 发送单聊消息接口 + * 发送单聊信息并保存消息记录 + * @param pType 服务模块名 + * @param dataId 业务ID + * @param msgType 消息类型 + * @param fuserId 发送方用户ID + * @param tuserId 接收方用户ID + * @param msgBodyJson 消息DTO类的JSON串 + * @param nodeCue 节点消息标志状态: 0:默认,不是节点消息;1:双方可见的节点消息;2:接收方可见的节点消息 + * @return + */ + @PostMapping(CONTENT_PATH + "/sendSingleMsg") + ResultDTO sendSingleMsg(@RequestParam(value = "pType") String pType, + @RequestParam(value = "dataId") String dataId, + @RequestParam(value = "msgType") String msgType, + @RequestParam(value = "fuserId") String fuserId, + @RequestParam(value = "tuserId") String tuserId, + @RequestParam(value = "msgBodyJson") String msgBodyJson, + @RequestParam(value = "nodeCue") int nodeCue); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/PushFeign.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/PushFeign.java new file mode 100644 index 0000000..eebcef1 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/PushFeign.java @@ -0,0 +1,25 @@ +package com.blockchain.server.otc.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.Map; + +@FeignClient(name = "dapp-user-server", path = "/inner") +public interface PushFeign { + + /*** + * 对单个用户推送消息 + * @param userId 用户id + * @param pushType 推送通知信息类型 + * @param payload 透传数据(通知附带数据) + * @return + */ + @PostMapping("/push/pushToSingle") + ResultDTO pushToSingle(@RequestParam("/userId") String userId, + @RequestParam("/pushType") String pushType, + @RequestBody Map payload); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/UserFeign.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/UserFeign.java new file mode 100644 index 0000000..1e739c2 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/feign/UserFeign.java @@ -0,0 +1,49 @@ +package com.blockchain.server.otc.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.otc.dto.user.UserBaseDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient(name = "dapp-user-server", path = "/inner") +public interface UserFeign { + /*** + * 查询用户主体信息 + * @return + */ + @GetMapping("/selectUserInfoById") + ResultDTO selectUserInfoById(@RequestParam("userId") String userId); + + /*** + * 检查用户初级认证和黑名单 + * @return + */ + @GetMapping("/hasLowAuthAndUserList") + ResultDTO hasLowAuthAndUserList(@RequestParam("userId") String userId); + + /*** + * 检查用户高级认证和黑名单 + * @return + */ + @GetMapping("/hasHighAuthAndUserList") + ResultDTO hasHighAuthAndUserList(@RequestParam("userId") String userId); + + /*** + * 免交易手续费 + * @param userId + * @return + */ + @PostMapping("/verifyFreeTransaction") + ResultDTO verifyFreeTransaction(@RequestParam("userId") String userId); + + /*** + * 是否可以发布广告 + * true为可以 + * @param userId + * @return + */ + @PostMapping("/verifyFreePushAd") + ResultDTO verifyFreePushAd(@RequestParam("userId") String userId); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AdMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AdMapper.java new file mode 100644 index 0000000..720a7c0 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AdMapper.java @@ -0,0 +1,69 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.dto.ad.ListAdDTO; +import com.blockchain.server.otc.dto.ad.ListUserAdDTO; +import com.blockchain.server.otc.entity.Ad; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * AdMapper 数据访问类 + * + * @version 1.0 + * @date 2019-04-15 10:37:31 + */ +@Repository +public interface AdMapper extends Mapper { + + /*** + * 根据参数查询广告(用于交易大厅) + * @param userId + * @param adType + * @param coinName + * @param unitName + * @param adStatus + * @param priceSort 单价排序规则 + * @return + */ + List listAd(@Param("userId") String userId, @Param("adType") String adType, + @Param("coinName") String coinName, @Param("unitName") String unitName, + @Param("adStatus") String[] adStatus, @Param("priceSort") String priceSort, + @Param("beginTime") String beginTime, @Param("endTime") String endTime); + + /*** + * 根据参数查询用户发布的广告(用于用户广告列表) + * @param userId + * @param adType + * @param coinName + * @param unitName + * @param adStatus + * @return + */ + List listUserAd(@Param("userId") String userId, @Param("adType") String adType, + @Param("coinName") String coinName, @Param("unitName") String unitName, + @Param("adStatus") String adStatus, @Param("beginTime") String beginTime, + @Param("endTime") String endTime); + + /*** + * 根据多个状态和类型查询广告 + * @param userId + * @param adType + * @param coinName + * @param unitName + * @param adStatus + * @return + */ + List listUserAdByStatusAndType(@Param("userId") String userId, @Param("adType") String adType, + @Param("coinName") String coinName, @Param("unitName") String unitName, + @Param("adStatus") String[] adStatus); + + /*** + * 使用排他锁查询广告 + * @param adId + * @return + */ + Ad selectByIdForUpdate(@Param("adId") String adId); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealDetailMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealDetailMapper.java new file mode 100644 index 0000000..79268ef --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealDetailMapper.java @@ -0,0 +1,14 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.entity.AppealDetail; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * AppealDetailMapper 数据访问类 + * @date 2019-04-18 15:54:26 + * @version 1.0 + */ +@Repository +public interface AppealDetailMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealHandleLogMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealHandleLogMapper.java new file mode 100644 index 0000000..bb14ac2 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealHandleLogMapper.java @@ -0,0 +1,24 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.dto.appeal.AppealHandleLogDTO; +import com.blockchain.server.otc.entity.AppealHandleLog; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * AppealHandleLogMapper 数据访问类 + * + * @version 1.0 + * @date 2019-04-18 15:54:26 + */ +@Repository +public interface AppealHandleLogMapper extends Mapper { + + /*** + * 根据订单流水查询申诉处理日志 + * @param orderNumber + * @return + */ + AppealHandleLogDTO selectByOrderNumber(@Param("orderNumber") String orderNumber); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealImgMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealImgMapper.java new file mode 100644 index 0000000..d9a043f --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealImgMapper.java @@ -0,0 +1,15 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.entity.AppealImg; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * AppealImgMapper 数据访问类 + * + * @version 1.0 + * @date 2019-04-19 17:14:29 + */ +@Repository +public interface AppealImgMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealMapper.java new file mode 100644 index 0000000..cf8199c --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/AppealMapper.java @@ -0,0 +1,24 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.entity.Appeal; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * AppealMapper 数据访问类 + * + * @version 1.0 + * @date 2019-04-18 15:54:26 + */ +@Repository +public interface AppealMapper extends Mapper { + + /*** + * 根据订单流水号查询申诉信息 + * @param orderNumber + * @return + */ + Appeal selectByOrderNumber(@Param("orderNumber") String orderNumber); + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/BillMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/BillMapper.java new file mode 100644 index 0000000..00345ba --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/BillMapper.java @@ -0,0 +1,15 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.entity.Bill; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * BillMapper 数据访问类 + * + * @version 1.0 + * @date 2019-04-15 10:37:31 + */ +@Repository +public interface BillMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/CoinMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/CoinMapper.java new file mode 100644 index 0000000..d30a37b --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/CoinMapper.java @@ -0,0 +1,41 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.dto.coin.CoinDTO; +import com.blockchain.server.otc.dto.coin.CoinServiceChargeDTO; +import com.blockchain.server.otc.entity.Coin; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * CoinMapper 数据访问类 + * + * @version 1.0 + * @date 2019-04-15 10:37:31 + */ +@Repository +public interface CoinMapper extends Mapper { + + /*** + * 根据基本货币、二级货币查询币种信息 + * @param coinName + * @param unitName + * @return + */ + Coin selectByCoinAndUnit(@Param("coinName") String coinName, @Param("unitName") String unitName); + + /*** + * 根据状态查询币种列表 + * @param coinStatus + * @return + */ + List listCoin(@Param("status") String coinStatus); + + /*** + * 查询所有代币手续费费率 + * @return + */ + List listCoinServiceCharge(); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/ConfigMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/ConfigMapper.java new file mode 100644 index 0000000..b8f23ea --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/ConfigMapper.java @@ -0,0 +1,23 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.entity.Config; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * ConfigMapper 数据访问类 + * + * @version 1.0 + * @date 2019-04-15 10:37:31 + */ +@Repository +public interface ConfigMapper extends Mapper { + + /*** + * 根据key查询配置信息 + * @param key + * @return + */ + Config selectByKey(@Param("key") String key); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/DealStatsMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/DealStatsMapper.java new file mode 100644 index 0000000..78c0b7c --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/DealStatsMapper.java @@ -0,0 +1,45 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.entity.DealStats; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.Date; + +/** + * DealStatsMapper 数据访问类 + * + * @version 1.0 + * @date 2019-04-15 10:37:31 + */ +@Repository +public interface DealStatsMapper extends Mapper { + /*** + * 更新广告交易次数 + * @param userId + * @return + */ + int updateAdTransNum(@Param("userId") String userId, @Param("modifyTime") Date modifyTime); + + /*** + * 更新广告成交次数 + * @param userId + * @return + */ + int updateAdMarkNum(@Param("userId") String userId, @Param("modifyTime") Date modifyTime); + + /*** + * 更新卖单成交次数 + * @param userId + * @return + */ + int updateOrderSellNum(@Param("userId") String userId, @Param("modifyTime") Date modifyTime); + + /*** + * 更新买单成交次数 + * @param userId + * @return + */ + int updateOrderBuyNum(@Param("userId") String userId, @Param("modifyTime") Date modifyTime); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketApplyHandleLogMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketApplyHandleLogMapper.java new file mode 100644 index 0000000..c0cae6c --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketApplyHandleLogMapper.java @@ -0,0 +1,14 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.entity.MarketApplyHandleLog; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * MarketApplyHandleLogMapper 数据访问类 + * @date 2019-07-13 11:48:11 + * @version 1.0 + */ +@Repository +public interface MarketApplyHandleLogMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketApplyMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketApplyMapper.java new file mode 100644 index 0000000..0ca86d8 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketApplyMapper.java @@ -0,0 +1,26 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.entity.MarketApply; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * MarketApplyMapper 数据访问类 + * + * @version 1.0 + * @date 2019-07-13 11:48:11 + */ +@Repository +public interface MarketApplyMapper extends Mapper { + + /*** + * 根据用户id和申请类型 + * @param userId + * @param applyType + * @return + */ + MarketApply selectByUserIdAndApplyTypeAndStatus(@Param("userId") String userId, + @Param("applyType") String applyType, + @Param("applyStatus") String applyStatus); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketFreezeMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketFreezeMapper.java new file mode 100644 index 0000000..34aa0b6 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketFreezeMapper.java @@ -0,0 +1,23 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.entity.MarketFreeze; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * MarketFreezeMapper 数据访问类 + * + * @version 1.0 + * @date 2019-07-13 11:48:11 + */ +@Repository +public interface MarketFreezeMapper extends Mapper { + + /*** + * 根据用户id查询 + * @param userId + * @return + */ + MarketFreeze selectByUserId(@Param("userId") String userId); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketUserHandleLogMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketUserHandleLogMapper.java new file mode 100644 index 0000000..eae9242 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketUserHandleLogMapper.java @@ -0,0 +1,14 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.entity.MarketUserHandleLog; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * MarketUserHandleLogMapper 数据访问类 + * @date 2019-07-13 11:48:11 + * @version 1.0 + */ +@Repository +public interface MarketUserHandleLogMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketUserMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketUserMapper.java new file mode 100644 index 0000000..0f16c55 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/MarketUserMapper.java @@ -0,0 +1,23 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.entity.MarketUser; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * MarketUserMapper 数据访问类 + * + * @version 1.0 + * @date 2019-07-13 11:48:11 + */ +@Repository +public interface MarketUserMapper extends Mapper { + + /*** + * 根据用户id查询 + * @param userId + * @return + */ + MarketUser selectByUserId(@Param("userId") String userId); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/OrderMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/OrderMapper.java new file mode 100644 index 0000000..3637090 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/OrderMapper.java @@ -0,0 +1,91 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.dto.order.OrderDTO; +import com.blockchain.server.otc.entity.Order; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.Date; +import java.util.List; + +/** + * OrderMapper 数据访问类 + * + * @version 1.0 + * @date 2019-04-15 10:37:32 + */ +@Repository +public interface OrderMapper extends Mapper { + + /*** + * 根据广告id和单个或多个状态查询订单 + * @param adId + * @param status + * @return + */ + List selectByAdIdAndStatus(@Param("adId") String adId, @Param("status") String[] status); + + /*** + * 根据参数查询用户订单 + * @param userId + * @param coinName + * @param unitName + * @param orderType + * @param orderStatus + * @param payType + * @return + */ + List listUserOrder(@Param("userId") String userId, @Param("coinName") String coinName, + @Param("unitName") String unitName, @Param("orderType") String orderType, + @Param("orderStatus") String orderStatus, @Param("payType") String payType); + + /*** + * 根据userid查询订单 + * @param userId + * @return + */ + Order selectByUserId(@Param("userId") String userId); + + /*** + * 使用排他锁查询订单 + * @param orderId + * @return + */ + Order selectByIdForUpdate(@Param("orderId") String orderId); + + /*** + * 根据订单流水号查询订单 + * @param orderNumber + * @return + */ + Order selectByOrderNumber(@Param("orderNumber") String orderNumber); + + /*** + * 根据userId和订单Id查询 + * @param userId + * @param orderId + * @return + */ + Order selectByUserIdAndOrderID(@Param("userId") String userId, @Param("orderId") String orderId); + + /*** + * 根据状态查询订单 + * @param status + * @return + */ + List listByStatus(@Param("status") String status); + + /*** + * 根据状态、创建时间比较 查询 + * @param status + * @param date + * @param offest + * @param rowConut + * @return + */ + List selectByStatusAndCreateTime(@Param("status") String status, + @Param("date") Date date, + @Param("offest") Integer offest, + @Param("rowConut") Integer rowConut); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/UserHandleLogMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/UserHandleLogMapper.java new file mode 100644 index 0000000..0e02a10 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/UserHandleLogMapper.java @@ -0,0 +1,15 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.entity.UserHandleLog; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * UserHandleLogMapper 数据访问类 + * + * @version 1.0 + * @date 2019-04-15 10:37:32 + */ +@Repository +public interface UserHandleLogMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/UserPayInfoMapper.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/UserPayInfoMapper.java new file mode 100644 index 0000000..93df6cb --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/mapper/UserPayInfoMapper.java @@ -0,0 +1,33 @@ +package com.blockchain.server.otc.mapper; + +import com.blockchain.server.otc.entity.UserPayInfo; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * UserPayInfoMapper 数据访问类 + * + * @version 1.0 + * @date 2019-04-15 14:35:49 + */ +@Repository +public interface UserPayInfoMapper extends Mapper { + + /*** + * 根据userId和支付类型查询支付信息 + * @param userId + * @param payType + * @return + */ + UserPayInfo selectByUserIdAndPayType(@Param("userId") String userId, @Param("payType") String payType); + + /*** + * 根据userId查询用户支付信息列表 + * @param userId + * @return + */ + List listByUserId(@Param("userId") String userId); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/redis/OrderCache.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/redis/OrderCache.java new file mode 100644 index 0000000..e2191ca --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/redis/OrderCache.java @@ -0,0 +1,50 @@ +package com.blockchain.server.otc.redis; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.text.MessageFormat; +import java.util.concurrent.TimeUnit; + +@Component +public class OrderCache { + + @Autowired + private RedisTemplate redisTemplate; + + //新建订单key的前缀,用于自动撤单 + private static final String REDIS_KEY_NEW_ORDER_PRE = "otc:new:order:"; + //新建订单key + private static final String REDIS_KEY_NEW_ORDER = REDIS_KEY_NEW_ORDER_PRE + "{0}"; + + /*** + * 设置新建订单id到redis中 + * 失效时间通过外部传递 + * 失效时间的单位是:分钟 + * + * @param orderId 订单id + * @param offset 失效时间/分钟 + */ + public void setNewOrderCache(String orderId, long offset) { + String key = this.getNewOrderKey(orderId); + redisTemplate.opsForValue().set(key, System.currentTimeMillis(), offset, TimeUnit.MINUTES); + } + + /*** + * 获取新建订单key前缀 + * @return + */ + public static String getNewOrderPre() { + return REDIS_KEY_NEW_ORDER_PRE; + } + + /*** + * 获取新建订单key + * @param orderId + * @return + */ + public static String getNewOrderKey(String orderId) { + return MessageFormat.format(REDIS_KEY_NEW_ORDER, orderId); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/schedule/AutoCancelOrderScheduling.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/schedule/AutoCancelOrderScheduling.java new file mode 100644 index 0000000..92b46a6 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/schedule/AutoCancelOrderScheduling.java @@ -0,0 +1,79 @@ +package com.blockchain.server.otc.schedule; + +import com.blockchain.server.otc.common.constant.OrderConstants; +import com.blockchain.server.otc.service.ConfigService; +import com.blockchain.server.otc.service.OrderService; +import org.apache.commons.lang3.time.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.List; + +@Component +public class AutoCancelOrderScheduling { + + @Autowired + private OrderService orderService; + @Autowired + private ConfigService configService; + + @Scheduled(cron = "0 */1 * * * ?") + public void autoCancelOrder() { + //查询是否开启自动撤单 + boolean autoCancelIsY = configService.selectAutoCancelIsY(); + //关闭自动撤单 + if (!autoCancelIsY) { + return; + } + //查询倒计时分钟数 + Long minute = configService.selectAutoCancelInterval(); + //计算当前时间减去倒计时分钟数的时间 + Date cancelTime = DateUtils.addMinutes(new Date(), -minute.intValue()); + //检查是否存在新建状态的超时订单 + boolean flag = checkNewOrderExist(cancelTime); + //存在超时订单 + if (flag) { + Integer pageNum = 1; + Integer pageSize = 10; + //撤单 + cancelOrders(cancelTime, pageNum, pageSize); + } + } + + /*** + * 检查是否存在新建状态的超时订单 + * @return + */ + private boolean checkNewOrderExist(Date cancelTime) { + //查询超时订单 + List orderIds = orderService.selectAutoCancelOrder(OrderConstants.NEW, cancelTime, 0, 1); + if (orderIds.size() > 0) { + return true; + } + return false; + } + + /*** + * 撤销订单 + * @param cancelTime + * @param pageNum + * @param pageSize + */ + private void cancelOrders(Date cancelTime, Integer pageNum, Integer pageSize) { + //根据分页查询超时订单 + List orderIds = orderService.selectAutoCancelOrder(OrderConstants.NEW, cancelTime, (pageNum - 1) * pageSize, pageSize); + //迭代撤销 + for (String orderId : orderIds) { + orderService.autoCancelOrder(orderId); + } + //如果超时订单列表还有下一页 + if (orderIds.size() >= pageSize) { + //翻页 + pageNum++; + //递归,继续撤销 + cancelOrders(cancelTime, pageNum, pageSize); + } + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AdService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AdService.java new file mode 100644 index 0000000..e7abeca --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AdService.java @@ -0,0 +1,88 @@ +package com.blockchain.server.otc.service; + +import com.blockchain.server.otc.dto.ad.ListAdDTO; +import com.blockchain.server.otc.dto.ad.ListUserAdDTO; +import com.blockchain.server.otc.dto.ad.PublishAdParamDTO; +import com.blockchain.server.otc.entity.Ad; + +import java.util.List; + +public interface AdService { + /*** + * 发布买入广告 + * @param param + */ + void publishBuyAd(PublishAdParamDTO param); + + /*** + * 发布卖出广告 + * @param param + */ + void publishSellAd(PublishAdParamDTO param); + + /*** + * 撤销广告 + * @param userId + * @param adId + */ + void cancelAd(String userId, String adId); + + /*** + * 下架广告 + * @param userId + * @param adId + */ + void setAdToPending(String userId, String adId); + + /*** + * 上架广告 + * @param userId + * @param adId + */ + void setAdToDefault(String userId,String adId); + + /*** + * 查询交易大厅广告列表 + * @param userId + * @param adType + * @param coinName + * @param unitName + * @param adStatus + * @param priceSort + * @return + */ + List listAd(String userId, String adType, String coinName, String unitName, + String[] adStatus, String priceSort, String beginTime, String endTime); + + /*** + * 查询用户发布的广告列表 + * @param userId + * @param adType + * @param coinName + * @param unitName + * @param adStatus + * @return + */ + List listUserAd(String userId, String adType, String coinName, String unitName, String adStatus, String beginTime, String endTime); + + /*** + * 使用排他锁查询广告 + * @param adId + * @return + */ + Ad selectByIdForUpdate(String adId); + + /*** + * 根据id查询广告 + * @param adId + * @return + */ + Ad selectById(String adId); + + /*** + * 更新广告 + * @param ad + * @return + */ + int updateByPrimaryKeySelective(Ad ad); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealDetailService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealDetailService.java new file mode 100644 index 0000000..41385d3 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealDetailService.java @@ -0,0 +1,14 @@ +package com.blockchain.server.otc.service; + +public interface AppealDetailService { + + /*** + * 新增申诉详情 + * @param userId + * @param appealId + * @param role + * @param remark + * @return + */ + String insertAppealDetail(String userId, String appealId, String role, String remark); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealHandleLogService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealHandleLogService.java new file mode 100644 index 0000000..4b96de1 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealHandleLogService.java @@ -0,0 +1,22 @@ +package com.blockchain.server.otc.service; + +import com.blockchain.server.otc.dto.appeal.AppealHandleLogDTO; + +public interface AppealHandleLogService { + + /*** + * 新增申诉处理日志记录 + * @param orderNumber + * @param sysUserId + * @param afterStatus + * @return + */ + int insertAppealHandleLog(String orderNumber, String sysUserId, String ipAddress, String afterStatus, String remark); + + /*** + * 根据订单流水查询订单申诉情况 + * @param orderNumber + * @return + */ + AppealHandleLogDTO selectByOrderNumber(String orderNumber); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealImgService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealImgService.java new file mode 100644 index 0000000..5393dbf --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealImgService.java @@ -0,0 +1,12 @@ +package com.blockchain.server.otc.service; + +public interface AppealImgService { + + /*** + * 新增申诉图片记录 + * @param appealDetailId + * @param appealImgUrl + * @return + */ + int insertAppealImg(String appealDetailId, String appealImgUrl); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealService.java new file mode 100644 index 0000000..b6d55a3 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/AppealService.java @@ -0,0 +1,22 @@ +package com.blockchain.server.otc.service; + +public interface AppealService { + /** + * 提交申诉 + * + * @param userId + * @param orderId + * @param urls + * @param remark + * @return + */ + void appeal(String userId, String orderId, String[] urls, String remark); + + /*** + * 更新申诉状态 + * @param orderNumber + * @param appealStatus + * @return + */ + int updateAppeal(String orderNumber, String appealStatus); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/BillService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/BillService.java new file mode 100644 index 0000000..b8fdf1c --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/BillService.java @@ -0,0 +1,18 @@ +package com.blockchain.server.otc.service; + +import java.math.BigDecimal; + +public interface BillService { + + /*** + * 新增资金对账记录 + * @param userId + * @param recordNumber + * @param freeBalance + * @param freezeBalance + * @param billType + * @param coinName + * @return + */ + int insertBill(String userId, String recordNumber, BigDecimal freeBalance, BigDecimal freezeBalance, String billType, String coinName); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/CoinService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/CoinService.java new file mode 100644 index 0000000..6555ec4 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/CoinService.java @@ -0,0 +1,30 @@ +package com.blockchain.server.otc.service; + +import com.blockchain.server.otc.dto.coin.CoinDTO; +import com.blockchain.server.otc.dto.coin.CoinServiceChargeDTO; +import com.blockchain.server.otc.entity.Coin; + +import java.util.List; + +public interface CoinService { + + /*** + * 根据基本货币、二级货币查询币种信息 + * @param coinName + * @param unitName + * @return + */ + Coin selectByCoinAndUnit(String coinName, String unitName); + + /*** + * 查询可用币种列表 + * @return + */ + List listCoin(String coinStatus); + + /*** + * 查询币种手续费列表 + * @return + */ + List listCoinServiceCharge(); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/ConfigService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/ConfigService.java new file mode 100644 index 0000000..d94556c --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/ConfigService.java @@ -0,0 +1,61 @@ +package com.blockchain.server.otc.service; + +import com.blockchain.server.otc.dto.config.ConfigDTO; +import com.blockchain.server.otc.entity.Config; + +import java.math.BigDecimal; +import java.util.List; + +public interface ConfigService { + + /*** + * 根据配置key查询配置信息 + * @param key + * @return + */ + Config selectByKey(String key); + + /*** + * 查询自动撤单是否开启 + * true 开启 + * false 关闭 + * @return + */ + boolean selectAutoCancelIsY(); + + /*** + * 查询自动撤单时间间隔 + * @return + */ + Long selectAutoCancelInterval(); + + /*** + * 查询手续费开启状态 + * @return + */ + List selectServiceCharge(); + + /*** + * 查询市商可发布多少卖单 + * @return + */ + Integer selectMarketSellAdCount(); + + /*** + * 查询市商可发布多少买单 + * @return + */ + Integer selectMarketBuyAdCount(); + + /*** + * 查询市商保证金扣除的代币 + * @return + */ + String selectMarketFreezeCoin(); + + /*** + * 查询市商保证金数量 + * @return + */ + BigDecimal selectMarketFreezeAmount(); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/DealStatsService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/DealStatsService.java new file mode 100644 index 0000000..8613d46 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/DealStatsService.java @@ -0,0 +1,47 @@ +package com.blockchain.server.otc.service; + +import com.blockchain.server.otc.entity.DealStats; + +public interface DealStatsService { + /*** + * 如果不存在交易统计记录,新增一条 + * @param userId + * @return + */ + int insertIsNotExist(String userId); + + /*** + * 根据userId查询用户成交记录 + * @param userId + * @return + */ + DealStats selectByUserId(String userId); + + /*** + * 更新广告交易次数 + * @param userId + * @return + */ + int updateAdTransNum(String userId); + + /*** + * 更新广告成交次数 + * @param userId + * @return + */ + int updateAdMarkNum(String userId); + + /*** + * 更新卖单成交次数 + * @param userId + * @return + */ + int updateOrderSellNum(String userId); + + /*** + * 更新买单成交次数 + * @param userId + * @return + */ + int updateOrderBuyNum(String userId); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/MarketApplyService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/MarketApplyService.java new file mode 100644 index 0000000..2bdae54 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/MarketApplyService.java @@ -0,0 +1,22 @@ +package com.blockchain.server.otc.service; + +import com.blockchain.server.otc.entity.MarketApply; + +public interface MarketApplyService { + + /*** + * 申请市商或申请取消市商 + * @param userId + * @param applyType + */ + void applyMarket(String userId, String applyType); + + /*** + * 根据用户id和申请类型查询 + * @param userId + * @param applyType + * @param status + * @return + */ + MarketApply getByUserIdAndApplyTypeAndStatus(String userId, String applyType, String status); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/MarketFreezeService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/MarketFreezeService.java new file mode 100644 index 0000000..3a6b0c6 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/MarketFreezeService.java @@ -0,0 +1,13 @@ +package com.blockchain.server.otc.service; + +import com.blockchain.server.otc.entity.MarketFreeze; + +public interface MarketFreezeService { + + /*** + * 根据用户id查询保证金信息 + * @param userId + * @return + */ + MarketFreeze getByUserId(String userId); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/MarketUserService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/MarketUserService.java new file mode 100644 index 0000000..4dd6394 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/MarketUserService.java @@ -0,0 +1,33 @@ +package com.blockchain.server.otc.service; + +import com.blockchain.server.otc.entity.MarketUser; + +public interface MarketUserService { + + /*** + * 判断用户是否是市商 + * @param userId + */ + void checkMarketUser(String userId); + + /*** + * 查询用户市商状态 + * @param userId + * @return + */ + String getMarketUserStatus(String userId); + + /*** + * 查询用户市商 + * @param userId + * @return + */ + MarketUser getMarketUserByUserId(String userId); + + /*** + * 更新用户市商 + * @param marketUser + * @return + */ + int updateByPrimaryKeySelective(MarketUser marketUser); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/OrderService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/OrderService.java new file mode 100644 index 0000000..129e6dd --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/OrderService.java @@ -0,0 +1,138 @@ +package com.blockchain.server.otc.service; + +import com.blockchain.server.otc.dto.order.OrderDTO; +import com.blockchain.server.otc.entity.Order; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +public interface OrderService { + /*** + * 根据广告id和单个或多个状态查询订单 + * @param adId + * @param status + * @return + */ + List selectByAdIdAndStatus(String adId, String... status); + + /*** + * 根据参数查询用户订单 + * @param userId + * @param coinName + * @param unitName + * @param orderType + * @param orderStatus + * @param payType + * @return + */ + List listUserOrder(String userId, String coinName, String unitName, String orderType, String orderStatus, String payType); + + /*** + * 根据id查询订单 + * @param orderId + * @return + */ + OrderDTO selectByUserIdAndOrderId(String userId, String orderId); + + /*** + * 法币买入 + * @param userId + * @param adId + * @param amount + * @param price + * @param turnover + */ + String buyOrder(String userId, String adId, BigDecimal amount, BigDecimal price, BigDecimal turnover); + + /*** + * 法币卖出 + * @param userId + * @param adId + * @param amount + * @param turnover + * @param pass + */ + String sellOrder(String userId, String adId, BigDecimal amount, BigDecimal price, BigDecimal turnover, String pass); + + /*** + * 撤销订单 + * @param userId + * @param orderId + */ + void cancelBuyOrder(String userId, String orderId); + + /**** + * 撤销订单,定时器调用 + * @param orderId + */ + void autoCancelOrder(String orderId); + + /*** + * 根据订单状态查询订单 + * @param status + * @return + */ + List listByStatus(String status); + + /*** + * 查询广告伞下是否还有未完成订单 + * @param adId + * @return true存在未完成订单 + */ + boolean checkOrdersUnfinished(String adId); + + /*** + * 确认付款 + * @param userId + * @param orderId + * @param payType + */ + void pay(String userId, String orderId, String payType); + + /*** + * 确认收款 + * @param userId + * @param orderId + * @param pass + */ + void receipt(String userId, String orderId, String pass); + + /*** + * 排他锁查询订单信息 + * @param orderId + * @return + */ + Order selectByIdForUpdate(String orderId); + + /*** + * 根据id查询订单 + * @param orderId + * @return + */ + Order selectById(String orderId); + + /*** + * 更新订单信息 + * @param order + * @return + */ + int updateByPrimaryKeySelective(Order order); + + /*** + * 根据订单流水号查询订单 + * @param orderNumber + * @return + */ + Order selectByOrderNumber(String orderNumber); + + /*** + * 查询可以撤销的订单id列表 + * @param status + * @param date + * @param pageNum + * @param pageSize + * @return + */ + List selectAutoCancelOrder(String status, Date date, Integer pageNum, Integer pageSize); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/UserHandleLogService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/UserHandleLogService.java new file mode 100644 index 0000000..3fa9905 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/UserHandleLogService.java @@ -0,0 +1,13 @@ +package com.blockchain.server.otc.service; + +public interface UserHandleLogService { + /*** + * 新建用户操作日志 + * @param userId + * @param handleNumber + * @param handleType + * @param handleNumberType + * @return + */ + int insertUserHandleLog(String userId, String handleNumber, String handleType, String handleNumberType); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/UserPayInfoService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/UserPayInfoService.java new file mode 100644 index 0000000..e7e9ccb --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/UserPayInfoService.java @@ -0,0 +1,73 @@ +package com.blockchain.server.otc.service; + +import com.blockchain.server.otc.entity.UserPayInfo; + +import java.util.List; + +public interface UserPayInfoService { + + /*** + * 根据userId和支付类型查询用户支付信息 + * @param userId + * @param payType + * @return + */ + UserPayInfo selectByUserIdAndPayType(String userId, String payType); + + /*** + * 根据userId查询用户支付信息列表 + * @param userId + * @return + */ + List listUserPay(String userId); + + /*** + * 根据广告设置的支付类型查询卖家支付信息 + * + * @param userId + * @param orderId + * @return + */ + List selectSellUserPayInfoByAdPayType(String userId, String orderId); + + /*** + * 新增微信或支付宝支付信息 + * @param userId + * @param payType + * @param accountInfo + * @param codeUrl + * @param pass + * @return + */ + int insertWXorZFB(String userId, String payType, String accountInfo, String codeUrl, String pass); + + /*** + * 新增银行卡支付信息 + * @param userId + * @param bankNumber + * @param bankUserName + * @param bankType + * @return + */ + int insertBank(String userId, String bankNumber, String bankUserName, String bankType, String pass); + + /*** + * 更新微信、支付宝信息 + * @param userId + * @param payType + * @param accountInfo + * @param codeUrl + * @return + */ + int updateWXorZFB(String userId, String payType, String accountInfo, String codeUrl, String pass); + + /*** + * 更新银行卡信息 + * @param userId + * @param bankNumber + * @param bankUserName + * @param bankType + * @return + */ + int updateBank(String userId, String bankNumber, String bankUserName, String bankType, String pass); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/WalletService.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/WalletService.java new file mode 100644 index 0000000..5b9bed1 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/WalletService.java @@ -0,0 +1,37 @@ +package com.blockchain.server.otc.service; + +import java.math.BigDecimal; + +public interface WalletService { + + /*** + * 冻结、解冻余额 + * @param userId + * @param publishId 订单id + * @param coinName 基本货币 + * @param unitName 二级货币 + * @param freeBalance 可用余额 + * @param freezeBalance 冻结余额 + */ + void handleBalance(String userId, String publishId, String coinName, + String unitName, BigDecimal freeBalance, BigDecimal freezeBalance); + + /*** + * 扣除、增加真实余额 + * @param userId + * @param detailId 成交记录id + * @param coinName 基本货币 + * @param unitName 二级货币 + * @param freeBalance 可用余额 + * @param freezeBalance 冻结余额 + * @param gasBalance 手续费 + */ + void handleRealBalance(String userId, String detailId, String coinName, + String unitName, BigDecimal freeBalance, BigDecimal freezeBalance, BigDecimal gasBalance); + + /*** + * 验证密码 + * @param pass 公钥加密后的密码 + */ + void isPassword(String pass); +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AdServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AdServiceImpl.java new file mode 100644 index 0000000..421a9cc --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AdServiceImpl.java @@ -0,0 +1,561 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.otc.common.constant.*; +import com.blockchain.server.otc.common.enums.*; +import com.blockchain.server.otc.common.exception.OtcException; +import com.blockchain.server.otc.common.util.CheckDecimalUtil; +import com.blockchain.server.otc.dto.ad.ListAdDTO; +import com.blockchain.server.otc.dto.ad.ListUserAdDTO; +import com.blockchain.server.otc.dto.ad.PublishAdParamDTO; +import com.blockchain.server.otc.dto.user.UserBaseDTO; +import com.blockchain.server.otc.entity.*; +import com.blockchain.server.otc.feign.UserFeign; +import com.blockchain.server.otc.mapper.AdMapper; +import com.blockchain.server.otc.service.*; +import com.codingapi.tx.annotation.ITxTransaction; +import com.codingapi.tx.annotation.TxTransaction; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +@Service +public class AdServiceImpl implements AdService, ITxTransaction { + + @Autowired + private AdMapper adMapper; + @Autowired + private CoinService coinService; + @Autowired + private DealStatsService dealStatsService; + @Autowired + private UserPayInfoService userPayInfoService; + @Autowired + private UserHandleLogService userHandleLogService; + @Autowired + private WalletService walletService; + @Autowired + private BillService billService; + @Autowired + private OrderService orderService; + @Autowired + private ConfigService configService; + @Autowired + private MarketUserService marketUserService; + @Autowired + private UserFeign userFeign; + + @Override + @Transactional + @TxTransaction(isStart = true) + public void publishBuyAd(PublishAdParamDTO param) { + //发布广告参数校验方法 + Coin coin = publishAdVerify(param); + //检查市商否能发布更多广告 + checkMarketAdCount(param.getUserId(), CommonConstans.BUY, coin.getCoinName(), coin.getUnitName()); + //广告手续费率 + BigDecimal serviceCharge = checkAdServiceCharge(coin.getCoinServiceCharge(), param.getUserId()); + //新建广告数据,返回广告流水号 + String adNumber = insertAd(param, CommonConstans.BUY, serviceCharge); + //新增用户交易数据表 + dealStatsService.insertIsNotExist(param.getUserId()); + //记录用户操作 + insertUserHandleLog(param.getUserId(), adNumber, UserHandleConstants.PUBLISH); + } + + @Override + @Transactional + @TxTransaction(isStart = true) + public void publishSellAd(PublishAdParamDTO param) { + //发布广告参数校验方法 + Coin coin = publishAdVerify(param); + //检查市商否能发布更多广告 + checkMarketAdCount(param.getUserId(), CommonConstans.SELL, coin.getCoinName(), coin.getUnitName()); + //检查卖家是否绑定支付方式 + checkPublishPaysIsBinging(param); + //判断是否开启广告手续费 + BigDecimal chargeRatio = checkAdServiceCharge(coin.getCoinServiceCharge(), param.getUserId()); + //新建广告数据,返回广告流水号 + String adNumber = insertAd(param, CommonConstans.SELL, chargeRatio); + //更新钱包并记录资金变动 + publishSellAdHandleWallet(param, adNumber, chargeRatio); + //新增用户交易数据表 + dealStatsService.insertIsNotExist(param.getUserId()); + //记录用户操作 + insertUserHandleLog(param.getUserId(), adNumber, UserHandleConstants.PUBLISH); + } + + /*** + * 检查市商否能发布更多广告 + * + * @param userId + * @param adType + */ + private void checkMarketAdCount(String userId, String adType, String coinName, String unitName) { + String[] adStatus = {AdConstants.DEFAULT, AdConstants.PENDING, AdConstants.UNDERWAY}; + //查询用户是否存在交易中、挂单中、下架中的广告 + List ads = adMapper.listUserAdByStatusAndType(userId, adType, coinName, unitName, adStatus); + //可发布次数 + Integer count; + //根据买卖查询买卖单可发布次数 + if (checkBuyOrSell(adType)) { + count = configService.selectMarketBuyAdCount(); + } else { + count = configService.selectMarketSellAdCount(); + } + + //如果用户未完成广告数量大于限制,抛出异常 + if (count != null && ads.size() >= count) { + throw new OtcException(OtcEnums.PUBLISH_AD_EXCEED_THE_LIMIT); + } + } + + @Override + @Transactional + @TxTransaction(isStart = true) + public void cancelAd(String userId, String adId) { + //校验的状态 + String[] realStatus = {AdConstants.DEFAULT, AdConstants.PENDING}; + //撤销广告时的校验方法 + Ad ad = handleAdVerify(userId, adId, realStatus); + //查询广告伞下是否还有未完成订单 + checkOrdersUnfinished(adId); + //如果是卖单,解冻余额 + cancelSellAdHandleWallet(ad); + //新增用户操作记录 + insertUserHandleLog(userId, ad.getAdNumber(), UserHandleConstants.CANCEL); + //更新修改时间和状态 + ad.setAdStatus(AdConstants.CANCEL); + ad.setModifyTime(new Date()); + adMapper.updateByPrimaryKeySelective(ad); + } + + @Override + @Transactional + public void setAdToPending(String userId, String adId) { + //校验的状态 + String[] realStatus = {AdConstants.DEFAULT}; + //下架广告时的校验方法 + Ad ad = handleAdVerify(userId, adId, realStatus); + //查询广告伞下是否还有未完成订单 + checkOrdersUnfinished(adId); + //新增用户操作记录 + insertUserHandleLog(userId, ad.getAdNumber(), UserHandleConstants.PENDING); + //更新修改时间和状态 + ad.setAdStatus(AdConstants.PENDING); + ad.setModifyTime(new Date()); + adMapper.updateByPrimaryKeySelective(ad); + } + + @Override + @Transactional + public void setAdToDefault(String userId, String adId) { + //校验的状态 + String[] realStatus = {AdConstants.PENDING}; + Ad ad = handleAdVerify(userId, adId, realStatus); + //上架时判断剩余数量是否足以交易 + if ((ad.getLastNum().multiply(ad.getPrice())).compareTo(ad.getMinLimit()) < 0) { + throw new OtcException(OtcEnums.AD_LAST_NUM_LESS_THAN_LIMIT); + } + //新增用户操作记录 + insertUserHandleLog(userId, ad.getAdNumber(), UserHandleConstants.DEFAULT); + //更新修改时间和状态 + ad.setAdStatus(AdConstants.DEFAULT); + ad.setModifyTime(new Date()); + adMapper.updateByPrimaryKeySelective(ad); + } + + @Override + public List listAd(String userId, String adType, String coinName, String unitName, String adStatus[], + String priceSort, String beginTime, String endTime) { + List listAdDTOS = adMapper.listAd(userId, adType, coinName, unitName, adStatus, priceSort, beginTime, endTime); + for (ListAdDTO listAdDTO : listAdDTOS) { + UserBaseDTO userBaseDTO = selectBaseUserByUserId(listAdDTO.getUserId()); + //查询用户信息 + listAdDTO.setNickname(userBaseDTO.getNickName()); + listAdDTO.setUsername(userBaseDTO.getMobilePhone()); + listAdDTO.setUserImg(userBaseDTO.getAvatar()); + } + return listAdDTOS; + } + + @Override + public List listUserAd(String userId, String adType, String coinName, String unitName, + String adStatus, String beginTime, String endTime) { + return adMapper.listUserAd(userId, adType, coinName, unitName, adStatus, beginTime, endTime); + } + + @Override + public Ad selectByIdForUpdate(String adId) { + return adMapper.selectByIdForUpdate(adId); + } + + @Override + public Ad selectById(String adId) { + return adMapper.selectByPrimaryKey(adId); + } + + @Override + @Transactional + public int updateByPrimaryKeySelective(Ad ad) { + return adMapper.updateByPrimaryKeySelective(ad); + } + + /**** + * 发布广告前的校验方法 + * @param param + * @return + */ + private Coin publishAdVerify(PublishAdParamDTO param) { + //检查发布参数 + checkPublishParam(param); + //判断用户是否可以发布广告 + marketUserService.checkMarketUser(param.getUserId()); + //检查密码 + walletService.isPassword(param.getPass()); + //检查币对 + Coin coin = checkCoinIsNull(param.getCoinName(), param.getUnitName()); + //检查发布数量、单价小数长度、单价下限是否合法 + checkPublishNumber(param, coin); + //检查发布交易额与交易下限是否合法 + checkPublishLimit(param); + + return coin; + } + + /*** + * 撤销广告时的校验方法 + * 上架、下架、撤销广告时的校验方法 + * @param userId + * @param adId + * @return + */ + private Ad handleAdVerify(String userId, String adId, String[] status) { + //广告id是否为空 + if (StringUtils.isBlank(adId)) { + throw new OtcException(OtcEnums.AD_ID_NULL); + } + //查询广告 + Ad ad = adMapper.selectByIdForUpdate(adId); + //用户id不一致 + if (!ad.getUserId().equals(userId)) { + throw new OtcException(OtcEnums.AD_USERID_NOT_EQUALS); + } + //记录是否外部状态是否匹配广告状态 + boolean statusFlag = false; + //判断外部传入的status参数 + for (String s : status) { + //匹配其中一个,改变flag为true + if (ad.getAdStatus().equals(s)) { + statusFlag = true; + } + } + //一个都不匹配,抛出异常 + if (!statusFlag) { + throw new OtcException(OtcEnums.AD_STATUS_ERROR); + } + + return ad; + } + + /*** + * 发布卖出广告时更新钱包并记录资金变动 + * @param param + * @param adNumber + * @param chargeRatio + */ + private void publishSellAdHandleWallet(PublishAdParamDTO param, String adNumber, BigDecimal chargeRatio) { + //增加冻结余额 + BigDecimal freezeBalance = param.getTotalNum().add(param.getTotalNum().multiply(chargeRatio)); + //扣除可用余额 + BigDecimal freeBalance = freezeBalance.multiply(CommonConstans.MINUS_ONE); + //调用feign冻结余额 + walletService.handleBalance(param.getUserId(), adNumber, param.getCoinName(), param.getUnitName(), freeBalance, freezeBalance); + //新增资金对账记录 + billService.insertBill(param.getUserId(), adNumber, freeBalance, freezeBalance, BillConstants.PUBLISH, param.getCoinName()); + } + + /**** + * 撤销卖出广告时更新钱包并记录资金变动 + * @param ad + */ + private void cancelSellAdHandleWallet(Ad ad) { + //判断是买或卖,买为true + boolean adTypeFlag = checkBuyOrSell(ad.getAdType()); + //广告为卖出时,退回余额 + if (!adTypeFlag) { + //手续费 + BigDecimal serviceCharge = ad.getChargeRatio(); + //增加可用余额 + BigDecimal freeBalance = ad.getLastNum().add(ad.getLastNum().multiply(serviceCharge)); + //减少冻结余额 + BigDecimal freezeBalance = freeBalance.multiply(CommonConstans.MINUS_ONE); + //解冻余额 + walletService.handleBalance(ad.getUserId(), ad.getAdNumber(), ad.getCoinName(), ad.getUnitName(), freeBalance, freezeBalance); + //新增资金对账记录 + billService.insertBill(ad.getUserId(), ad.getAdNumber(), freeBalance, freezeBalance, BillConstants.CANCEL, ad.getCoinName()); + } + } + + /*** + * 判断类型是买或卖 + * true是买 + * @param type + * @return + */ + private boolean checkBuyOrSell(String type) { + if (type.equals(CommonConstans.BUY)) { + return true; + } + if (type.equals(CommonConstans.SELL)) { + return false; + } + throw new OtcException(OtcEnums.BUY_OR_SELL_TYPE_ERROR); + } + + /*** + * 检查币种是否可用 + * @param coinName + * @param unitName + * @return + */ + private Coin checkCoinIsNull(String coinName, String unitName) { + Coin coin = coinService.selectByCoinAndUnit(coinName, unitName); + //判空 + if (coin == null) { + throw new OtcException(OtcEnums.COIN_NULL); + } + //是否可用 + if (coin.getStatus().equals(CommonConstans.NO)) { + throw new OtcException(OtcEnums.COIN_NULL); + } + return coin; + } + + /*** + * 检查发布广告参数 + * @param param + */ + private void checkPublishParam(PublishAdParamDTO param) { + if (StringUtils.isBlank(param.getCoinName())) { + throw new OtcException(OtcEnums.PUBLISH_AD_COIN_NULL); + } + if (StringUtils.isBlank(param.getUnitName())) { + throw new OtcException(OtcEnums.PUBLISH_AD_UNIT_NULL); + } + if (StringUtils.isBlank(param.getUserId())) { + throw new OtcException(OtcEnums.USERID_NULL); + } + if (param.getMinLimit() == null || param.getMinLimit().compareTo(BigDecimal.ZERO) <= 0) { + throw new OtcException(OtcEnums.PUBLISH_AD_MINLIMIT_NULL); + } +// if (param.getMinLimit().toString().indexOf(".") != -1) { +// throw new OtcException(OtcEnums.PUBLISH_AD_MINLIMIT_ERROR); +// } + if (param.getPrice() == null || param.getPrice().compareTo(BigDecimal.ZERO) <= 0) { + throw new OtcException(OtcEnums.PUBLISH_AD_PRICE_NULL); + } + if (param.getTotalNum() == null || param.getTotalNum().compareTo(BigDecimal.ZERO) <= 0) { + throw new OtcException(OtcEnums.PUBLISH_AD_TOTALNUM_NULL); + } + if (param.getPayType() == null || param.getPayType().length == 0) { + throw new OtcException(OtcEnums.PUBLISH_AD_PAYTYPE_NULL); + } + } + + /*** + * 检查发布数量、小数长度是否合法 + * @param param + * @param coin + */ + private void checkPublishNumber(PublishAdParamDTO param, Coin coin) { + //检查单价小数长度 + CheckDecimalUtil.checkDecimal(param.getPrice(), coin.getUnitDecimal(), OtcEnums.PUBLISH_AD_PRICE_ERROR); + //检查数量小数长度 + CheckDecimalUtil.checkDecimal(param.getTotalNum(), coin.getCoinDecimal(), OtcEnums.PUBLISH_AD_TOTALNUM_ERROR); + } + + /*** + * 校验支付信息是否匹配枚举 + * 并返回支付信息拼接字符串 + * @param paysType + * @return + */ + private String checkPaysTypeIsRealAndGeneratePayStr(String[] paysType) { + //最终返回的支付类型拼接字符串 + String pays = ""; + for (int i = 0; i < paysType.length; i++) { + String pay = paysType[i]; + //是否是银行类型 + boolean isBank = pay.equals(UserPayConstants.BANK); + //是否是微信类型 + boolean isWx = pay.equals(UserPayConstants.WX); + //是否是支付宝类型 + boolean isZfb = pay.equals(UserPayConstants.ZFB); + //如果以上三个类型都不匹配,抛出异常 + if (!isBank && !isWx && !isZfb) { + throw new OtcException(OtcEnums.PUBLISH_AD_PAY_ERROR); + } + //如果是数组的最后一个,不拼接逗号 + if (i == paysType.length - 1) { + pays += pay; + } else { + pays += pay + ","; + } + } + return pays; + } + + /*** + * 检查发布卖单时,用户选择的支付方式是否已绑定 + * @param param + */ + private void checkPublishPaysIsBinging(PublishAdParamDTO param) { + for (String pay : param.getPayType()) { + UserPayInfo userPayInfo = userPayInfoService.selectByUserIdAndPayType(param.getUserId(), pay); + if (userPayInfo == null) { + if (pay.equals(UserPayConstants.BANK)) { + throw new OtcException(OtcEnums.PUBLISH_AD_PAY_BANK_NULL); + } + if (pay.equals(UserPayConstants.WX)) { + throw new OtcException(OtcEnums.PUBLISH_AD_PAY_WX_NULL); + } + if (pay.equals(UserPayConstants.ZFB)) { + throw new OtcException(OtcEnums.PUBLISH_AD_PAY_ZFB_NULL); + } + } + } + } + + /*** + * 检查发布数量、单价与交易上/下限是否合法 + * @param param + */ + private void checkPublishLimit(PublishAdParamDTO param) { + BigDecimal turnover = param.getPrice().multiply(param.getTotalNum()); + //判断交易总额是否比交易下限低 + if (turnover.compareTo(param.getMinLimit()) < 0) { + throw new OtcException(OtcEnums.PUBLISH_AD_MINLIMIT_TOO_HIGH); + } + } + + /*** + * 新增广告记录,返回广告流水号 + * @param param + * @param adType + * @param coinChargeRaito + * @return adNumber + */ + private String insertAd(PublishAdParamDTO param, String adType, BigDecimal coinChargeRaito) { + //检查发布支付类型与枚举是否正确,并返回支付信息字符串 + String pays = checkPaysTypeIsRealAndGeneratePayStr(param.getPayType()); + + Ad ad = new Ad(); + Date now = new Date(); + String adId = UUID.randomUUID().toString(); + String adNumber = createAdNumber(); + ad.setId(adId); + ad.setUserId(param.getUserId()); + ad.setAdNumber(adNumber); + ad.setTotalNum(param.getTotalNum()); + ad.setLastNum(param.getTotalNum()); + ad.setPrice(param.getPrice()); + ad.setMaxLimit(param.getPrice().multiply(param.getTotalNum()).setScale(CommonConstans.LIMIT_CNY_DECIMAL, BigDecimal.ROUND_DOWN)); + ad.setMinLimit(param.getMinLimit().setScale(CommonConstans.LIMIT_CNY_DECIMAL, BigDecimal.ROUND_DOWN)); + ad.setChargeRatio(coinChargeRaito); + ad.setUnitName(param.getUnitName()); + ad.setCoinName(param.getCoinName()); + ad.setAdPay(pays); + ad.setAdStatus(AdConstants.DEFAULT); + ad.setAdType(adType); + ad.setAdRemark(param.getRemark()); + ad.setCreateTime(now); + ad.setModifyTime(now); + adMapper.insertSelective(ad); + return adNumber; + } + + /*** + * 新增用户操作日志 + * @param userId + * @param adNumber + * @param handleType + * @return + */ + private int insertUserHandleLog(String userId, String adNumber, String handleType) { + return userHandleLogService.insertUserHandleLog(userId, adNumber, handleType, UserHandleConstants.AD); + } + + /*** + * 查询广告伞下是否还有未完成订单 + * @param adId + */ + private void checkOrdersUnfinished(String adId) { + //查询广告伞下是否还有未完成订单 + boolean flag = orderService.checkOrdersUnfinished(adId); + //true存在未完成订单,抛出异常 + if (flag) { + throw new OtcException(OtcEnums.CANCEL_AD_ORDERS_UNFINISHED); + } + } + + /*** + * 查询用户主体信息 + * @param userId + * @return + */ + private UserBaseDTO selectBaseUserByUserId(String userId) { + ResultDTO result = userFeign.selectUserInfoById(userId); + return result.getData(); + } + + /*** + * 生成广告随机流水号 + * @return + */ + private String createAdNumber() { + return System.currentTimeMillis() + UUID.randomUUID().toString().substring(0, 6).toUpperCase(); + } + + /*** + * 判断是否开启广告手续费、用户是否免手续费 + * @param coinServiceCharge 币种手续费 + * @param userId + * @return + */ + private BigDecimal checkAdServiceCharge(BigDecimal coinServiceCharge, String userId) { + boolean flag = verifyFreeTransaction(userId); + //用户免除手续费 + if (flag) { + return BigDecimal.ZERO; + } + //查询广告手续费是否开启 + Config config = configService.selectByKey(ConfigConstants.AD_SERVICE_CHARGE); +// //查询卖家手续费是否开启 +// Config config = configService.selectByKey(ConfigConstants.SELL_SERVICE_CHARGE); + //如果广告手续费配置为空或者配置状态为禁用 + if (config == null || config.getStatus().equals(CommonConstans.NO)) { + //返回0手续费 + return BigDecimal.ZERO; + } + //返回手续费 + return coinServiceCharge; + } + + /*** + * 查询用户是否免交易手续费 + * true是免手续费 + * @param userId + * @return + */ + private boolean verifyFreeTransaction(String userId) { + ResultDTO result = userFeign.verifyFreeTransaction(userId); + return result.getData(); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealDetailServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealDetailServiceImpl.java new file mode 100644 index 0000000..cb944fa --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealDetailServiceImpl.java @@ -0,0 +1,33 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.server.otc.entity.AppealDetail; +import com.blockchain.server.otc.mapper.AppealDetailMapper; +import com.blockchain.server.otc.service.AppealDetailService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.UUID; + +@Service +public class AppealDetailServiceImpl implements AppealDetailService { + + @Autowired + private AppealDetailMapper appealDetailMapper; + + @Override + @Transactional + public String insertAppealDetail(String userId, String appealId, String role, String remark) { + AppealDetail appealDetail = new AppealDetail(); + String appealDetialId = UUID.randomUUID().toString(); + appealDetail.setId(appealDetialId); + appealDetail.setAppealId(appealId); + appealDetail.setUserId(userId); + appealDetail.setAppealRole(role); + appealDetail.setRemark(remark); + appealDetail.setCreateTime(new Date()); + appealDetailMapper.insertSelective(appealDetail); + return appealDetialId; + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealHandleLogServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealHandleLogServiceImpl.java new file mode 100644 index 0000000..322c7c3 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealHandleLogServiceImpl.java @@ -0,0 +1,38 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.server.otc.dto.appeal.AppealHandleLogDTO; +import com.blockchain.server.otc.entity.AppealHandleLog; +import com.blockchain.server.otc.mapper.AppealHandleLogMapper; +import com.blockchain.server.otc.service.AppealHandleLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.UUID; + +@Service +public class AppealHandleLogServiceImpl implements AppealHandleLogService { + + @Autowired + private AppealHandleLogMapper appealHandleLogMapper; + + @Override + @Transactional + public int insertAppealHandleLog(String orderNumber, String sysUserId, String ipAddress, String afterStatus, String remark) { + AppealHandleLog appealHandleLog = new AppealHandleLog(); + appealHandleLog.setId(UUID.randomUUID().toString()); + appealHandleLog.setOrderNubmer(orderNumber); + appealHandleLog.setSysUserId(sysUserId); + appealHandleLog.setIpAddress(ipAddress); + appealHandleLog.setRemark(remark); + appealHandleLog.setAfterStatus(afterStatus); + appealHandleLog.setCreateTime(new Date()); + return appealHandleLogMapper.insertSelective(appealHandleLog); + } + + @Override + public AppealHandleLogDTO selectByOrderNumber(String orderNumber) { + return appealHandleLogMapper.selectByOrderNumber(orderNumber); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealImgServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealImgServiceImpl.java new file mode 100644 index 0000000..d10decf --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealImgServiceImpl.java @@ -0,0 +1,29 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.server.otc.entity.AppealImg; +import com.blockchain.server.otc.mapper.AppealImgMapper; +import com.blockchain.server.otc.service.AppealImgService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.UUID; + +@Service +public class AppealImgServiceImpl implements AppealImgService { + + @Autowired + private AppealImgMapper appealImgMapper; + + @Override + @Transactional + public int insertAppealImg(String appealDetailId, String appealImgUrl) { + AppealImg appealImg = new AppealImg(); + appealImg.setId(UUID.randomUUID().toString()); + appealImg.setAppealDetailId(appealDetailId); + appealImg.setAppealImgUrl(appealImgUrl); + appealImg.setCreateTime(new Date()); + return appealImgMapper.insertSelective(appealImg); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealServiceImpl.java new file mode 100644 index 0000000..905e732 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/AppealServiceImpl.java @@ -0,0 +1,234 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.common.base.constant.PushConstants; +import com.blockchain.common.base.enums.PushEnums; +import com.blockchain.server.otc.common.constant.AppealConstants; +import com.blockchain.server.otc.common.constant.CommonConstans; +import com.blockchain.server.otc.common.constant.OrderConstants; +import com.blockchain.server.otc.common.constant.UserHandleConstants; +import com.blockchain.server.otc.common.enums.JgMsgEnums; +import com.blockchain.server.otc.common.enums.OtcEnums; +import com.blockchain.server.otc.common.exception.OtcException; +import com.blockchain.server.otc.common.util.ImUtil; +import com.blockchain.server.otc.entity.Appeal; +import com.blockchain.server.otc.entity.Order; +import com.blockchain.server.otc.feign.PushFeign; +import com.blockchain.server.otc.mapper.AppealMapper; +import com.blockchain.server.otc.service.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@Service +public class AppealServiceImpl implements AppealService { + + @Autowired + private AppealMapper appealMapper; + @Autowired + private AppealDetailService appealDetailService; + @Autowired + private AppealImgService appealImgService; + @Autowired + private OrderService orderService; + @Autowired + private UserHandleLogService userHandleLogService; + @Autowired + private ImUtil imUtil; + @Autowired + private PushFeign pushFeign; + + @Override + @Transactional + public void appeal(String userId, String orderId, String[] urls, String remark) { + //查询订单 + Order order = orderService.selectByIdForUpdate(orderId); + //如果订单状态不等于进行中和申诉中,不能申诉 + if (!order.getOrderStatus().equals(OrderConstants.UNDERWAY) && + !order.getOrderStatus().equals(OrderConstants.APPEAL)) { + throw new OtcException(OtcEnums.ORDER_STATUS_CHANGES); + } + //操作用户是否是订单其中一方用户 + if (!order.getSellUserId().equals(userId) && !order.getBuyUserId().equals(userId)) { + throw new OtcException(OtcEnums.ORDER_APPEAL_USERID_NOT_EQUALS); + } + //如果卖家状态等于确认收款,不能申诉 + if (order.getSellStatus().equals(OrderConstants.CONFIRM)) { + throw new OtcException(OtcEnums.ORDER_SELL_STATUS_CHANGES); + } + //如果买家状态等于取消,不能申诉 + if (order.getBuyStatus().equals(OrderConstants.CANCEL)) { + throw new OtcException(OtcEnums.ORDER_BUY_STATUS_CHANGES); + } + //判断申诉记录是否存在 + String appealId = checkAppealIsExist(order.getOrderNumber()); + //更新订单状态 + updateOrderStatus(order, userId); + //新增申诉详情记录 + String appealDetailId = insertAppealDetail(userId, appealId, order, remark); + //新增申诉图片记录 + insertAppealImg(appealDetailId, urls); + //新增用户操作记录 + insertUserHandleLog(userId, order.getOrderNumber(), UserHandleConstants.APPEAL); + //发送聊天信息 + sendAppealMsg(order.getSellUserId(), order.getBuyUserId(), orderId); + //申诉后发送消息通知给被诉方 + checkRoleAndPush(userId, order.getSellUserId(), order.getBuyUserId(), orderId); + } + + @Override + @Transactional + public int updateAppeal(String orderNumber, String appealStatus) { + Appeal appeal = appealMapper.selectByOrderNumber(orderNumber); + appeal.setStatus(appealStatus); + appeal.setModifyTime(new Date()); + return appealMapper.updateByPrimaryKeySelective(appeal); + } + + /*** + * 申诉后发送聊天提示信息(极光IM聊天API) + * @param sellUserId + * @param buyUserId + * @param orderId + */ + private void sendAppealMsg(String sellUserId, String buyUserId, String orderId) { + imUtil.postMsgIMSelf(JgMsgEnums.APPEAL.getName(), sellUserId, buyUserId, orderId); + imUtil.postMsgIMSelf(JgMsgEnums.APPEAL.getName(), buyUserId, sellUserId, orderId); + } + + /*** + * 申诉后发送消息通知给被诉方 + * @param userId + * @param sellUserId + * @param buyUserId + * @param orderId + */ + private void checkRoleAndPush(String userId, String sellUserId, String buyUserId, String orderId) { + //判断提交申诉的用户然后发送消息通知被诉用户 + if (userId.equals(sellUserId)) { + pushToSingle(buyUserId, orderId, PushEnums.OTC_ORDER_APPEAL.getPushType()); + } + if (userId.equals(buyUserId)) { + pushToSingle(sellUserId, orderId, PushEnums.OTC_ORDER_APPEAL.getPushType()); + } + } + + /*** + * 发送手机消息通知(个推消息推送API) + * @param userId + * @param orderId + * @param pushType + */ + private void pushToSingle(String userId, String orderId, String pushType) { + Map payload = new HashMap<>(); + payload.put(PushConstants.ORDER_ID, orderId); + payload.put(PushConstants.PUSH_TYPE, pushType); + pushFeign.pushToSingle(userId, pushType, payload); + } + + /*** + * 判断申诉记录是否存在 + * @param orderNumber + * @return + */ + private String checkAppealIsExist(String orderNumber) { + //根据流水号查询申诉记录 + Appeal appeal = appealMapper.selectByOrderNumber(orderNumber); + //如果记录为空,新建一个 + if (appeal == null) { + Appeal newAppeal = new Appeal(); + Date now = new Date(); + String appealId = UUID.randomUUID().toString(); + newAppeal.setId(appealId); + newAppeal.setCreateTime(now); + newAppeal.setModifyTime(now); + newAppeal.setOrderNumber(orderNumber); + newAppeal.setStatus(AppealConstants.NEW); + appealMapper.insertSelective(newAppeal); + return appealId; + } else { + //订单id + return appeal.getId(); + } + } + + /*** + * 更新订单状态 + * @param order + * @param userId + */ + private void updateOrderStatus(Order order, String userId) { + //判断是那一方进行申诉 + if (order.getBuyUserId().equals(userId)) { + order.setBuyStatus(OrderConstants.APPEAL); + } + if (order.getSellUserId().equals(userId)) { + order.setSellStatus(OrderConstants.APPEAL); + } + order.setOrderStatus(OrderConstants.APPEAL); + order.setModifyTime(new Date()); + orderService.updateByPrimaryKeySelective(order); + } + + /*** + * 新建申诉详情 + * @param userId + * @param appealId + * @param order + * @param remark + * @return + */ + private String insertAppealDetail(String userId, String appealId, Order order, String remark) { + String role; + //买单 + if (order.getOrderType().equals(CommonConstans.BUY)) { + //申诉用户和买家匹配 + if (order.getBuyUserId().equals(userId)) { + //下单方申诉 + role = AppealConstants.ORDER; + } else { + //否则,发布方申诉 + role = AppealConstants.AD; + } + } else { + //卖单 + //申诉用户和卖家匹配 + if (order.getSellUserId().equals(userId)) { + //下单方申诉 + role = AppealConstants.ORDER; + } else { + //否则,发布方申诉 + role = AppealConstants.AD; + } + } + + //新增申诉详情记录 + return appealDetailService.insertAppealDetail(userId, appealId, role, remark); + } + + /*** + * 新增申诉图片记录 + * @param appealDetailId + * @param urls + */ + private void insertAppealImg(String appealDetailId, String[] urls) { + for (String url : urls) { + appealImgService.insertAppealImg(appealDetailId, url); + } + } + + /*** + * 新增用户操作日志 + * @param userId + * @param orderNumber + * @param handleType + * @return + */ + private int insertUserHandleLog(String userId, String orderNumber, String handleType) { + return userHandleLogService.insertUserHandleLog(userId, orderNumber, handleType, UserHandleConstants.ORDER); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/BillServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/BillServiceImpl.java new file mode 100644 index 0000000..4bbb4e6 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/BillServiceImpl.java @@ -0,0 +1,34 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.server.otc.entity.Bill; +import com.blockchain.server.otc.mapper.BillMapper; +import com.blockchain.server.otc.service.BillService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.UUID; + +@Service +public class BillServiceImpl implements BillService { + + @Autowired + private BillMapper billMapper; + + @Override + @Transactional + public int insertBill(String userId, String recordNumber, BigDecimal freeBalance, BigDecimal freezeBalance, String billType, String coinName) { + Bill bill = new Bill(); + bill.setId(UUID.randomUUID().toString()); + bill.setUserId(userId); + bill.setRecordNumber(recordNumber); + bill.setBillType(billType); + bill.setFreebalance(freeBalance); + bill.setFreezebalance(freezeBalance); + bill.setCoinName(coinName); + bill.setCreateTime(new Date()); + return billMapper.insertSelective(bill); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/CoinServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/CoinServiceImpl.java new file mode 100644 index 0000000..75dc30a --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/CoinServiceImpl.java @@ -0,0 +1,35 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.server.otc.dto.coin.CoinDTO; +import com.blockchain.server.otc.dto.coin.CoinServiceChargeDTO; +import com.blockchain.server.otc.entity.Coin; +import com.blockchain.server.otc.mapper.CoinMapper; +import com.blockchain.server.otc.service.CoinService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class CoinServiceImpl implements CoinService { + + @Autowired + private CoinMapper coinMapper; + + @Override + public Coin selectByCoinAndUnit(String coinName, String unitName) { + return coinMapper.selectByCoinAndUnit(coinName, unitName); + } + + @Override + public List listCoin(String coinStatus) { + return coinMapper.listCoin(coinStatus); + } + + @Override + public List listCoinServiceCharge() { + return coinMapper.listCoinServiceCharge(); + } + + +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/ConfigServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/ConfigServiceImpl.java new file mode 100644 index 0000000..6b6574a --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/ConfigServiceImpl.java @@ -0,0 +1,115 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.server.otc.common.constant.CommonConstans; +import com.blockchain.server.otc.common.constant.ConfigConstants; +import com.blockchain.server.otc.dto.config.ConfigDTO; +import com.blockchain.server.otc.entity.Config; +import com.blockchain.server.otc.mapper.ConfigMapper; +import com.blockchain.server.otc.service.ConfigService; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +@Service +public class ConfigServiceImpl implements ConfigService { + + @Autowired + private ConfigMapper configMapper; + + private static final long DEFAULT_AUTO_CANCEL_INTERVAL = 15L;//默认撤单时间间隔 + + @Override + public Config selectByKey(String key) { + return configMapper.selectByKey(key); + } + + @Override + public boolean selectAutoCancelIsY() { + Config config = configMapper.selectByKey(ConfigConstants.AUTO_CANCEL); + if (config == null) { + return false; + } + if (config.getStatus().equals(CommonConstans.NO)) { + return false; + } + return true; + } + + @Override + public Long selectAutoCancelInterval() { + //查询是否打开自动撤单 + boolean flag = selectAutoCancelIsY(); + //打开自动撤单 + if (flag) { + //查询自动撤单时间间隔 + Config config = configMapper.selectByKey(ConfigConstants.AUTO_CANCEL_INTERVAL); + //配置为空,返回默认值 + if (config == null) { + return DEFAULT_AUTO_CANCEL_INTERVAL; + } else { + return Long.valueOf(config.getDataValue()); + } + } else { + //没打开自动撤单,返回空 + return null; + } + + } + + @Override + public List selectServiceCharge() { + Config adCharge = configMapper.selectByKey(ConfigConstants.AD_SERVICE_CHARGE); + ConfigDTO adChargeDTO = new ConfigDTO(); + BeanUtils.copyProperties(adCharge, adChargeDTO); + + Config orderCharge = configMapper.selectByKey(ConfigConstants.ORDER_SERVICE_CHARGE); + ConfigDTO orderChargeDTO = new ConfigDTO(); + BeanUtils.copyProperties(orderCharge, orderChargeDTO); + + List results = new ArrayList<>(); + results.add(adChargeDTO); + results.add(orderChargeDTO); + + return results; + } + + @Override + public Integer selectMarketSellAdCount() { + Config config = configMapper.selectByKey(ConfigConstants.MARKET_SELL_COUNT); + if (config != null && config.getStatus().equals(CommonConstans.YES)) { + return Integer.valueOf(config.getDataValue()); + } + return null; + } + + @Override + public Integer selectMarketBuyAdCount() { + Config config = configMapper.selectByKey(ConfigConstants.MARKET_BUY_COUNT); + if (config != null && config.getStatus().equals(CommonConstans.YES)) { + return Integer.valueOf(config.getDataValue()); + } + return null; + } + + @Override + public String selectMarketFreezeCoin() { + Config config = configMapper.selectByKey(ConfigConstants.MARKET_FREEZE_COIN); + if (config != null && config.getStatus().equals(CommonConstans.YES)) { + return config.getDataValue(); + } + return null; + } + + @Override + public BigDecimal selectMarketFreezeAmount() { + Config config = configMapper.selectByKey(ConfigConstants.MARKET_FREEZE_AMOUNT); + if (config != null && config.getStatus().equals(CommonConstans.YES)) { + return new BigDecimal(config.getDataValue()); + } + return BigDecimal.ZERO; + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/DealStatsServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/DealStatsServiceImpl.java new file mode 100644 index 0000000..3c97fa8 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/DealStatsServiceImpl.java @@ -0,0 +1,67 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.server.otc.entity.DealStats; +import com.blockchain.server.otc.mapper.DealStatsMapper; +import com.blockchain.server.otc.service.DealStatsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; + +@Service +public class DealStatsServiceImpl implements DealStatsService { + + @Autowired + private DealStatsMapper dealStatsMapper; + + @Override + @Transactional + public int insertIsNotExist(String userId) { + DealStats isNull = dealStatsMapper.selectByPrimaryKey(userId); + if (isNull != null) { + return 0; + } + DealStats dealStats = new DealStats(); + Date now = new Date(); + dealStats.setUserId(userId); + dealStats.setCreateTime(now); + dealStats.setModifyTime(now); + return dealStatsMapper.insertSelective(dealStats); + } + + @Override + public DealStats selectByUserId(String userId) { + return dealStatsMapper.selectByPrimaryKey(userId); + } + + @Override + @Transactional + public int updateAdTransNum(String userId) { + this.insertIsNotExist(userId); + return dealStatsMapper.updateAdTransNum(userId, new Date()); + } + + @Override + @Transactional + public int updateAdMarkNum(String userId) { + this.insertIsNotExist(userId); + return dealStatsMapper.updateAdMarkNum(userId, new Date()); + } + + @Override + @Transactional + public int updateOrderSellNum(String userId) { + this.insertIsNotExist(userId); + return dealStatsMapper.updateOrderSellNum(userId, new Date()); + } + + @Override + @Transactional + public int updateOrderBuyNum(String userId) { + this.insertIsNotExist(userId); + return dealStatsMapper.updateOrderBuyNum(userId, new Date()); + } + +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/MarketApplyServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/MarketApplyServiceImpl.java new file mode 100644 index 0000000..4a84b3b --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/MarketApplyServiceImpl.java @@ -0,0 +1,169 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.otc.common.constant.MarketApplyConstants; +import com.blockchain.server.otc.common.constant.MarketUserConstants; +import com.blockchain.server.otc.common.enums.OtcEnums; +import com.blockchain.server.otc.common.exception.OtcException; +import com.blockchain.server.otc.entity.MarketApply; +import com.blockchain.server.otc.entity.MarketFreeze; +import com.blockchain.server.otc.entity.MarketUser; +import com.blockchain.server.otc.feign.UserFeign; +import com.blockchain.server.otc.mapper.MarketApplyMapper; +import com.blockchain.server.otc.service.ConfigService; +import com.blockchain.server.otc.service.MarketApplyService; +import com.blockchain.server.otc.service.MarketFreezeService; +import com.blockchain.server.otc.service.MarketUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.UUID; + +@Service +public class MarketApplyServiceImpl implements MarketApplyService { + + @Autowired + private MarketApplyMapper marketApplyMapper; + @Autowired + private MarketFreezeService marketFreezeService; + @Autowired + private MarketUserService marketUserService; + @Autowired + private ConfigService configService; + @Autowired + private UserFeign userFeign; + + @Override + @Transactional + public void applyMarket(String userId, String applyType) { + //提交市商申请时,判断用户是否通过高级认证 + checkUserHighAuth(userId, applyType); + //判断用户市商状态 + MarketUser marketUser = checkMarketUserStatus(userId, applyType); + //判断用户是否存在未处理的申请记录 + checkUserHasMoreApply(userId, applyType); + //申请取消市商时,更新市商用户状态 + cancelApplyUpdateMarketUser(marketUser, applyType); + //新增市商申请 + insertMarketApply(userId, applyType); + } + + @Override + public MarketApply getByUserIdAndApplyTypeAndStatus(String userId, String applyType, String status) { + return marketApplyMapper.selectByUserIdAndApplyTypeAndStatus(userId, applyType, status); + } + + /*** + * 检查市商用户状态 + * @param userId + * @param applyType + */ + private MarketUser checkMarketUserStatus(String userId, String applyType) { + MarketUser marketUser = marketUserService.getMarketUserByUserId(userId); + + //申请成为市商 + if (applyType.equals(MarketApplyConstants.MARKET)) { + if (marketUser != null) { + //市商状态不等于未认证 + if (!marketUser.getStatus().equals(MarketUserConstants.NOTMARKET)) { + throw new OtcException(OtcEnums.USER_IS_MRAKET); + } + } + } + + //申请取消市商 + if (applyType.equals(MarketApplyConstants.CANCEL)) { + //市商用户为空 + if (marketUser == null) { + throw new OtcException(OtcEnums.MARKET_USER_NULL); + } + //市商用户状态不等于市商 + if (!marketUser.getStatus().equals(MarketUserConstants.MARKET)) { + throw new OtcException(OtcEnums.CANCEL_MARKET_USER_STATUS_ERROR); + } + } + + return marketUser; + } + + /*** + * 申请市商时,检查 + * @param userId + * @param applyType + */ + private void checkUserHighAuth(String userId, String applyType) { + if (applyType.equals(MarketApplyConstants.MARKET)) { + //判断用户是否通过高级认证 + userFeign.hasHighAuthAndUserList(userId); + } + } + + /*** + * 新增市商申请 + * @param userId + * @param applyType + * @return + */ + private int insertMarketApply(String userId, String applyType) { + MarketApply marketApply = new MarketApply(); + Date now = new Date(); + marketApply.setId(UUID.randomUUID().toString()); + marketApply.setUserId(userId); + marketApply.setApplyType(applyType); + marketApply.setCreateTime(now); + marketApply.setModifyTime(now); + marketApply.setStatus(MarketApplyConstants.NEW); + + //市商申请时,查询配置表获取保证金信息 + if (applyType.equals(MarketApplyConstants.MARKET)) { + //查询保证金扣除的代币 + String coin = configService.selectMarketFreezeCoin(); + marketApply.setCoinName(coin); + //查询保证金扣除的数量 + BigDecimal amount = configService.selectMarketFreezeAmount(); + marketApply.setAmount(amount); + } + + //取消市商时,查询押金表获取保证金信息 + if (applyType.equals(MarketApplyConstants.CANCEL)) { + MarketFreeze marketFreeze = marketFreezeService.getByUserId(userId); + //押金记录不存在,抛出异常 + if (marketFreeze == null) { + throw new OtcException(OtcEnums.MARKET_FREEZE_NULL); + } + marketApply.setCoinName(marketFreeze.getCoinName()); + marketApply.setAmount(marketFreeze.getAmount()); + } + + return marketApplyMapper.insertSelective(marketApply); + } + + /*** + * 提交取消市商,更新市商用户状态 + * @param marketUser + * @param applyType + */ + private void cancelApplyUpdateMarketUser(MarketUser marketUser, String applyType) { + if (applyType.equals(MarketApplyConstants.CANCEL)) { + //状态改为取消中 + marketUser.setStatus(MarketUserConstants.CANCELING); + marketUser.setModifyTime(new Date()); + marketUserService.updateByPrimaryKeySelective(marketUser); + } + } + + /*** + * 判断用户是否存在未处理的申请记录 + * @param userId + * @param applyType + */ + private void checkUserHasMoreApply(String userId, String applyType) { + MarketApply marketApply = getByUserIdAndApplyTypeAndStatus(userId, applyType, MarketApplyConstants.NEW); + if (marketApply != null) { + throw new OtcException(OtcEnums.MARKET_APPLY_HAS_MORE); + } + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/MarketFreezeServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/MarketFreezeServiceImpl.java new file mode 100644 index 0000000..b755bb1 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/MarketFreezeServiceImpl.java @@ -0,0 +1,19 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.server.otc.entity.MarketFreeze; +import com.blockchain.server.otc.mapper.MarketFreezeMapper; +import com.blockchain.server.otc.service.MarketFreezeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class MarketFreezeServiceImpl implements MarketFreezeService { + + @Autowired + private MarketFreezeMapper marketFreezeMapper; + + @Override + public MarketFreeze getByUserId(String userId) { + return marketFreezeMapper.selectByUserId(userId); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/MarketUserServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/MarketUserServiceImpl.java new file mode 100644 index 0000000..3fd9e24 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/MarketUserServiceImpl.java @@ -0,0 +1,47 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.server.otc.common.constant.MarketUserConstants; +import com.blockchain.server.otc.common.enums.OtcEnums; +import com.blockchain.server.otc.common.exception.OtcException; +import com.blockchain.server.otc.entity.MarketUser; +import com.blockchain.server.otc.mapper.MarketUserMapper; +import com.blockchain.server.otc.service.MarketUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class MarketUserServiceImpl implements MarketUserService { + + @Autowired + private MarketUserMapper marketUserMapper; + + @Override + public void checkMarketUser(String userId) { + MarketUser marketUser = marketUserMapper.selectByUserId(userId); + //市商用户为空或者不等于市商状态,抛出异常 + if (marketUser == null || !marketUser.getStatus().equals(MarketUserConstants.MARKET)) { + throw new OtcException(OtcEnums.USER_NOT_MARKET); + } + } + + @Override + public String getMarketUserStatus(String userId) { + MarketUser marketUser = marketUserMapper.selectByUserId(userId); + if (marketUser != null) { + return marketUser.getStatus(); + } + return null; + } + + @Override + public MarketUser getMarketUserByUserId(String userId) { + return marketUserMapper.selectByUserId(userId); + } + + @Override + @Transactional + public int updateByPrimaryKeySelective(MarketUser marketUser) { + return marketUserMapper.updateByPrimaryKeySelective(marketUser); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/OrderServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/OrderServiceImpl.java new file mode 100644 index 0000000..8aa1b77 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/OrderServiceImpl.java @@ -0,0 +1,1125 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.common.base.constant.PushConstants; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.enums.PushEnums; +import com.blockchain.server.otc.common.constant.*; +import com.blockchain.server.otc.common.enums.*; +import com.blockchain.server.otc.common.exception.OtcException; +import com.blockchain.server.otc.common.util.CheckDecimalUtil; +import com.blockchain.server.otc.common.util.ImUtil; +import com.blockchain.server.otc.dto.order.OrderDTO; +import com.blockchain.server.otc.dto.user.UserBaseDTO; +import com.blockchain.server.otc.entity.*; +import com.blockchain.server.otc.feign.PushFeign; +import com.blockchain.server.otc.feign.UserFeign; +import com.blockchain.server.otc.mapper.OrderMapper; +import com.blockchain.server.otc.redis.OrderCache; +import com.blockchain.server.otc.service.*; +import com.codingapi.tx.annotation.ITxTransaction; +import com.codingapi.tx.annotation.TxTransaction; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.*; + +@Service +public class OrderServiceImpl implements OrderService, ITxTransaction { + + @Autowired + private OrderMapper orderMapper; + @Autowired + private UserFeign userFeign; + @Autowired + private PushFeign pushFeign; + @Autowired + private AdService adService; + @Autowired + private CoinService coinService; + @Autowired + private UserHandleLogService userHandleLogService; + @Autowired + private DealStatsService dealStatsService; + @Autowired + private ConfigService configService; + @Autowired + private WalletService walletService; + @Autowired + private BillService billService; + @Autowired + private UserPayInfoService userPayInfoService; + @Autowired + private AppealService appealService; + @Autowired + private AppealHandleLogService appealHandleLogService; + @Autowired + private ImUtil imUtil; + @Autowired + private OrderCache orderCache; + + private static final BigDecimal DECIMAL_DISH = new BigDecimal("0.1"); //下单金额计算的偏差值 + + @Override + public List selectByAdIdAndStatus(String adId, String... status) { + return orderMapper.selectByAdIdAndStatus(adId, status); + } + + @Override + public List listUserOrder(String userId, String coinName, String unitName, String orderType, String orderStatus, String payType) { + List orderDTOS = orderMapper.listUserOrder(userId, coinName, unitName, orderType, orderStatus, payType); + //设置角色 + for (OrderDTO orderDTO : orderDTOS) { + //判断用户角色,true是买方 + boolean flag = checkBuyUserOrSellUser(userId, orderDTO.getBuyUserId(), orderDTO.getSellUserId()); + if (flag) { + orderDTO.setRole(CommonConstans.BUY); + } else { + orderDTO.setRole(CommonConstans.SELL); + } + } + return orderDTOS; + } + + @Override + public OrderDTO selectByUserIdAndOrderId(String userId, String orderId) { + Order order = orderMapper.selectByUserIdAndOrderID(userId, orderId); + //防空 + if (order == null) { + throw new OtcException(OtcEnums.ORDER_NULL); + } + OrderDTO orderDTO = new OrderDTO(); + BeanUtils.copyProperties(order, orderDTO); + //判断用户角色,true是买方 + boolean flag = checkBuyUserOrSellUser(userId, orderDTO.getBuyUserId(), orderDTO.getSellUserId()); + if (flag) { + orderDTO.setRole(CommonConstans.BUY); + } else { + orderDTO.setRole(CommonConstans.SELL); + } + + //查询双方用户数据,用于聊天展示 + UserBaseDTO buyUser = selectBaseUserByUserId(orderDTO.getBuyUserId()); + orderDTO.setBuyUserName(buyUser.getMobilePhone()); + orderDTO.setBuyNickName(buyUser.getNickName()); + orderDTO.setBuyAvatar(buyUser.getAvatar()); + + UserBaseDTO sellUser = selectBaseUserByUserId(orderDTO.getSellUserId()); + orderDTO.setSellUserName(sellUser.getMobilePhone()); + orderDTO.setSellNickName(sellUser.getNickName()); + orderDTO.setSellAvatar(sellUser.getAvatar()); + + return orderDTO; + } + + @Override + @Transactional + @TxTransaction(isStart = true) + public String buyOrder(String userId, String adId, BigDecimal amount, BigDecimal price, BigDecimal turnover) { + //校验下单参数、用户权限 + dealOrderParamVerify(userId, adId, amount, price, turnover); + //校验广告数据 + Ad ad = dealOrderAdVerify(adId, userId, amount, price, CommonConstans.SELL); + //校验数据小数位 + Coin coin = dealOrderDecimalVerify(ad.getCoinName(), ad.getUnitName(), amount, turnover); + //订单手续费 + BigDecimal serviceCharge = checkOrderServiceCharge(coin.getCoinServiceCharge(), userId); + //检查下单交易额的小数失真偏差,返回较小偏差的交易额插入数据库 + BigDecimal realTurnover = checkTurnoverDish(turnover, price, amount, coin.getUnitDecimal()); + //发布订单 + Order order = insertOrder(ad, userId, amount, realTurnover, CommonConstans.BUY, serviceCharge); + //广告扣除数量,更新广告 + updateAdLastNum(amount, ad, UserHandleConstants.DEAL); + //记录用户操作 + insertUserHandleLog(userId, order.getOrderNumber(), UserHandleConstants.DEAL); + //记录广告用户交易统计 + dealStatsService.updateAdTransNum(ad.getUserId());//广告交易次数 + //发送聊天提示信息 + sendNewOrderMsg(JgMsgEnums.FIRSTINIT_SELL.getName(), JgMsgEnums.FIRSTINIT_BUY.getName(), + order.getSellUserId(), order.getBuyUserId(), order.getId()); + //发送手机消息通知 + pushToSingle(order.getSellUserId(), order.getId(), PushEnums.OTC_ORDER_DEAL_BUY.getPushType()); + //判断是否需要设置订单自动撤单 + checkNewOrderAutoCancel(order.getId()); + //返回订单id + return order.getId(); + } + + @Override + @Transactional + @TxTransaction(isStart = true) + public String sellOrder(String userId, String adId, BigDecimal amount, BigDecimal price, BigDecimal turnover, String pass) { + //校验下单参数、用户权限 + dealOrderParamVerify(userId, adId, amount, price, turnover); + //检查密码 + walletService.isPassword(pass); + //校验广告数据 + Ad ad = dealOrderAdVerify(adId, userId, amount, price, CommonConstans.BUY); + //校验数据小数位 + Coin coin = dealOrderDecimalVerify(ad.getCoinName(), ad.getUnitName(), amount, turnover); + //检查用户是否绑定买家支付方式 + checkSellUserPayIsBingding(userId, ad.getAdPay()); + //判断是否开启卖家手续费 ;关闭订单用户手续费 20190618 + BigDecimal chargeRatio = checkOrderServiceCharge(coin.getCoinServiceCharge(), userId); + //检查下单交易额的小数失真偏差,返回较小偏差的交易额插入数据库 + BigDecimal realTurnover = checkTurnoverDish(turnover, price, amount, coin.getUnitDecimal()); + //发布订单 + Order order = insertOrder(ad, userId, amount, realTurnover, CommonConstans.SELL, chargeRatio); + //广告扣除数量,更新广告 + updateAdLastNum(amount, ad, UserHandleConstants.DEAL); + //下单卖单时更新钱包并记录资金变动 + dealSellOrderAndHandleWallet(userId, order.getOrderNumber(), coin.getCoinName(), coin.getUnitName(), amount, chargeRatio); + //记录用户操作 + insertUserHandleLog(userId, order.getOrderNumber(), UserHandleConstants.DEAL); + //记录广告用户交易统计 + dealStatsService.updateAdTransNum(ad.getUserId());//广告交易次数 + //发送提示信息 + sendNewOrderMsg(JgMsgEnums.FIRSTINIT_SELL.getName(), JgMsgEnums.FIRSTINIT_BUY.getName(), + order.getSellUserId(), order.getBuyUserId(), order.getId()); + //发送手机消息通知 + pushToSingle(order.getBuyUserId(), order.getId(), PushEnums.OTC_ORDER_DEAL_SELL.getPushType()); + //判断是否需要设置订单自动撤单 + checkNewOrderAutoCancel(order.getId()); + //返回订单id + return order.getId(); + } + + @Override + @Transactional + public void cancelBuyOrder(String userId, String orderId) { + //检查订单类型、状态 + Order order = checkCancelBuyOrder(orderId, userId); + //修改订单状态 + order.setBuyStatus(OrderConstants.CANCEL); + order.setOrderStatus(OrderConstants.CANCEL); + order.setModifyTime(new Date()); + orderMapper.updateByPrimaryKeySelective(order); + //将撤单数量退回到广告中 + Ad ad = adService.selectByIdForUpdate(order.getAdId()); + updateAdLastNum(order.getAmount(), ad, UserHandleConstants.CANCEL); + //记录用户操作 + insertUserHandleLog(userId, order.getOrderNumber(), UserHandleConstants.CANCEL); + //订单取消,发送提示消息 + sendNewOrderMsg(JgMsgEnums.REPEAL_SELL.getName(), JgMsgEnums.REPEAL_BUY.getName(), + order.getSellUserId(), order.getBuyUserId(), order.getId()); + //发送手机消息通知 + pushToSingle(order.getSellUserId(), order.getId(), PushEnums.OTC_ORDER_CANCEL.getPushType()); + } + + @Override + @Transactional + @TxTransaction(isStart = true) + public void autoCancelOrder(String orderId) { + //排他锁查询订单 + Order order = selectByIdForUpdate(orderId); + //订单数据校验 + boolean flag = autoCancelOrderVerify(order); + //返回false校验不通过,返回 + if (!flag) { + return; + } + //如果是卖单,退钱 + autoCancelOrderHandleWallet(order); + //更新订单状态 + order.setOrderStatus(OrderConstants.CANCEL); + order.setModifyTime(new Date()); + orderMapper.updateByPrimaryKeySelective(order); + //更新广告数量 + Ad ad = adService.selectByIdForUpdate(order.getAdId()); + updateAdLastNum(order.getAmount(), ad, UserHandleConstants.CANCEL); + //订单取消,发送提示消息 + sendNewOrderMsg(JgMsgEnums.AUTO_REPEAL.getName(), JgMsgEnums.AUTO_REPEAL.getName(), + order.getSellUserId(), order.getBuyUserId(), order.getId()); + //发送手机消息通知 + pushToSingle(order.getSellUserId(), order.getId(), PushEnums.OTC_ORDER_AUTO_CANCEL.getPushType()); + pushToSingle(order.getBuyUserId(), order.getId(), PushEnums.OTC_ORDER_AUTO_CANCEL.getPushType()); + } + + @Override + public List listByStatus(String status) { + return orderMapper.listByStatus(status); + } + + @Override + public boolean checkOrdersUnfinished(String adId) { + //根据广告id和订单状态(新建、进行中、申诉中)查询订单列表 + List orders = this.selectByAdIdAndStatus(adId, OrderConstants.NEW, + OrderConstants.UNDERWAY, OrderConstants.APPEAL); + //存在未完成订单,返回true + if (orders.size() != 0) { + return true; + } + return false; + } + + @Override + @Transactional + public void pay(String userId, String orderId, String payType) { + //订单id判空 + checkOrderIdNull(orderId); + //支付类型判空 + if (StringUtils.isBlank(payType)) { + throw new OtcException(OtcEnums.ORDER_PAY_PAYTYPE_NULL); + } + //排他锁查询订单 + Order order = orderMapper.selectByIdForUpdate(orderId); + //订单是否存在 + checkOrderNull(order); + //订单状态是否可确认付款 + checkOrderCanHandle(order, userId, UserHandleConstants.PAY); + //排他锁查询广告信息 + Ad ad = adService.selectByIdForUpdate(order.getAdId()); + //广告是否为空 + checkAdNull(ad); + //支付类型是否和广告设置的匹配 + checkPayTypeIsAdPayType(payType, ad.getAdPay()); + //确认支付时,设置订单支付信息 + order.setOrderPayType(payType); + //更新订单状态 + receiptOrPayUpdateOrder(order, UserHandleConstants.PAY); + //记录用户操作 + insertUserHandleLog(userId, order.getOrderNumber(), UserHandleConstants.PAY); + //确认付款发送提示消息 + sendNewOrderMsg(JgMsgEnums.CONFIRM_BUYER_SELL.getName(), JgMsgEnums.CONFIRM_BUYER_BUY.getName(), + order.getSellUserId(), order.getBuyUserId(), order.getId()); + //发送手机消息通知 + pushToSingle(order.getSellUserId(), order.getId(), PushEnums.OTC_ORDER_PAY.getPushType()); + } + + @Override + @Transactional + @TxTransaction(isStart = true) + public void receipt(String userId, String orderId, String pass) { + //订单id判空 + checkOrderIdNull(orderId); + //校验密码 + walletService.isPassword(pass); + //排他锁查询订单 + Order order = orderMapper.selectByIdForUpdate(orderId); + //订单是否存在 + checkOrderNull(order); + //检查订单能否确认收款 + checkOrderCanHandle(order, userId, UserHandleConstants.RECEIPT); + //更新订单状态 + receiptOrPayUpdateOrder(order, UserHandleConstants.RECEIPT); + //记录用户操作 + insertUserHandleLog(userId, order.getOrderNumber(), UserHandleConstants.RECEIPT); + //更新广告发布方成交统计数据 + updateAdUserDealStats(order); + //更新余额并记录资金变动 + receiptUpdateBalance(order); + //判断广告是否可以结束 + checkAdCanFinish(order.getAdId()); + //确认收款发送提示消息 + sendNewOrderMsg(JgMsgEnums.CONFIRM_SELLER_SELL.getName(), JgMsgEnums.CONFIRM_SELLER_BUY.getName(), + order.getSellUserId(), order.getBuyUserId(), order.getId()); + //发送手机消息通知 + pushToSingle(order.getBuyUserId(), order.getId(), PushEnums.OTC_ORDER_RECEIPT.getPushType()); + } + + @Override + public Order selectByIdForUpdate(String orderId) { + return orderMapper.selectByIdForUpdate(orderId); + } + + @Override + public Order selectById(String orderId) { + return orderMapper.selectByPrimaryKey(orderId); + } + + @Override + @Transactional + public int updateByPrimaryKeySelective(Order order) { + return orderMapper.updateByPrimaryKeySelective(order); + } + + @Override + public Order selectByOrderNumber(String orderNumber) { + return orderMapper.selectByOrderNumber(orderNumber); + } + + @Override + public List selectAutoCancelOrder(String status, Date date, Integer pageNum, Integer pageSize) { + return orderMapper.selectByStatusAndCreateTime(status, date, pageNum, pageSize); + } + + /*** + * 校验下单参数、用户权限 + * @param userId + * @param adId + * @param amount + * @param price + * @param turnover + */ + private void dealOrderParamVerify(String userId, String adId, BigDecimal amount, BigDecimal price, BigDecimal turnover) { + //检查参数是否为空 + checkDealParam(amount, price, turnover, adId); + //用户是否有权限交易 + userFeign.hasLowAuthAndUserList(userId); + } + + /*** + * 校验下单的广告 + * @param adId + * @param userId + * @param amount + * @param price + * @return + */ + private Ad dealOrderAdVerify(String adId, String userId, BigDecimal amount, BigDecimal price, String adType) { + //排他锁查询广告 + Ad ad = adService.selectByIdForUpdate(adId); + //检查广告状态、剩余数量 + checkAdStatus(ad); + //判断广告类型是否正确 + checkAdType(ad.getAdType(), adType); + //检查下单用户是不是广告发布用户 + checkAdUserAndOrderUserEquals(ad.getUserId(), userId); + //检查检查下单数量、单价是否合法 + checkAmountAndPrice(ad, amount, price); + + return ad; + } + + /*** + * 校验下单的数据小数位 + * @param coinName + * @param unitName + * @param amount + * @param turnover + * @return + */ + private Coin dealOrderDecimalVerify(String coinName, String unitName, BigDecimal amount, BigDecimal turnover) { + //查询币种 + Coin coin = coinService.selectByCoinAndUnit(coinName, unitName); + //检查下单数量小数长度是否合法 + CheckDecimalUtil.checkDecimal(amount, coin.getCoinDecimal(), OtcEnums.ORDER_DEAL_AMOUNT_DECIMAL_ERROR); + //检查下单交易额小数长度是否合法 + CheckDecimalUtil.checkDecimal(turnover, coin.getUnitDecimal(), OtcEnums.ORDER_DEAL_TURNOVER_DECIMAL_ERROR); + + return coin; + } + + /*** + * 下单卖单时更新钱包并记录资金变动 + * @param userId + * @param orderNumber + * @param coinName + * @param unitName + * @param amount + * @param chargeRatio + */ + private void dealSellOrderAndHandleWallet(String userId, String orderNumber, String coinName, String unitName, + BigDecimal amount, BigDecimal chargeRatio) { + //扣除可用余额 数量 + (数量 * 手续费) * -1 + BigDecimal freeBalance = amount.add(amount.multiply(chargeRatio)).multiply(CommonConstans.MINUS_ONE); + //增加冻结余额 数量 + (数量 * 手续费) + BigDecimal freezeBalance = amount.add(amount.multiply(chargeRatio)); + //冻结余额 + walletService.handleBalance(userId, orderNumber, coinName, unitName, freeBalance, freezeBalance); + //记录资金变动记录 + billService.insertBill(userId, orderNumber, freeBalance, freezeBalance, BillConstants.DEAL, coinName); + } + + /*** + * 自动撤单的订单数据校验 + * @param order + * @return + */ + private boolean autoCancelOrderVerify(Order order) { + //防空 + if (order == null) { + return false; + } + //买家不等于新建,返回 + if (!order.getBuyStatus().equals(OrderConstants.NEW)) { + return false; + } + //订单不等于新建,返回 + if (!order.getOrderStatus().equals(OrderConstants.NEW)) { + return false; + } + return true; + } + + /*** + * 撤销卖单时更新钱包 + * @param order + */ + private void autoCancelOrderHandleWallet(Order order) { + //true为买 + boolean flag = checkBuyOrSell(order.getOrderType()); + //订单卖出时解冻余额 + if (!flag) { + //退回手续费 + BigDecimal chargeRatio = order.getChargeRatio(); + //可用余额 + BigDecimal freeBalance = order.getAmount().add(order.getAmount().multiply(chargeRatio)); + //冻结余额 + BigDecimal freezeBalance = freeBalance.multiply(CommonConstans.MINUS_ONE); + //解冻卖家资金 + walletService.handleBalance(order.getSellUserId(), order.getOrderNumber(), order.getCoinName(), order.getUnitName(), freeBalance, freezeBalance); + //记录资金变动 + billService.insertBill(order.getSellUserId(), order.getOrderNumber(), freeBalance, freezeBalance, BillConstants.AUTO_CANCEL, order.getCoinName()); + } + } + + /*** + * 新增用户操作日志 + * @param userId + * @param orderNumber + * @param handleType + * @return + */ + private int insertUserHandleLog(String userId, String orderNumber, String handleType) { + return userHandleLogService.insertUserHandleLog(userId, orderNumber, handleType, UserHandleConstants.ORDER); + } + + /*** + * 判断用户角色 + * true是买方 + * @param userId + * @param buyUserId + * @param sellUserId + * @return + */ + private boolean checkBuyUserOrSellUser(String userId, String buyUserId, String sellUserId) { + if (userId.equals(buyUserId)) { + return true; + } + if (userId.equals(sellUserId)) { + return false; + } + throw new OtcException(OtcEnums.BUY_USER_OR_SELL_USER_ERROR); + } + + /*** + * 判断买卖类型 + * true是买 + * @param type + * @return + */ + private boolean checkBuyOrSell(String type) { + if (type.equals(CommonConstans.BUY)) { + return true; + } + if (type.equals(CommonConstans.SELL)) { + return false; + } + throw new OtcException(OtcEnums.BUY_OR_SELL_TYPE_ERROR); + } + + /*** + * 新建订单时判断是否需要自动撤单 + * @param orderId + */ + private void checkNewOrderAutoCancel(String orderId) { + //查询是否开启自动撤单 + boolean flag = configService.selectAutoCancelIsY(); + if (flag) { + //查询撤单时间间隔 + long interval = configService.selectAutoCancelInterval(); + //设置缓存 + orderCache.setNewOrderCache(orderId, interval); + } + } + + /*** + * 新建订单后发送聊天提示(极光IM聊天API) + * @param sellMsg + * @param buyMsg + * @param sellUserId + * @param buyUserId + * @param orderId + */ + private void sendNewOrderMsg(String sellMsg, String buyMsg, String sellUserId, String buyUserId, String orderId) { + imUtil.postMsgIMSelf(buyMsg, sellUserId, buyUserId, orderId); + imUtil.postMsgIMSelf(sellMsg, buyUserId, sellUserId, orderId); + } + + /*** + * 发送手机消息通知(个推消息推送API) + * @param userId + * @param orderId + * @param pushType + */ + private void pushToSingle(String userId, String orderId, String pushType) { + Map payload = new HashMap<>(); + payload.put(PushConstants.ORDER_ID, orderId); + payload.put(PushConstants.PUSH_TYPE, pushType); + pushFeign.pushToSingle(userId, pushType, payload); + } + + /*** + * 检查交易参数 + * @param amount + * @param price + * @param turnover + * @param adId + */ + private void checkDealParam(BigDecimal amount, BigDecimal price, BigDecimal turnover, String adId) { + //交易数量 + if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) { + throw new OtcException(OtcEnums.ORDER_DEAL_AMOUNT_NULL); + } + //单价 + if (price == null || price.compareTo(BigDecimal.ZERO) <= 0) { + throw new OtcException(OtcEnums.ORDER_DEAL_PRICE_NULL); + } + //交易额 + if (turnover == null || turnover.compareTo(BigDecimal.ZERO) <= 0) { + throw new OtcException(OtcEnums.ORDER_DEAL_TURNOVER_NULL); + } + //广告id + if (StringUtils.isBlank(adId)) { + throw new OtcException(OtcEnums.ORDER_DEAL_ADID_NULL); + } + } + + /*** + * 检查 + * 广告是否存在 + * 广告状态是否可交易 + * 广告剩余数量是否充足 + * @param ad + */ + private void checkAdStatus(Ad ad) { + //广告是否存在 + if (ad == null) { + throw new OtcException(OtcEnums.ORDER_AD_NULL); + } + //广告状态是否可以交易 + if (!ad.getAdStatus().equals(AdConstants.UNDERWAY) && !ad.getAdStatus().equals(AdConstants.DEFAULT)) { + throw new OtcException(OtcEnums.ORDER_DEAL_AD_STATUS_ERROR); + } + //广告剩余数量是否大于0,剩余数量是否足够交易 + if (ad.getLastNum().compareTo(BigDecimal.ZERO) <= 0 || + (ad.getLastNum().multiply(ad.getPrice())).compareTo(ad.getMinLimit()) < 0) { + throw new OtcException(OtcEnums.ORDER_DEAL_AD_NUM_ZERO); + } + } + + /*** + * 检查广告类型是否正确 + * @param adType 广告类型 + * @param trueType 正确的类型 + */ + private void checkAdType(String adType, String trueType) { + if (!adType.equals(trueType)) { + throw new OtcException(OtcEnums.ORDER_DEAL_AD_TYPE_ERROR); + } + } + + /*** + * 检查下单数量是否合法 + * 下单单价是否是最新单价 + * @param ad + * @param amount + * @param price + */ + private void checkAmountAndPrice(Ad ad, BigDecimal amount, BigDecimal price) { + //下单时单价和广告最新单价不一致 + if (price.compareTo(ad.getPrice()) != 0) { + throw new OtcException(OtcEnums.ORDER_DEAL_PRICE_NOT_REAL_TIME); + } + //广告数量是否充足 + if (amount.compareTo(ad.getLastNum()) > 0) { + throw new OtcException(OtcEnums.ORDER_DEAL_AD_NUM_ZERO); + } + } + + /**** + * 下单用户是不是广告发布的用户 + * @param adUserId + * @param orderUserId + */ + private void checkAdUserAndOrderUserEquals(String adUserId, String orderUserId) { + if (adUserId.equals(orderUserId)) { + throw new OtcException(OtcEnums.ORDER_USERID_EQUALS_AD_USERID); + } + } + + /*** + * 检查下单交易额的小数失真偏差 + * 偏差较小,返回交易额参数 + * 偏差较大,返回单价数量相乘的结果 + * @param turnover + * @param price + * @param amount + * @return + */ + private BigDecimal checkTurnoverDish(BigDecimal turnover, BigDecimal price, BigDecimal amount, Integer unitDecimal) { + //保留法币小数长度 + BigDecimal computeTurnover = amount.multiply(price).setScale(unitDecimal, BigDecimal.ROUND_DOWN); + //相减之后的绝对值作为偏差值 + BigDecimal dish = (turnover.subtract(computeTurnover)).abs(); + //如果偏差值较小,返回交易额参数 + if (dish.compareTo(DECIMAL_DISH) <= 0) { + return turnover; + } else { + //偏差值较大,返回单价数量相乘的结果 + return computeTurnover; + } + } + + /*** + * 检查卖家是否绑定买家选择的支付方式 + * @param userId + * @param pays + */ + private void checkSellUserPayIsBingding(String userId, String pays) { + //解析广告的支付方式 + String[] pay = pays.split(","); + //判断用户时候绑定广告支付方式标识 + boolean flag = false; + //循环 + for (String p : pay) { + UserPayInfo userPayInfo = userPayInfoService.selectByUserIdAndPayType(userId, p); + //如果绑定了其中一种,将标识改为true + if (userPayInfo != null) { + flag = true; + } + } + //循环结束,如果没有绑定任何一种广告的支付方式,抛出异常 + if (!flag) { + throw new OtcException(OtcEnums.ORDER_DEAL_AD_PAY_NOT_BINGDING); + } + } + + /*** + * 新增订单 + * @param ad + * @param userId + * @param amount + * @param turnover + * @param orderType + * @param chargeRatio 手续费 + * @return + */ + private Order insertOrder(Ad ad, String userId, BigDecimal amount, BigDecimal turnover, String orderType, BigDecimal chargeRatio) { + Order order = new Order(); + Date now = new Date(); + String orderId = UUID.randomUUID().toString(); + String orderNumber = createAdNumber(); + order.setId(orderId); + order.setOrderNumber(orderNumber); + order.setAdId(ad.getId()); + order.setCoinName(ad.getCoinName()); + order.setUnitName(ad.getUnitName()); + order.setAmount(amount); + order.setPrice(ad.getPrice()); + order.setTurnover(turnover); + order.setChargeRatio(chargeRatio); + order.setBuyStatus(OrderConstants.NEW); + //判断双方角色,true的时候orderType为买 + if (checkBuyOrSell(orderType)) { + order.setBuyUserId(userId); + order.setSellUserId(ad.getUserId()); + } else { + order.setBuyUserId(ad.getUserId()); + order.setSellUserId(userId); + } + order.setSellStatus(OrderConstants.NEW); +// order.setOrderPayType(); 下单时不需要选择支付方式 + order.setOrderStatus(OrderConstants.NEW); + order.setOrderType(orderType); + order.setCreateTime(now); + order.setModifyTime(now); + orderMapper.insertSelective(order); + return order; + } + + /*** + * 更新广告剩余数量 + * @param amount + * @param ad + * @param type + */ + private void updateAdLastNum(BigDecimal amount, Ad ad, String type) { + //下单 + if (type.equals(UserHandleConstants.DEAL)) { + //下单时将广告状态设置为交易中 + ad.setAdStatus(AdConstants.UNDERWAY); + //扣除数量 + ad.setLastNum(ad.getLastNum().subtract(amount)); + } + //撤单 + if (type.equals(UserHandleConstants.CANCEL)) { + //查询广告伞下是否还有未完成订单 + boolean flag = checkOrdersUnfinished(ad.getId()); + //false代表没有未完成订单,将广告设置为挂单中 + if (!flag) { + ad.setAdStatus(AdConstants.DEFAULT); + } + //增加数量 + ad.setLastNum(ad.getLastNum().add(amount)); + } + //变动后的最大限额 + BigDecimal maxLimit = ad.getLastNum().multiply(ad.getPrice()).setScale(CommonConstans.LIMIT_CNY_DECIMAL, BigDecimal.ROUND_DOWN); + //如果广告最小限额小于等于最大限额,更新广告最大限额 + if (ad.getMinLimit().compareTo(maxLimit) <= 0) { + ad.setMaxLimit(maxLimit); + } else { + //剩余数量不足以交易,下架广告 + ad.setAdStatus(AdConstants.PENDING); + } + ad.setModifyTime(new Date()); + //更新广告 + adService.updateByPrimaryKeySelective(ad); + } + + /*** + * 校验订单状态、类型是否可以撤单 + * @param orderId + * @param userId + */ + private Order checkCancelBuyOrder(String orderId, String userId) { + //订单id是否为空 + checkOrderIdNull(orderId); + //排他锁查询订单信息 + Order order = orderMapper.selectByIdForUpdate(orderId); + //订单是否存在 + checkOrderNull(order); + //订单买家和用户是否匹配 + if (!order.getBuyUserId().equals(userId)) { + throw new OtcException(OtcEnums.ORDER_CANCEL_USERID_NOT_EQUALS); + } + //买家状态不是新建或者已确认 + if (!order.getBuyStatus().equals(OrderConstants.NEW) && + !order.getBuyStatus().equals(OrderConstants.CONFIRM)) { + throw new OtcException(OtcEnums.ORDER_BUY_STATUS_CHANGES); + } + //卖家状态不是新建 + if (!order.getSellStatus().equals(OrderConstants.NEW)) { + throw new OtcException(OtcEnums.ORDER_SELL_STATUS_CHANGES); + } + //订单状态不是新建或者进行中 + if (!order.getOrderStatus().equals(OrderConstants.NEW) && + !order.getOrderStatus().equals(OrderConstants.UNDERWAY)) { + throw new OtcException(OtcEnums.ORDER_STATUS_CHANGES); + } + //订单类型不是买单 + if (!order.getOrderType().equals(CommonConstans.BUY)) { + throw new OtcException(OtcEnums.ORDER_CANCEL_TYPE_NOT_BUY); + } + + return order; + } + + /*** + * 更新广告发布方成交统计数据 + * @param order + */ + private void updateAdUserDealStats(Order order) { + //orderType等于买flag为true + boolean flag = checkBuyOrSell(order.getOrderType()); + //如果订单是买单,更新广告方成交卖单数量 + if (flag) { + //已完成卖单数量 + dealStatsService.updateOrderSellNum(order.getSellUserId()); + //更新广告发布方成交数量 + dealStatsService.updateAdMarkNum(order.getSellUserId()); + } else { + //如果订单是卖单,更新广告方成交买单数量 + //已完成买单数量 + dealStatsService.updateOrderBuyNum(order.getBuyUserId()); + //更新广告发布方成交数量 + dealStatsService.updateAdMarkNum(order.getBuyUserId()); + } + } + + /*** + * 更新订单状态 + * @param order + * @param handleType + */ + private void receiptOrPayUpdateOrder(Order order, String handleType) { + //确认付款操作时 + if (handleType.equals(UserHandleConstants.PAY)) { + //更新买家状态为已确认 + order.setBuyStatus(OrderConstants.CONFIRM); + //更新订单状态为进行中 + order.setOrderStatus(OrderConstants.UNDERWAY); + order.setModifyTime(new Date()); + } + //确认收款操作时 + if (handleType.equals(UserHandleConstants.RECEIPT)) { + //订单状态为申诉时,确认收款后将申诉记录失效 + if (order.getOrderStatus().equals(OrderConstants.APPEAL)) { + //将申诉记录改为已处理 + appealService.updateAppeal(order.getOrderNumber(), AppealConstants.HANDLE); + //新增申诉记录处理日志 + appealHandleLogService.insertAppealHandleLog(order.getOrderNumber(), + AppealConstants.RECEIPT_SYS_USER_ID, AppealConstants.RECEIPT_IP_ADDR, + OrderConstants.FINISH, AppealConstants.RECEIPT_REMARK); + } + //更新卖家状态为已确认 + order.setSellStatus(OrderConstants.CONFIRM); + //更新订单状态为已完成 + order.setOrderStatus(OrderConstants.FINISH); + order.setModifyTime(new Date()); + } + //更新订单 + orderMapper.updateByPrimaryKeySelective(order); + } + + /*** + * 检查确认付款时的支付类型参数 + * 跟广告设置的支付信息是否匹配 + * + * @param payType + * @param adPayType + */ + private void checkPayTypeIsAdPayType(String payType, String adPayType) { + //广告设置的支付信息 + String[] pay = adPayType.split(","); + //如果匹配其中一个,将状态改为true + boolean flag = false; + for (String p : pay) { + if (p.equals(payType)) { + flag = true; + } + } + //没有匹配其中一个,抛出异常 + if (!flag) { + throw new OtcException(OtcEnums.ORDER_PAY_ERROR); + } + } + + /*** + * 订单id是否为空 + * @param orderId + */ + private void checkOrderIdNull(String orderId) { + //id是否为空 + if (StringUtils.isBlank(orderId)) { + throw new OtcException(OtcEnums.ORDER_ID_NULL); + } + } + + /*** + * 订单是否为空 + * @param order + */ + private void checkOrderNull(Order order) { + //订单是否存在 + if (order == null) { + throw new OtcException(OtcEnums.ORDER_NULL); + } + } + + /*** + * 广告是否为空 + * @param ad + */ + private void checkAdNull(Ad ad) { + if (ad == null) { + throw new OtcException(OtcEnums.ORDER_AD_NULL); + } + } + + /*** + * 检查订单能否确认付款或确认收款 + * @param order + * @param userId + * @param handleType + */ + private void checkOrderCanHandle(Order order, String userId, String handleType) { + /*** + * 订单是否可以确认付款: + * 订单状态等于新建 √ + * 买家id等于操作用户id √ + * 买家状态等于新建 √ + */ + if (handleType.equals(UserHandleConstants.PAY)) { + if (!order.getOrderStatus().equals(OrderConstants.NEW)) { + throw new OtcException(OtcEnums.ORDER_STATUS_CHANGES); + } + //操作用户和买家id是否匹配 + if (!order.getBuyUserId().equals(userId)) { + throw new OtcException(OtcEnums.ORDER_PAY_USERID_NOT_EQUALS); + } + //买家状态是否可确认付款 + if (!order.getBuyStatus().equals(OrderConstants.NEW)) { + throw new OtcException(OtcEnums.ORDER_BUY_STATUS_CHANGES); + } + } + + /*** + * 订单状态是否可以确认收款: + * 订单状态等于进行中和申诉中 √ + * 卖家id等于操作用户id √ + * 卖家状态等于新建和申诉中 √ + */ + if (handleType.equals(UserHandleConstants.RECEIPT)) { + if (!order.getOrderStatus().equals(OrderConstants.UNDERWAY) && + !order.getOrderStatus().equals(OrderConstants.APPEAL)) { + throw new OtcException(OtcEnums.ORDER_STATUS_CHANGES); + } + //操作用户和卖家id是否匹配 + if (!order.getSellUserId().equals(userId)) { + throw new OtcException(OtcEnums.ORDER_PAY_USERID_NOT_EQUALS); + } + //卖家是否可以确认收款 + if (!order.getSellStatus().equals(OrderConstants.NEW) && + !order.getSellStatus().equals(OrderConstants.APPEAL)) { + throw new OtcException(OtcEnums.ORDER_SELL_STATUS_CHANGES); + } + } + } + + /*** + * 确认收款后更新余额并记录资金变动 + * @param order + */ + private void receiptUpdateBalance(Order order) { + Ad ad = adService.selectById(order.getAdId()); + //交易数量 + BigDecimal amount = order.getAmount(); + //手续费 + BigDecimal serviceCharge; + //买家实际获得数量 + BigDecimal realAmount; + //卖家解冻数量 + BigDecimal minusAmount; + + //true为买 + boolean flag = checkBuyOrSell(order.getOrderType()); + if (flag) { + //订单为买单-(买方是订单方,卖方是广告方) + //手续费 + serviceCharge = amount.multiply(order.getChargeRatio()); + //买家实际获得数量 + realAmount = amount; + //卖家解冻数量 + minusAmount = amount.add(amount.multiply(ad.getChargeRatio())).multiply(CommonConstans.MINUS_ONE); + } else { + //订单为卖单-(买方是广告方,卖方是订单方) + //手续费 + serviceCharge = amount.multiply(ad.getChargeRatio()); + //买家实际获得数量 + realAmount = amount.subtract(serviceCharge); + //卖家解冻数量 + minusAmount = amount.add(amount.multiply(order.getChargeRatio())).multiply(CommonConstans.MINUS_ONE); + } + + + //买家加款 + walletService.handleRealBalance(order.getBuyUserId(), order.getOrderNumber(), order.getCoinName(), order.getUnitName(), realAmount, BigDecimal.ZERO, serviceCharge); + //记录资金变动 + billService.insertBill(order.getBuyUserId(), order.getOrderNumber(), realAmount, BigDecimal.ZERO, BillConstants.MARK, order.getCoinName()); + //卖家解冻 + walletService.handleRealBalance(order.getSellUserId(), order.getOrderNumber(), order.getCoinName(), order.getUnitName(), BigDecimal.ZERO, minusAmount, BigDecimal.ZERO); + //记录资金变动 + billService.insertBill(order.getSellUserId(), order.getOrderNumber(), BigDecimal.ZERO, minusAmount, BillConstants.MARK, order.getCoinName()); + } + + /*** + * 判断广告是否能结束 + * @param adId + */ + private void checkAdCanFinish(String adId) { + //查询广告伞下是否还有未完成订单 + boolean unfinished = this.checkOrdersUnfinished(adId); + + //排他锁查询广告 + Ad ad = adService.selectByIdForUpdate(adId); + + //剩余交易额不足以交易 + if ((ad.getLastNum().multiply(ad.getPrice())).compareTo(ad.getMinLimit()) < 0) { + //有未完成订单,下架 + if (unfinished) { + ad.setAdStatus(AdConstants.PENDING); + } else {//没有未完成订单 + //falge为卖 + boolean flag = checkBuyOrSell(ad.getAdType()); + //广告是卖出类型时 并且 有剩余数量时,解冻余额 + if (!flag && ad.getLastNum().compareTo(BigDecimal.ZERO) > 0) { + //广告手续费 + BigDecimal serviceCharge = ad.getLastNum().multiply(ad.getChargeRatio()); + //增加可用 + BigDecimal freeBalance = ad.getLastNum().add(serviceCharge); + //扣除冻结 + BigDecimal freezeBalance = freeBalance.multiply(CommonConstans.MINUS_ONE); + //解冻余额 + walletService.handleBalance(ad.getUserId(), ad.getAdNumber(), ad.getCoinName(), ad.getUnitName(), freeBalance, freezeBalance); + //记录资金变动 + billService.insertBill(ad.getUserId(), ad.getAdNumber(), freeBalance, freezeBalance, BillConstants.CANCEL, ad.getCoinName()); + } + //已完成 + ad.setAdStatus(AdConstants.FINISH); + } + } else { + //还有剩余剩余数量 + //有未完成订单,设置为进行中 + if (unfinished) { + ad.setAdStatus(AdConstants.UNDERWAY); + } else { + //没有未完成订单,设置为挂单中 + ad.setAdStatus(AdConstants.DEFAULT); + } + } + + ad.setModifyTime(new Date()); + //更新广告 + adService.updateByPrimaryKeySelective(ad); + } + + /*** + * 生成广告随机流水号 + * @return + */ + private String createAdNumber() { + return System.currentTimeMillis() + UUID.randomUUID().toString().substring(0, 6).toUpperCase(); + } + + /*** + * 查询用户主体信息 + * @param userId + * @return + */ + private UserBaseDTO selectBaseUserByUserId(String userId) { + ResultDTO result = userFeign.selectUserInfoById(userId); + return result.getData(); + } + + /*** + * 判断是否开启订单手续费、用户是否免手续费 + * @param coinServiceCharge 币种手续费 + * @param userId + * @return + */ + private BigDecimal checkOrderServiceCharge(BigDecimal coinServiceCharge, String userId) { +// boolean flag = verifyFreeTransaction(userId); +// //用户免除手续费 +// if (flag) { +// return BigDecimal.ZERO; +// } +//// //查询订单手续费是否开启 +//// Config config = configService.selectByKey(ConfigConstants.ORDER_SERVICE_CHARGE); +// //查询卖家手续费是否开启 +// Config config = configService.selectByKey(ConfigConstants.SELL_SERVICE_CHARGE); +// //如果订单手续费配置为空或者配置状态为禁用 +// if (config == null || config.getStatus().equals(CommonConstans.NO)) { +// //返回0手续费 +// return BigDecimal.ZERO; +// } +// //返回手续费 +// return coinServiceCharge; + return BigDecimal.ZERO; + } + + /*** + * 查询用户是否免交易手续费 + * true是免手续费 + * @param userId + * @return + */ + private boolean verifyFreeTransaction(String userId) { + ResultDTO result = userFeign.verifyFreeTransaction(userId); + return result.getData(); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/UserHandleLogServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/UserHandleLogServiceImpl.java new file mode 100644 index 0000000..20d169c --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/UserHandleLogServiceImpl.java @@ -0,0 +1,32 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.server.otc.entity.UserHandleLog; +import com.blockchain.server.otc.mapper.UserHandleLogMapper; +import com.blockchain.server.otc.service.UserHandleLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.UUID; + +@Service +public class UserHandleLogServiceImpl implements UserHandleLogService { + + @Autowired + private UserHandleLogMapper userHandleLogMapper; + + @Override + @Transactional + public int insertUserHandleLog(String userId, String handleNumber, String handleType, String handleNumberType) { + UserHandleLog userHandleLog = new UserHandleLog(); + Date now = new Date(); + userHandleLog.setId(UUID.randomUUID().toString()); + userHandleLog.setUserId(userId); + userHandleLog.setHandleNumber(handleNumber); + userHandleLog.setHandleType(handleType); + userHandleLog.setHandleNumberType(handleNumberType); + userHandleLog.setCreateTime(now); + return userHandleLogMapper.insertSelective(userHandleLog); + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/UserPayInfoServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/UserPayInfoServiceImpl.java new file mode 100644 index 0000000..400fbf5 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/UserPayInfoServiceImpl.java @@ -0,0 +1,182 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.server.otc.common.constant.OrderConstants; +import com.blockchain.server.otc.common.constant.UserPayConstants; +import com.blockchain.server.otc.common.enums.OtcEnums; +import com.blockchain.server.otc.common.exception.OtcException; +import com.blockchain.server.otc.entity.Ad; +import com.blockchain.server.otc.entity.Order; +import com.blockchain.server.otc.entity.UserPayInfo; +import com.blockchain.server.otc.mapper.UserPayInfoMapper; +import com.blockchain.server.otc.service.AdService; +import com.blockchain.server.otc.service.OrderService; +import com.blockchain.server.otc.service.UserPayInfoService; +import com.blockchain.server.otc.service.WalletService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +@Service +public class UserPayInfoServiceImpl implements UserPayInfoService { + + @Autowired + private UserPayInfoMapper userPayInfoMapper; + @Autowired + private OrderService orderService; + @Autowired + private AdService adService; + @Autowired + private WalletService walletService; + + @Override + public UserPayInfo selectByUserIdAndPayType(String userId, String payType) { + return userPayInfoMapper.selectByUserIdAndPayType(userId, payType); + } + + @Override + public List listUserPay(String userId) { + return userPayInfoMapper.listByUserId(userId); + } + + @Override + public List selectSellUserPayInfoByAdPayType(String userId, String orderId) { + Order order = orderService.selectById(orderId); + //返回参数 + List userPayInfos = new ArrayList<>(); + //订单状态新建时,查询广告的支付方法 + if (order.getOrderStatus().equals(OrderConstants.NEW)) { + //查询广告 + Ad ad = adService.selectById(order.getAdId()); + //获取广告设置的支付信息 + String[] pays = ad.getAdPay().split(","); + //遍历查询支付信息,添加到集合中 + for (String pay : pays) { + UserPayInfo userPayInfo = selectByUserIdAndPayType(order.getSellUserId(), pay); + //防空 + if (userPayInfo == null) { + continue; + } + userPayInfos.add(userPayInfo); + } + } else { + //其他状态时,查询确认付款后选择的支付方式 + UserPayInfo userPayInfo = selectByUserIdAndPayType(order.getSellUserId(), order.getOrderPayType()); + userPayInfos.add(userPayInfo); + } + + return userPayInfos; + } + + @Override + @Transactional + public int insertWXorZFB(String userId, String payType, String accountInfo, String codeUrl, String pass) { + //判空 + if (StringUtils.isBlank(accountInfo)) { + throw new OtcException(OtcEnums.USER_PAY_ACCOUNTINFO_NULL); + } + if (StringUtils.isBlank(codeUrl)) { + throw new OtcException(OtcEnums.USER_PAY_CODE_URL_NULL); + } + //判断是否已存在 + checkUserPayIsExist(userId, payType); + //校验密码 + walletService.isPassword(pass); + UserPayInfo userPayInfo = new UserPayInfo(); + Date now = new Date(); + userPayInfo.setUserId(userId); + userPayInfo.setId(UUID.randomUUID().toString()); + userPayInfo.setAccountInfo(accountInfo); + userPayInfo.setCollectionCodeUrl(codeUrl); + userPayInfo.setPayType(payType); + userPayInfo.setCreateTime(now); + userPayInfo.setModifyTime(now); + return userPayInfoMapper.insertSelective(userPayInfo); + } + + @Override + @Transactional + public int insertBank(String userId, String bankNumber, String bankUserName, String bankType, String pass) { + //判空 + if (StringUtils.isBlank(bankNumber)) { + throw new OtcException(OtcEnums.USER_PAY_BANK_NUMBER_NULL); + } + if (StringUtils.isBlank(bankUserName)) { + throw new OtcException(OtcEnums.USER_PAY_BANK_USER_NAME_NULL); + } + if (StringUtils.isBlank(bankType)) { + throw new OtcException(OtcEnums.USER_PAY_BANK_TYPE_NULL); + } + //判断是否已存在 + checkUserPayIsExist(userId, UserPayConstants.BANK); + //校验密码 + walletService.isPassword(pass); + UserPayInfo userPayInfo = new UserPayInfo(); + Date now = new Date(); + userPayInfo.setUserId(userId); + userPayInfo.setId(UUID.randomUUID().toString()); + userPayInfo.setBankNumber(bankNumber); + userPayInfo.setBankUserName(bankUserName); + userPayInfo.setBankType(bankType); + userPayInfo.setPayType(UserPayConstants.BANK); + userPayInfo.setCreateTime(now); + userPayInfo.setModifyTime(now); + return userPayInfoMapper.insertSelective(userPayInfo); + } + + @Override + @Transactional + public int updateWXorZFB(String userId, String payType, String accountInfo, String codeUrl, String pass) { + UserPayInfo userPayInfo = checkUserPayIsNull(userId, payType); + //校验密码 + walletService.isPassword(pass); + userPayInfo.setAccountInfo(accountInfo); + userPayInfo.setCollectionCodeUrl(codeUrl); + userPayInfo.setModifyTime(new Date()); + return userPayInfoMapper.updateByPrimaryKeySelective(userPayInfo); + } + + @Override + @Transactional + public int updateBank(String userId, String bankNumber, String bankUserName, String bankType, String pass) { + UserPayInfo userPayInfo = checkUserPayIsNull(userId, UserPayConstants.BANK); + //校验密码 + walletService.isPassword(pass); + userPayInfo.setBankNumber(bankNumber); + userPayInfo.setBankUserName(bankUserName); + userPayInfo.setBankType(bankType); + userPayInfo.setModifyTime(new Date()); + return userPayInfoMapper.updateByPrimaryKeySelective(userPayInfo); + } + + /*** + * 判断用户对应支付信息是否 '已存在' + * @param userId + * @param payType + */ + private void checkUserPayIsExist(String userId, String payType) { + UserPayInfo isExist = selectByUserIdAndPayType(userId, payType); + if (isExist != null) { + throw new OtcException(OtcEnums.USER_PAY_INSERT_EXIST); + } + } + + /*** + * 判断用户对应支付信息是否 '不存在' + * @param userId + * @param payType + * @return + */ + private UserPayInfo checkUserPayIsNull(String userId, String payType) { + UserPayInfo isNull = selectByUserIdAndPayType(userId, payType); + if (isNull == null) { + throw new OtcException(OtcEnums.USER_PAY_INSERT_EXIST); + } + return isNull; + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/WalletServiceImpl.java b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/WalletServiceImpl.java new file mode 100644 index 0000000..267ade1 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/java/com/blockchain/server/otc/service/impl/WalletServiceImpl.java @@ -0,0 +1,125 @@ +package com.blockchain.server.otc.service.impl; + +import com.blockchain.common.base.dto.WalletChangeDTO; +import com.blockchain.common.base.dto.WalletOrderDTO; +import com.blockchain.server.otc.common.enums.OtcEnums; +import com.blockchain.server.otc.common.exception.OtcException; +import com.blockchain.server.otc.entity.Coin; +import com.blockchain.server.otc.feign.BTCFeign; +import com.blockchain.server.otc.feign.EOSFeign; +import com.blockchain.server.otc.feign.ETHFeign; +import com.blockchain.server.otc.service.CoinService; +import com.blockchain.server.otc.service.WalletService; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; + +@Service +public class WalletServiceImpl implements WalletService { + + private static final Logger LOG = LoggerFactory.getLogger(WalletServiceImpl.class); + + @Autowired + private BTCFeign btcFeign; + @Autowired + private EOSFeign eosFeign; + @Autowired + private ETHFeign ethFeign; + @Autowired + private CoinService coinService; + + //主网标识 + private static final String BTC_NET = "BTC"; + private static final String ETH_NET = "ETH"; + private static final String EOS_NET = "EOS"; + + //法币交易调用钱包Feign调用应用标识 + private static final String C2C_APP = "C2C"; + + @Override + @Transactional + public void handleBalance(String userId, String publishId, String coinName, String unitName, BigDecimal freeBalance, BigDecimal freezeBalance) { + WalletOrderDTO order = new WalletOrderDTO(); + order.setUserId(userId); + order.setRecordId(publishId); + order.setTokenName(coinName); + order.setWalletType(C2C_APP); + order.setFreeBalance(freeBalance); + order.setFreezeBalance(freezeBalance); + LOG.info("调用钱包Feign,入参: order :" + order.toString() + " unitName :" + unitName); + Coin coin = selectByCoinAndUnit(coinName, unitName); + //根据主网标识,区分微服务调用 + switch (coin.getCoinNet()) { + case BTC_NET: + btcFeign.order(order); + break; + case ETH_NET: + ethFeign.order(order); + break; + case EOS_NET: + eosFeign.order(order); + break; + default: + LOG.error("更新余额失败,钱包处理出现未知主网标识:" + coin.getCoinNet()); + throw new OtcException(OtcEnums.WALLET_COIN_NET_ERROR); + } + } + + @Override + @Transactional + public void handleRealBalance(String userId, String recordId, String coinName, String unitName, BigDecimal freeBalance, BigDecimal freezeBalance, BigDecimal gasBalance) { + WalletChangeDTO change = new WalletChangeDTO(); + change.setUserId(userId); + change.setRecordId(recordId); + change.setTokenName(coinName); + change.setFreeBalance(freeBalance); + change.setFreezeBalance(freezeBalance); + change.setGasBalance(gasBalance); + change.setWalletType(C2C_APP); + LOG.info("调用钱包Feign,入参: change :" + change.toString() + " unitName :" + unitName); + Coin coin = selectByCoinAndUnit(coinName, unitName); + //根据主网标识,区分微服务调用 + switch (coin.getCoinNet()) { + case BTC_NET: + btcFeign.change(change); + break; + case ETH_NET: + ethFeign.change(change); + break; + case EOS_NET: + eosFeign.change(change); + break; + default: + LOG.error("扣款或加钱失败,钱包处理出现未知主网标识:" + coin.getCoinNet()); + throw new OtcException(OtcEnums.WALLET_COIN_NET_ERROR); + } + } + + @Override + public void isPassword(String pass) { + LOG.info("校验密码,参数:" + pass); +// if (StringUtils.isBlank(pass)) { TODO +// throw new OtcException(OtcEnums.PASS_NULL); +// } +// ethFeign.isPassword(pass); + } + + /*** + * 查询代币信息 + * @param coinName + * @param unitName + * @return + */ + private Coin selectByCoinAndUnit(String coinName, String unitName) { + Coin coin = coinService.selectByCoinAndUnit(coinName, unitName); + if (coin == null) { + throw new OtcException(OtcEnums.COIN_NULL); + } + return coin; + } +} diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/application.yml b/blockchain-server/blockchain-server-otc/src/main/resources/application.yml new file mode 100644 index 0000000..95aad49 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/application.yml @@ -0,0 +1,3 @@ +#日志配置路径 +logging: + config: classpath:logback/logback-${spring.cloud.config.profile}.xml \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/bootstrap.yml b/blockchain-server/blockchain-server-otc/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..59ac1c6 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/bootstrap.yml @@ -0,0 +1,39 @@ +##注册中心 +#eureka: +# client: +# service-url: +# defaultZone: http://localhost:8001/eureka/ +#spring: +# cloud: +# #配置中心 +# config: +# discovery: +# service-id: dapp-config-server +# enabled: true +# profile: dev +# name: springconf,springcloudconf,redisconf,dbconf,txconf,xssconf,ipconf,fileconf +# application: +# name: dapp-otc-server + +server: + port: 8701 +#注册中心 +eureka: + client: + service-url: + defaultZone: http://eureka:8001/eureka/ + instance: + prefer-ip-address: true + instance-id: ${spring.cloud.client.ip-address}:${server.port} + hostname: ${spring.cloud.client.ip-address} +spring: + cloud: + #配置中心 + config: + discovery: + service-id: dapp-config-server + enabled: true + profile: dev + name: springconf,springcloudconf,redisconf,dbconf,txconf,xssconf,ipconf,fileconf + application: + name: dapp-otc-server \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/logback/logback-dev.xml b/blockchain-server/blockchain-server-otc/src/main/resources/logback/logback-dev.xml new file mode 100644 index 0000000..9a0e324 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/logback/logback-dev.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/logback/logback-pro.xml b/blockchain-server/blockchain-server-otc/src/main/resources/logback/logback-pro.xml new file mode 100644 index 0000000..5b55059 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/logback/logback-pro.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/AdMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/AdMapper.xml new file mode 100644 index 0000000..dd55b6a --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/AdMapper.xml @@ -0,0 +1,132 @@ + + + + + otc_ad + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealDetailMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealDetailMapper.xml new file mode 100644 index 0000000..94b8419 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealDetailMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealHandleLogMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealHandleLogMapper.xml new file mode 100644 index 0000000..58fd7a8 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealHandleLogMapper.xml @@ -0,0 +1,31 @@ + + + + + otc_appeal_handle_log + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealImgMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealImgMapper.xml new file mode 100644 index 0000000..2727bdc --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealImgMapper.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealMapper.xml new file mode 100644 index 0000000..dedd75e --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/AppealMapper.xml @@ -0,0 +1,24 @@ + + + + + otc_appeal + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/BillMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/BillMapper.xml new file mode 100644 index 0000000..edf1332 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/BillMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/CoinMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/CoinMapper.xml new file mode 100644 index 0000000..ac41ee6 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/CoinMapper.xml @@ -0,0 +1,59 @@ + + + + + otc_coin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/ConfigMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/ConfigMapper.xml new file mode 100644 index 0000000..422877d --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/ConfigMapper.xml @@ -0,0 +1,25 @@ + + + + + otc_config + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/DealStatsMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/DealStatsMapper.xml new file mode 100644 index 0000000..432254e --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/DealStatsMapper.xml @@ -0,0 +1,51 @@ + + + + + otc_deal_stats + + + + + + + + + + + + + UPDATE + + SET + ad_trans_num = ad_trans_num + 1, + modify_time = #{modifyTime} + + + + UPDATE + + SET + ad_mark_num = ad_mark_num + 1, + modify_time = #{modifyTime} + + + + UPDATE + + SET + order_sell_num = order_sell_num + 1, + modify_time = #{modifyTime} + + + + UPDATE + + SET + order_buy_num = order_buy_num + 1, + modify_time = #{modifyTime} + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketApplyHandleLogMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketApplyHandleLogMapper.xml new file mode 100644 index 0000000..8455b4c --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketApplyHandleLogMapper.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketApplyMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketApplyMapper.xml new file mode 100644 index 0000000..2e589fd --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketApplyMapper.xml @@ -0,0 +1,29 @@ + + + + + otc_market_apply + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketFreezeMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketFreezeMapper.xml new file mode 100644 index 0000000..6533f4d --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketFreezeMapper.xml @@ -0,0 +1,25 @@ + + + + + otc_market_freeze + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketUserHandleLogMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketUserHandleLogMapper.xml new file mode 100644 index 0000000..101ca69 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketUserHandleLogMapper.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketUserMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketUserMapper.xml new file mode 100644 index 0000000..eae4ea8 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/MarketUserMapper.xml @@ -0,0 +1,24 @@ + + + + + otc_market_user + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/OrderMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/OrderMapper.xml new file mode 100644 index 0000000..8f9ab49 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/OrderMapper.xml @@ -0,0 +1,113 @@ + + + + + otc_order + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/UserHandleLogMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/UserHandleLogMapper.xml new file mode 100644 index 0000000..92491ae --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/UserHandleLogMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-otc/src/main/resources/mapper/UserPayInfoMapper.xml b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/UserPayInfoMapper.xml new file mode 100644 index 0000000..585c496 --- /dev/null +++ b/blockchain-server/blockchain-server-otc/src/main/resources/mapper/UserPayInfoMapper.xml @@ -0,0 +1,35 @@ + + + + + otc_user_pay_info + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/pom.xml b/blockchain-server/blockchain-server-sysconf/pom.xml new file mode 100644 index 0000000..d9f52be --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/pom.xml @@ -0,0 +1,29 @@ + + + + blockchain-server + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + blockchain-server-sysconf + + + com.blockchain + blockchain-server-base + 1.0-SNAPSHOT + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/SysConfigApplication.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/SysConfigApplication.java new file mode 100644 index 0000000..4b16b57 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/SysConfigApplication.java @@ -0,0 +1,19 @@ +package com.blockchain.server.sysconf; + +import com.blockchain.server.base.BaseConf; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.openfeign.EnableFeignClients; +import tk.mybatis.spring.annotation.MapperScan; + +/** + * @author huangxl + * @create 2018-11-13 10:15 + */ +@SpringBootApplication(scanBasePackageClasses = {BaseConf.class, SysConfigApplication.class}) +public class SysConfigApplication { + public static void main(String[] args) { + SpringApplication.run(SysConfigApplication.class,args); + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/config/InterceptorConf.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/config/InterceptorConf.java new file mode 100644 index 0000000..c7f28b3 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/config/InterceptorConf.java @@ -0,0 +1,14 @@ +package com.blockchain.server.sysconf.common.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class InterceptorConf implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**").allowedOrigins("*"); + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/AgreementConstant.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/AgreementConstant.java new file mode 100644 index 0000000..e8c8e0f --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/AgreementConstant.java @@ -0,0 +1,10 @@ +package com.blockchain.server.sysconf.common.constants; + +public class AgreementConstant { + public static final String TYPE_USER = "user";//用户协议 + public static final String TYPE_PRIVACY = "privacy";//隐私协议 + public static final String TYPE_BUSINESS = "business";//商家协议 + + //联系我们排序 + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/ContactUsConstant.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/ContactUsConstant.java new file mode 100644 index 0000000..dad2082 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/ContactUsConstant.java @@ -0,0 +1,9 @@ +package com.blockchain.server.sysconf.common.constants; + +public class ContactUsConstant { + public static final int STATUS_SHOW = 1;//显示 + public static final int STATUS_HIDE = 0;//隐藏 + + //联系我们排序 + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/ImageConstant.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/ImageConstant.java new file mode 100644 index 0000000..c01a5a9 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/ImageConstant.java @@ -0,0 +1,14 @@ +package com.blockchain.server.sysconf.common.constants; + +/** + * @author: Liusd + * @create: 2019-04-10 16:25 + **/ +public class ImageConstant { + public static final String STATUS_SHOW = "Y";//显示 + public static final String STATUS_HIDE = "N";//隐藏 + + public static final String TYPE_PC = "PC"; + + public static final String TYPE_APP = "APP"; +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/NewsConstant.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/NewsConstant.java new file mode 100644 index 0000000..9114302 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/NewsConstant.java @@ -0,0 +1,12 @@ +package com.blockchain.server.sysconf.common.constants; + +public class NewsConstant { + + public static final Integer VALI_TITLE_MAX_LENGTH = 40; + + public static final Integer VALI_URL_MAX_LENGTH = 200; + + public static final Integer TYPE_PUBLIC_NEWS = 1; //资讯 + + public static final Integer TYPE_FAST_NEWS = 2; //行业资讯 +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/NoticeConstant.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/NoticeConstant.java new file mode 100644 index 0000000..a51312d --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/NoticeConstant.java @@ -0,0 +1,10 @@ +package com.blockchain.server.sysconf.common.constants; + +/** + * @author: Liusd + * @create: 2019-04-10 16:25 + **/ +public class NoticeConstant { + public static final String STATUS_SHOW = "Y";//显示 + public static final String STATUS_HIDE = "N";//隐藏 +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/UserConstant.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/UserConstant.java new file mode 100644 index 0000000..bc5a517 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/UserConstant.java @@ -0,0 +1,23 @@ +package com.blockchain.server.sysconf.common.constants; + +public class UserConstant { + + public static final int ENABLE_GP = 1;//开启手势密码 + public static final int DISABLE_GP = 0;//关闭手势密码 + + public static final int USER_SEX_MALE = 1; //男性 + + public static final int USER_SEX_FEMALE = 0; //女性 + + public static final int USER_NICKNAME_MAX_LENGTH = 30; //昵称默认长度 + + public static final String USER_LOCAL_CHINA = "zh_CN"; //中文语种 + + public static final String USER_LOCAL_ENG = "en_US"; //英文语种 + + public static final int STATUS_DISABLE = 0; //禁用 + public static final int STATUS_DEFAULT = 1; //默认 + public static final int STATUS_AUTHENTICATED = 2;//已认证 + public static final int STATUS_MAKER = 3;//市商认证 + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/VersionConstant.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/VersionConstant.java new file mode 100644 index 0000000..4914e60 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/constants/VersionConstant.java @@ -0,0 +1,14 @@ +package com.blockchain.server.sysconf.common.constants; + +/** + * 常量 + */ +public class VersionConstant { + + + /** + * 设备型号(系统型号) + */ + public static final String SYSTEMTYPE_IOS = "ios"; + public static final String SYSTEMTYPE_ANDROID = "android"; +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/enums/SysConfigEnums.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/enums/SysConfigEnums.java new file mode 100644 index 0000000..4b7fa75 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/enums/SysConfigEnums.java @@ -0,0 +1,27 @@ +package com.blockchain.server.sysconf.common.enums; + +import lombok.Getter; + +/** + * @author huangxl + * @create 2018-11-22 09:29 + */ +public enum SysConfigEnums { + NOTICE_DOES_NOT_EXIST(1009, "公告不存在","no motice"), + IMAGE_DOES_NOT_EXIST(1010, "轮播图不存在","no image"), + IS_STAR(1012, "不可重复点赞","Non repeatable thumb up"), + FILE_UPLOAD_ERROR(1011, "文件上传失败","file upload error"); + + @Getter + private int code; + @Getter + private String msg; + @Getter + private String enMsg; + + private SysConfigEnums(int code, String msg, String enMsg) { + this.code = code; + this.msg = msg; + this.enMsg = enMsg; + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/exception/SysConfigException.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/exception/SysConfigException.java new file mode 100644 index 0000000..3174725 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/exception/SysConfigException.java @@ -0,0 +1,33 @@ +package com.blockchain.server.sysconf.common.exception; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.sysconf.common.enums.SysConfigEnums; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author huangxl + * @create 2018-11-22 09:29 + */ +public class SysConfigException extends BaseException { + + public SysConfigException(SysConfigEnums rs) { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + String userLocale = HttpRequestUtil.getUserLocale(request); + String msg; + switch (userLocale) { + case BaseConstant.USER_LOCALE_EN_US: + msg = rs.getEnMsg(); + break; + default: + msg = rs.getMsg(); + break; + } + this.code = rs.getCode(); + this.msg = msg; + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/util/CheckUtils.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/util/CheckUtils.java new file mode 100644 index 0000000..e1ee23e --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/common/util/CheckUtils.java @@ -0,0 +1,164 @@ +package com.blockchain.server.sysconf.common.util; + +import org.apache.commons.lang3.StringUtils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * \*

Desciption:数据校检验证工具类

+ * \* + * \* CreateTime: 2018/8/10 0010 15:49 + * \* User: YeHao + * \ + */ +public class CheckUtils { + /** + * 校验手机号 + * + * @param phoneNum 手机号 + * @return + */ + public static boolean checkMobilePhone(String phoneNum) { + if (!StringUtils.isEmpty(phoneNum)) { + String reg = "1[3-9]\\d{9}"; + return phoneNum.matches(reg); + } + return false; + } + + /** + * 验证电话号码 + * + * @param phoneNum 电话 + * @return + */ + public static boolean checkPhone(String phoneNum) { + + boolean flag = false; + if (StringUtils.isEmpty(phoneNum)) + return flag; + if (phoneNum.contains("-")) { //固定电话带区号 3-4 + String reg = "0[1-9]{2,3}-\\d{7,8}"; + flag = phoneNum.matches(reg); + } else if (phoneNum.contains("+")) { // + 3-5区号 加手机号 + String reg1 = "\\+[1-9]\\d{2,4}1[3-9]\\d{9}"; //手机号 + String reg2 = "\\+[1-9]\\d{2,4}[1-9]\\d{6,7}"; //固定电话 + flag = phoneNum.matches(reg1) || phoneNum.matches(reg2); + } else { + String reg1 = "[1-9]\\d{6,7}"; //固定电话不带区号 7-8 + String reg2 = "1[3-9]\\d{9}"; //手机号 + flag = phoneNum.matches(reg1) || phoneNum.matches(reg2); + } + return flag; + } + + /** + * 验证邮箱地址是否正确 + * + * @param email 邮箱 + * @return + */ + public static boolean checkEmail(String email) { + + boolean flag = false; + try { + // String check = + // "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; + String check = "^([a-z0-9A-Z]+[-|.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?.)+[a-zA-Z]{2,}$"; + Pattern regex = Pattern.compile(check); + Matcher matcher = regex.matcher(email); + flag = matcher.matches(); + } catch (Exception e) { + flag = false; + } + return flag; + } + + /** + * 校验微信账号 + * + * @param wxCode 微信账号 + * @return + */ + public static boolean checkWeixin(String wxCode) { + + boolean flag = false; + if (!StringUtils.isEmpty(wxCode)) { + + if (!StringUtils.isEmpty(wxCode)) { + if (wxCode.contains("@")) { //验证邮箱号 + String check = "^([a-z0-9A-Z]+[-|.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?.)+[a-zA-Z]{2,}$"; + Pattern regex = Pattern.compile(check); + Matcher matcher = regex.matcher(wxCode); + flag = matcher.matches(); + } else { + String reg1 = "[1-9]\\d{5,19}"; //qq号 6 - 20 + String reg2 = "1[3-9]\\d{9}"; //qq号或者手机号 11 + String reg3 = "[a-zA-Z][-_a-zA-Z0-9]{5,19}"; //微信号带字母的 6-20 + flag = wxCode.matches(reg1) || wxCode.matches(reg2) || wxCode.matches(reg3); + } + } + + } + return flag; + + } + + /** + * 校验支付宝账号 + * + * @param zgbCode 支付宝账号 + * @return + */ + public static boolean checkZhiFuBao(String zgbCode) { + boolean flag = false; + if (checkEmail(zgbCode) || checkWeixin(zgbCode)) { + flag = true; + } + return flag; + } + + /** + * 校检银行卡号 + * + * @param bankCard 银行卡号 + * @return + */ + public static boolean checkBankCard(String bankCard) { + if (bankCard.length() < 15 || bankCard.length() > 19) { + return false; + } + char bit = getBankCardCheckCode(bankCard.substring(0, bankCard.length() - 1)); + if (bit == 'N') { + return false; + } + return bankCard.charAt(bankCard.length() - 1) == bit; + } + + /** + * 从不含校验位的银行卡卡号采用 Luhm 校验算法获得校验位 + * + * @param nonCheckCodeBankCard + * @return + */ + private static char getBankCardCheckCode(String nonCheckCodeBankCard) { + if (nonCheckCodeBankCard == null || nonCheckCodeBankCard.trim().length() == 0 + || !nonCheckCodeBankCard.matches("\\d+")) { + //如果传的不是数据返回N + return 'N'; + } + char[] chs = nonCheckCodeBankCard.trim().toCharArray(); + int luhmSum = 0; + for (int i = chs.length - 1, j = 0; i >= 0; i--, j++) { + int k = chs[i] - '0'; + if (j % 2 == 0) { + k *= 2; + k = k / 10 + k % 10; + } + luhmSum += k; + } + return (luhmSum % 10 == 0) ? '0' : (char) ((10 - luhmSum % 10) + '0'); + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/AboutUsController.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/AboutUsController.java new file mode 100644 index 0000000..1ba7ec2 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/AboutUsController.java @@ -0,0 +1,39 @@ +package com.blockchain.server.sysconf.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.sysconf.controller.api.AboutUsApi; +import com.blockchain.server.sysconf.entity.AboutUs; +import com.blockchain.server.sysconf.service.AboutUsService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Api(AboutUsApi.ABOUTUS_API) +@RestController +@RequestMapping("/aboutUs") +public class AboutUsController { + + private static final Logger LOGGER = LoggerFactory.getLogger(AboutUsController.class); + + @Autowired + private AboutUsService aboutUsService; + + /** + * 查询关于我们信息 (客户端) + * @return + */ + @ApiOperation(value = AboutUsApi.FINDABOUTUS.METHOD_TITLE_NAME, notes = AboutUsApi.FINDABOUTUS.METHOD_TITLE_NOTE) + @RequestMapping(value = "/findAboutUs", method = RequestMethod.POST) + public ResultDTO findAboutUs(@ApiParam(AboutUsApi.FINDABOUTUS.METHOD_API_LANGUAGES) @RequestParam("languages") String languages) { + AboutUs aboutUs = aboutUsService.findNewestAboutUs(languages); + return ResultDTO.requstSuccess(aboutUs); + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/AdSliderController.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/AdSliderController.java new file mode 100644 index 0000000..ef67e8e --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/AdSliderController.java @@ -0,0 +1,32 @@ +package com.blockchain.server.sysconf.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.sysconf.controller.api.AdSliderApi; +import com.blockchain.server.sysconf.service.AdSliderService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Api(AdSliderApi.AD_SLIDER_API) +@RestController +@RequestMapping("/adSlider") +public class AdSliderController { + + @Autowired + private AdSliderService adSliderService; + + /** + * 获取首页轮播图列表 + * + * @return + */ + @ApiOperation(value = AdSliderApi.ListAdSlider.METHOD_TITLE_NAME, notes = AdSliderApi.ListAdSlider.METHOD_TITLE_NOTE) + @GetMapping("/listAdSlider") + public ResultDTO listAdSlider() { + return ResultDTO.requstSuccess(adSliderService.listAdSliderForApp()); + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/AgreementController.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/AgreementController.java new file mode 100644 index 0000000..6984cef --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/AgreementController.java @@ -0,0 +1,45 @@ +package com.blockchain.server.sysconf.controller; + + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.sysconf.common.constants.AgreementConstant; +import com.blockchain.server.sysconf.controller.api.AgreementApi; +import com.blockchain.server.sysconf.controller.api.SysconfImageApi; +import com.blockchain.server.sysconf.entity.Agreement; +import com.blockchain.server.sysconf.service.AgreementService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + + +@Api(AgreementApi.AGREEMENT_API) +@RestController +@RequestMapping("/agreement") +public class AgreementController { + + private static final Logger LOGGER = LoggerFactory.getLogger(AgreementController.class); + + @Autowired + private AgreementService agreementService; + /** + * 查询用户协议 (客户端) + * @return + */ + @ApiOperation(value = AgreementApi.FINDAGREEMENT.METHOD_TITLE_NAME, notes = AgreementApi.FINDAGREEMENT.METHOD_TITLE_NOTE) + @RequestMapping(value = "/findAgreement", method = RequestMethod.GET) + public ResultDTO findAgreement(@ApiParam(SysconfImageApi.SystemImageList.METHOD_API_TYPE) @RequestParam(value = "type", defaultValue = AgreementConstant.TYPE_USER, required = false) String type, HttpServletRequest request){ + Agreement agreement = agreementService.findAgreement(type,HttpRequestUtil.getUserLocale(request)); + return ResultDTO.requstSuccess(agreement); + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/ContactUsController.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/ContactUsController.java new file mode 100644 index 0000000..ed4b123 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/ContactUsController.java @@ -0,0 +1,39 @@ +package com.blockchain.server.sysconf.controller; + + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.sysconf.common.constants.ContactUsConstant; +import com.blockchain.server.sysconf.controller.api.ContactUsApi; +import com.blockchain.server.sysconf.entity.ContactUs; +import com.blockchain.server.sysconf.service.ContactUsService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +@Api(ContactUsApi.CONTACTUS_API) +@RestController +@RequestMapping("/contactUs") +public class ContactUsController { + @Autowired + private ContactUsService contactUsService; + + /** + * 查询联系我们信息列表 + * + * @return + */ + @ApiOperation(value = ContactUsApi.FINDCONTACTUSALL.METHOD_TITLE_NAME, notes = ContactUsApi.FINDCONTACTUSALL.METHOD_TITLE_NOTE) + @RequestMapping(value = "/findContactUsAll", method = RequestMethod.POST) + public ResultDTO findContactUsAll(HttpServletRequest request) { + String userLocale = HttpRequestUtil.getUserLocale(request); + List contactUsList = contactUsService.listAll(ContactUsConstant.STATUS_SHOW, userLocale); + return ResultDTO.requstSuccess(contactUsList); + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/HelpCenterController.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/HelpCenterController.java new file mode 100644 index 0000000..d03c0bc --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/HelpCenterController.java @@ -0,0 +1,59 @@ +package com.blockchain.server.sysconf.controller; + + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.sysconf.controller.api.HelpCenterApi; +import com.blockchain.server.sysconf.service.HelpCenterService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +@Api(HelpCenterApi.HELPCENTER_API) +@RestController +@RequestMapping("/helpCenter") +public class HelpCenterController { + + + @Autowired + private HelpCenterService helpCenterService; + + /** + * 查询帮助中心列表,手机端 + * @return + */ + @ApiOperation(value = HelpCenterApi.SELECTHELPCENTERFORAPP.METHOD_TITLE_NAME, notes = HelpCenterApi.SELECTHELPCENTERFORAPP.METHOD_TITLE_NOTE) + @RequestMapping(value = "/selectHelpCenterForApp", method = RequestMethod.POST) + public ResultDTO selectHelpCenterForApp(HttpServletRequest request) { + String userLocale = HttpRequestUtil.getUserLocale(request); + return ResultDTO.requstSuccess(helpCenterService.selectHelpCenterForApp(userLocale)); + } + /** + * 查询帮助中心列表,PC端 + * @return + */ + @ApiOperation(value = HelpCenterApi.SELECTHELPCENTERFORAPP.METHOD_TITLE_NAME, notes = HelpCenterApi.SELECTHELPCENTERFORAPP.METHOD_TITLE_NOTE) + @RequestMapping(value = "/selectHelpCenterForPc", method = RequestMethod.POST) + public ResultDTO selectHelpCenterForPc(@ApiParam(HelpCenterApi.SELECTHELPCENTERFORPC.METHOD_API_TITLE) @RequestParam(required = false,value = "title") String title,HttpServletRequest request) { + String userLocale = HttpRequestUtil.getUserLocale(request); + return ResultDTO.requstSuccess(helpCenterService.selectHelpCenterForPc(title,userLocale)); + } + + /** + * 根据id查询帮助中心内容 + * @return + */ + @ApiOperation(value = HelpCenterApi.SELECTCONTENTBYID.METHOD_TITLE_NAME, notes = HelpCenterApi.SELECTCONTENTBYID.METHOD_TITLE_NOTE) + @RequestMapping(value = "/selectContentById", method = RequestMethod.POST) + public ResultDTO selectContentById(@ApiParam(HelpCenterApi.SELECTCONTENTBYID.METHOD_API_ID) @RequestParam("id") String id) { + return ResultDTO.requstSuccess(helpCenterService.selectContentById(id)); + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/ImageController.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/ImageController.java new file mode 100644 index 0000000..fd2df89 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/ImageController.java @@ -0,0 +1,50 @@ +package com.blockchain.server.sysconf.controller; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.base.controller.BaseController; +import com.blockchain.server.sysconf.common.constants.ImageConstant; +import com.blockchain.server.sysconf.controller.api.SysconfImageApi; +import com.blockchain.server.sysconf.service.SystemImageService; +import com.github.pagehelper.PageHelper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author: Liusd + * @create: 2019-03-25 13:45 + **/ +@RestController +@RequestMapping("/systemImage") +@Api(SysconfImageApi.CONTROLLER_API) +public class ImageController extends BaseController { + private static final Logger LOG = LoggerFactory.getLogger(ImageController.class); + + @Autowired + private SystemImageService systemImageService; + + /** + * @Description: 轮播图列表 + * @Param: [status, pageNum, pageSize] + * @return: com.blockchain.common.base.dto.ResultDTO + * @Author: Liu.sd + * @Date: 2019/3/25 + */ + @GetMapping("/systemImageList") + @ApiOperation(value = SysconfImageApi.SystemImageList.METHOD_TITLE_NAME, notes = SysconfImageApi.SystemImageList.METHOD_TITLE_NOTE) + public ResultDTO systemImageList( + @ApiParam(SysconfImageApi.SystemImageList.METHOD_API_PAGENUM) @RequestParam(required = false, value = "type" ,defaultValue = ImageConstant.TYPE_APP) String type, + @ApiParam(SysconfImageApi.SystemImageList.METHOD_API_PAGENUM) @RequestParam(value = "pageNum", defaultValue = BaseConstant.PAGE_DEFAULT_INDEX, required = false) Integer pageNum, + @ApiParam(SysconfImageApi.SystemImageList.METHOD_API_PAGESIZE) @RequestParam(value = "pageSize", defaultValue = BaseConstant.PAGE_DEFAULT_SIZE, required = false) Integer pageSize) { + PageHelper.startPage(pageNum, pageSize); + return generatePage(systemImageService.systemImageList(type,ImageConstant.STATUS_SHOW)); + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/NewsController.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/NewsController.java new file mode 100644 index 0000000..4e8702f --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/NewsController.java @@ -0,0 +1,40 @@ +package com.blockchain.server.sysconf.controller; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.sysconf.controller.api.NewsApi; +import com.blockchain.server.sysconf.service.NewsService; +import com.github.pagehelper.PageHelper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; + +@Api(NewsApi.NEWS_API) +@RestController +@RequestMapping("/news") +public class NewsController { + + private NewsService newsService; + + @Autowired + public void setNewsService(NewsService newsService) { + this.newsService = newsService; + } + + @ApiOperation(value = NewsApi.LISTNEWS.METHOD_TITLE_NAME, notes = NewsApi.LISTNEWS.METHOD_TITLE_NOTE) + @GetMapping("/list") + public ResultDTO ListNews(@ApiParam(NewsApi.LISTNEWS.METHOD_API_TYPE) @RequestParam(value = "type") Integer type, HttpServletRequest request) { + return ResultDTO.requstSuccess(newsService.listNews(type, HttpRequestUtil.getUserLocale(request), null, null)); + } + + @ApiOperation(value = NewsApi.NEWDETAIL.METHOD_TITLE_NAME, notes = NewsApi.NEWDETAIL.METHOD_TITLE_NOTE) + @GetMapping("/getNewsById") + public ResultDTO NewsDetail(@ApiParam(NewsApi.NEWDETAIL.METHOD_API_ID) @RequestParam(value = "newsId") String newsId) { + return ResultDTO.requstSuccess(newsService.getNewsById(newsId)); + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/NoticeController.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/NoticeController.java new file mode 100644 index 0000000..f86a28a --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/NoticeController.java @@ -0,0 +1,48 @@ +package com.blockchain.server.sysconf.controller; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.base.controller.BaseController; +import com.blockchain.server.sysconf.controller.api.SysconfNoticeApi; +import com.blockchain.server.sysconf.service.SystemNoticeService; +import com.github.pagehelper.PageHelper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author: Liusd + * @create: 2019-03-25 13:46 + **/ +@RestController +@RequestMapping("/systemNotice") +@Api(SysconfNoticeApi.CONTROLLER_API) +public class NoticeController extends BaseController { + + private static final Logger LOG = LoggerFactory.getLogger(NoticeController.class); + + @Autowired + private SystemNoticeService systemNoticeService; + + /** + * @Description: 公告列表 + * @Param: [request, pageNum, pageSize] + * @return: com.blockchain.common.base.dto.ResultDTO + * @Author: Liu.sd + * @Date: 2019/3/25 + */ + @GetMapping("/systemNoticeList") + @ApiOperation(value = SysconfNoticeApi.SystemNoticeList.METHOD_TITLE_NAME, notes = SysconfNoticeApi.SystemNoticeList.METHOD_TITLE_NOTE) + public ResultDTO systemNoticeList(@ApiParam(SysconfNoticeApi.SystemNoticeList.METHOD_API_PAGENUM) @RequestParam(value = "pageNum", defaultValue = BaseConstant.PAGE_DEFAULT_INDEX, required = false) Integer pageNum, + @ApiParam(SysconfNoticeApi.SystemNoticeList.METHOD_API_PAGESIZE) @RequestParam(value = "pageSize", defaultValue = BaseConstant.PAGE_DEFAULT_SIZE, required = false) Integer pageSize, HttpServletRequest request) { + PageHelper.startPage(pageNum, pageSize); + return generatePage(systemNoticeService.systemNoticeList(HttpRequestUtil.getUserLocale(request))); + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/ProjectCenterController.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/ProjectCenterController.java new file mode 100644 index 0000000..f8b58a5 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/ProjectCenterController.java @@ -0,0 +1,112 @@ +package com.blockchain.server.sysconf.controller; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.base.controller.BaseController; +import com.blockchain.server.sysconf.common.constants.ImageConstant; +import com.blockchain.server.sysconf.controller.api.ProjectCenterApi; +import com.blockchain.server.sysconf.controller.api.SysconfImageApi; +import com.blockchain.server.sysconf.controller.api.SysconfNoticeApi; +import com.blockchain.server.sysconf.service.ProjectCenterService; +import com.blockchain.server.sysconf.service.ProjectCenterStarService; +import com.github.pagehelper.PageHelper; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; + +/** + * 项目中心 + * + */ +@RestController +@RequestMapping("/projectCenter") +public class ProjectCenterController extends BaseController { + + private static final Logger LOG = LoggerFactory.getLogger(ProjectCenterController.class); + + @Autowired + private ProjectCenterService projectCenterService; + @Autowired + private ProjectCenterStarService projectCenterStarService; + + @Autowired + private RedisTemplate redisTemplate; + + /** + * 项目列表 + */ + @GetMapping("/list") + @ApiOperation(value = ProjectCenterApi.ProjectCenterList.METHOD_TITLE_NAME, notes = ProjectCenterApi.ProjectCenterList.METHOD_TITLE_NOTE) + public ResultDTO list(@ApiParam(ProjectCenterApi.ProjectCenterList.METHOD_API_CLASSIFYID) @RequestParam(required = false, value = "classifyId") String classifyId, + @ApiParam(ProjectCenterApi.ProjectCenterList.METHOD_API_DESCR) @RequestParam(required = false, value = "descr") String descr, + @ApiParam(ProjectCenterApi.ProjectCenterList.METHOD_API_PAGENUM) @RequestParam(value = "pageNum", defaultValue = BaseConstant.PAGE_DEFAULT_INDEX, required = false) Integer pageNum, + @ApiParam(ProjectCenterApi.ProjectCenterList.METHOD_API_PAGESIZE) @RequestParam(value = "pageSize", defaultValue = BaseConstant.PAGE_DEFAULT_SIZE, required = false) Integer pageSize, HttpServletRequest request) { + PageHelper.startPage(pageNum, pageSize); + String userOpenId = ""; + try { + userOpenId = SSOHelper.getUserId(redisTemplate, request); + }catch (Exception e){ + e.printStackTrace(); + } + return generatePage(projectCenterService.list(userOpenId,classifyId,descr,ImageConstant.STATUS_SHOW, HttpRequestUtil.getUserLocale(request))); + } + + /** + * @Description: + * @Param: [status, pageNum, pageSize] + * @return: com.blockchain.common.base.dto.ResultDTO + * @Author: Liu.sd + * @Date: 2019/7/13 + */ + @GetMapping("/classifyList") + @ApiOperation(value = ProjectCenterApi.ClassifyList.METHOD_TITLE_NAME, notes = ProjectCenterApi.ClassifyList.METHOD_TITLE_NOTE) + public ResultDTO classifyList(HttpServletRequest request) { + return generatePage(projectCenterService.classifyList(ImageConstant.STATUS_SHOW,HttpRequestUtil.getUserLocale(request))); + } + /** + * 详情 + * @param id + * @return + */ + @GetMapping("/selectById") + @ApiOperation(value = ProjectCenterApi.ProjectCenterSelectById.METHOD_TITLE_NAME, notes = ProjectCenterApi.ProjectCenterSelectById.METHOD_TITLE_NOTE) + public ResultDTO selectById( + @ApiParam(ProjectCenterApi.ProjectCenterSelectById.METHOD_API_ID) @RequestParam("id") String id) { + return ResultDTO.requstSuccess(projectCenterService.selectById(id)); + } + + + /** + * @Description: 项目报告列表 + * @Param: [reportType, dateType, projectId, pageNum, pageSize] + * @return: com.blockchain.common.base.dto.ResultDTO + * @Author: Liu.sd + * @Date: 2019/7/13 + */ + @GetMapping("/reportList") + @ApiOperation(value = ProjectCenterApi.ReportList.METHOD_TITLE_NAME, notes = ProjectCenterApi.ReportList.METHOD_TITLE_NOTE) + public ResultDTO reportList(@ApiParam(ProjectCenterApi.ReportList.METHOD_API_REPORTTYPE) @RequestParam(required = false, value = "reportType") String reportType, + @ApiParam(ProjectCenterApi.ReportList.METHOD_API_DATETYPE) @RequestParam(required = false, value = "dateType") Integer dateType, + @ApiParam(ProjectCenterApi.ReportList.METHOD_API_PROJECTID) @RequestParam("projectId") String projectId, + @ApiParam(ProjectCenterApi.ReportList.METHOD_API_PAGENUM) @RequestParam(value = "pageNum", defaultValue = BaseConstant.PAGE_DEFAULT_INDEX, required = false) Integer pageNum, + @ApiParam(ProjectCenterApi.ReportList.METHOD_API_PAGESIZE) @RequestParam(value = "pageSize", defaultValue = BaseConstant.PAGE_DEFAULT_SIZE, required = false) Integer pageSize) { + PageHelper.startPage(pageNum, pageSize); + return generatePage(projectCenterService.reportList(reportType,dateType,ImageConstant.STATUS_SHOW,projectId)); + } + + + @PostMapping("/star") + @ApiOperation(value = ProjectCenterApi.Star.METHOD_TITLE_NAME, notes = ProjectCenterApi.Star.METHOD_TITLE_NOTE) + public ResultDTO star(@ApiParam(ProjectCenterApi.Star.METHOD_API_PROJECTID) @RequestParam("projectId") String projectId, + @ApiParam(ProjectCenterApi.Star.METHOD_API_USERID) @RequestParam("userId") String userId) { + return ResultDTO.requstSuccess(projectCenterStarService.add(projectId,userId)); + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/VersionController.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/VersionController.java new file mode 100644 index 0000000..3e67f96 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/VersionController.java @@ -0,0 +1,53 @@ +package com.blockchain.server.sysconf.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.sysconf.controller.api.VersionApi; +import com.blockchain.server.sysconf.entity.Version; +import com.blockchain.server.sysconf.service.VersionService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + + +@Api(VersionApi.VERSION_API) +@RestController +@RequestMapping("/version") +public class VersionController { + + private static final Logger LOGGER = LoggerFactory.getLogger(VersionController.class); + + @Autowired + private VersionService versionService; + /** + * 根据系统类型查询最新app版本信息 + * @param device + * @return + */ + @ApiOperation(value = VersionApi.FINDNEWVERSION.METHOD_TITLE_NAME, notes = VersionApi.FINDNEWVERSION.METHOD_TITLE_NOTE) + @RequestMapping(value = "/findNewVersion", method = RequestMethod.GET) + public ResultDTO findNewVersion(@ApiParam(VersionApi.FINDNEWVERSION.METHOD_API_DEVICE) @RequestParam("device") String device){ + Version version = versionService.findNewVersion(device); + return ResultDTO.requstSuccess(version); + } + + /** + * 查询所有系统最新的版本信息 + * @return + */ + @ApiOperation(value = VersionApi.FINDNEWVERSIONALL.METHOD_TITLE_NAME, notes = VersionApi.FINDNEWVERSIONALL.METHOD_TITLE_NOTE) + @RequestMapping(value = "/findNewVersionAll", method = RequestMethod.GET) + public ResultDTO findNewVersionAll(){ + Map map = versionService.findNewVersionAll(); + return ResultDTO.requstSuccess(map); + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/WgtVersionController.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/WgtVersionController.java new file mode 100644 index 0000000..ac7b49b --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/WgtVersionController.java @@ -0,0 +1,33 @@ +package com.blockchain.server.sysconf.controller; + + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.server.sysconf.controller.api.WgtVersionApi; +import com.blockchain.server.sysconf.service.WgtVersionService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + + +@Api(WgtVersionApi.WGT_VERSION_API) +@RestController +@RequestMapping("/wgtVersion") +public class WgtVersionController { + + private static final Logger LOGGER = LoggerFactory.getLogger(WgtVersionController.class); + + @Autowired + private WgtVersionService wgtVersionService; + + @ApiOperation(value = WgtVersionApi.FINDNEWWGTVERSION.METHOD_TITLE_NAME, notes = WgtVersionApi.FINDNEWWGTVERSION.METHOD_TITLE_NOTE) + @RequestMapping(value = "/findNewWgtVersion", method = RequestMethod.GET) + public ResultDTO findNewWgtVersion() { + return ResultDTO.requstSuccess(wgtVersionService.findNewWgtVersion()); + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/AboutUsApi.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/AboutUsApi.java new file mode 100644 index 0000000..2e6b179 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/AboutUsApi.java @@ -0,0 +1,11 @@ +package com.blockchain.server.sysconf.controller.api; + +public class AboutUsApi { + public static final String ABOUTUS_API = "关于我们控制器"; + + public static class FINDABOUTUS{ + public static final String METHOD_TITLE_NAME = "查询关于我们信息"; + public static final String METHOD_TITLE_NOTE = "根据内容语种查询关于我们信息"; + public static final String METHOD_API_LANGUAGES="内容语种"; + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/AdSliderApi.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/AdSliderApi.java new file mode 100644 index 0000000..0d31b42 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/AdSliderApi.java @@ -0,0 +1,10 @@ +package com.blockchain.server.sysconf.controller.api; + +public class AdSliderApi { + public static final String AD_SLIDER_API = "首页轮播图控制器"; + + public static class ListAdSlider{ + public static final String METHOD_TITLE_NAME = "获取首页轮播图列表"; + public static final String METHOD_TITLE_NOTE = "获取首页轮播图列表"; + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/AgreementApi.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/AgreementApi.java new file mode 100644 index 0000000..9e3d572 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/AgreementApi.java @@ -0,0 +1,13 @@ +package com.blockchain.server.sysconf.controller.api; + +public class AgreementApi { + + public static final String AGREEMENT_API = "用户协议控制器"; + + public static class FINDAGREEMENT{ + public static final String METHOD_TITLE_NAME = "查询用户协议信息"; + public static final String METHOD_TITLE_NOTE = "根据协议语种查询用户协议信息"; + public static final String METHOD_API_LANGUAGES="协议语种"; + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/ContactUsApi.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/ContactUsApi.java new file mode 100644 index 0000000..6d47e83 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/ContactUsApi.java @@ -0,0 +1,11 @@ +package com.blockchain.server.sysconf.controller.api; + +public class ContactUsApi { + + public static final String CONTACTUS_API = "联系我们控制器"; + + public static class FINDCONTACTUSALL{ + public static final String METHOD_TITLE_NAME = "查询联系我们信息列表"; + public static final String METHOD_TITLE_NOTE = "查询联系我们信息列表"; + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/HelpCenterApi.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/HelpCenterApi.java new file mode 100644 index 0000000..4c02340 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/HelpCenterApi.java @@ -0,0 +1,21 @@ +package com.blockchain.server.sysconf.controller.api; + +public class HelpCenterApi { + public static final String HELPCENTER_API = "帮助中心控制器"; + + public static class SELECTHELPCENTERFORAPP{ + public static final String METHOD_TITLE_NAME = "查询帮助中心信息"; + public static final String METHOD_TITLE_NOTE = "查询帮助中心信息"; + } + public static class SELECTCONTENTBYID{ + public static final String METHOD_TITLE_NAME = "查询帮助中心内容"; + public static final String METHOD_TITLE_NOTE = "根据id查询帮助中心内容"; + public static final String METHOD_API_ID="id"; + } + public static class SELECTHELPCENTERFORPC{ + public static final String METHOD_TITLE_NAME = "查询帮助中心信息"; + public static final String METHOD_TITLE_NOTE = "根据标题查询帮助中心信息"; + public static final String METHOD_API_TITLE="标题"; + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/NewsApi.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/NewsApi.java new file mode 100644 index 0000000..9caca30 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/NewsApi.java @@ -0,0 +1,20 @@ +package com.blockchain.server.sysconf.controller.api; + +public class NewsApi { + + public static final String NEWS_API = "文章控制器"; + + public static class LISTNEWS{ + public static final String METHOD_TITLE_NAME = "查询文章列表"; + public static final String METHOD_TITLE_NOTE = "根据文章类型、国际化标识查询文章列表"; + public static final String METHOD_API_TYPE="文章类型"; + public static final String METHOD_API_LANGUAGES="国际化标识"; + public static final String METHOD_API_PAGENUM="查询页码"; + public static final String METHOD_API_PAGESIZE="每页记录数"; + } + public static class NEWDETAIL{ + public static final String METHOD_TITLE_NAME = "获取文章详细信息"; + public static final String METHOD_TITLE_NOTE = "根据id获取文章详细信息"; + public static final String METHOD_API_ID="id"; + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/ProjectCenterApi.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/ProjectCenterApi.java new file mode 100644 index 0000000..93cfd3c --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/ProjectCenterApi.java @@ -0,0 +1,48 @@ +package com.blockchain.server.sysconf.controller.api; + +/** + * @author: Liusd + * @create: 2019-03-25 13:51 + **/ +public class ProjectCenterApi { + + public static final String CONTROLLER_API = "轮播图管理控制器"; + + public static class ProjectCenterList { + public static final String METHOD_TITLE_NAME = "项目列表"; + public static final String METHOD_TITLE_NOTE = "项目列表"; + public static final String METHOD_API_CLASSIFYID = "分类id"; + public static final String METHOD_API_DESCR = "简介"; + public static final String METHOD_API_PAGENUM="查询页码"; + public static final String METHOD_API_PAGESIZE="每页记录数"; + } + + public static class ProjectCenterSelectById { + public static final String METHOD_TITLE_NAME = "项目基础信息"; + public static final String METHOD_TITLE_NOTE = "项目基础信息"; + public static final String METHOD_API_ID = "id"; + } + + public static class ReportList { + public static final String METHOD_TITLE_NAME = "报告列表"; + public static final String METHOD_TITLE_NOTE = "报告列表"; + public static final String METHOD_API_PROJECTID = "项目id"; + public static final String METHOD_API_DATETYPE = "时间范围"; + public static final String METHOD_API_REPORTTYPE = "报告类型"; + public static final String METHOD_API_PAGENUM="查询页码"; + public static final String METHOD_API_PAGESIZE="每页记录数"; + } + + public static class Star { + public static final String METHOD_TITLE_NAME = "点赞"; + public static final String METHOD_TITLE_NOTE = "点赞"; + public static final String METHOD_API_PROJECTID = "项目id"; + public static final String METHOD_API_USERID = "用户id"; + } + + public static class ClassifyList { + public static final String METHOD_TITLE_NAME = "分类列表"; + public static final String METHOD_TITLE_NOTE = "分类列表"; + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/SysconfImageApi.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/SysconfImageApi.java new file mode 100644 index 0000000..0083594 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/SysconfImageApi.java @@ -0,0 +1,20 @@ +package com.blockchain.server.sysconf.controller.api; + +/** + * @author: Liusd + * @create: 2019-03-25 13:51 + **/ +public class SysconfImageApi { + + public static final String CONTROLLER_API = "轮播图管理控制器"; + + public static class SystemImageList { + + public static final String METHOD_TITLE_NAME = "系统用户列表接口"; + public static final String METHOD_TITLE_NOTE = "系统用户列表接口,可根据状态查询"; + public static final String METHOD_API_TYPE = "协议类型"; + public static final String METHOD_API_PAGENUM="查询页码"; + public static final String METHOD_API_PAGESIZE="每页记录数"; + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/SysconfNoticeApi.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/SysconfNoticeApi.java new file mode 100644 index 0000000..ff55754 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/SysconfNoticeApi.java @@ -0,0 +1,18 @@ +package com.blockchain.server.sysconf.controller.api; + +/** + * @author: Liusd + * @create: 2019-03-25 13:51 + **/ +public class SysconfNoticeApi { + + public static final String CONTROLLER_API = "公告管理控制器"; + + public static class SystemNoticeList { + + public static final String METHOD_TITLE_NAME = "系统用户列表接口"; + public static final String METHOD_TITLE_NOTE = "系统用户列表接口,可根据状态查询"; + public static final String METHOD_API_PAGENUM="查询页码"; + public static final String METHOD_API_PAGESIZE="每页记录数"; + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/VersionApi.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/VersionApi.java new file mode 100644 index 0000000..78e6ec3 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/VersionApi.java @@ -0,0 +1,15 @@ +package com.blockchain.server.sysconf.controller.api; + +public class VersionApi { + public static final String VERSION_API = "版本控制器"; + + public static class FINDNEWVERSION{ + public static final String METHOD_TITLE_NAME = "查询最新app版本信息"; + public static final String METHOD_TITLE_NOTE = "根据设备型号查询最新app版本信息"; + public static final String METHOD_API_DEVICE="设备型号"; + } + public static class FINDNEWVERSIONALL{ + public static final String METHOD_TITLE_NAME = "查询所有系统最新app版本信息"; + public static final String METHOD_TITLE_NOTE = "查询所有系统最新app版本信息"; + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/WgtVersionApi.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/WgtVersionApi.java new file mode 100644 index 0000000..369c480 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/controller/api/WgtVersionApi.java @@ -0,0 +1,12 @@ +package com.blockchain.server.sysconf.controller.api; + +public class WgtVersionApi { + + public static final String WGT_VERSION_API = "app补丁控制器"; + + public static class FINDNEWWGTVERSION{ + public static final String METHOD_TITLE_NAME = "查询最新补丁版本信息"; + public static final String METHOD_TITLE_NOTE = "查询最新补丁版本信息"; + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/AboutUsQueryConditionDTO.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/AboutUsQueryConditionDTO.java new file mode 100644 index 0000000..d52ad8a --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/AboutUsQueryConditionDTO.java @@ -0,0 +1,20 @@ +package com.blockchain.server.sysconf.dto; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 关于我们信息表 + */ +@Data +public class AboutUsQueryConditionDTO implements Serializable{ + + private String textContent; + private Date createTime; + private Date modifyTime; + private Date beginTime;//开始时间 + private Date endTime;//结束时间 + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/AdSliderDto.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/AdSliderDto.java new file mode 100644 index 0000000..91064bf --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/AdSliderDto.java @@ -0,0 +1,36 @@ +package com.blockchain.server.sysconf.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 首页广告轮播表 app_ad_slider + * + * @author hugq + * @date 2018-09-03 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AdSliderDto implements Serializable { + /** + * 主键 + */ + private Integer id; + /** + * 图片路径 + */ + private String imgPath; + /** + * 序号 + */ + private Integer serialNumber; + /** + * 广告状态:0-隐藏,1-显示 + */ + private String status; + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/ContactUsQueryConditionDTO.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/ContactUsQueryConditionDTO.java new file mode 100644 index 0000000..eea7726 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/ContactUsQueryConditionDTO.java @@ -0,0 +1,19 @@ +package com.blockchain.server.sysconf.dto; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 关于我们信息表 + */ +@Data +public class ContactUsQueryConditionDTO implements Serializable{ + + private Integer showStatus; + private String contactValue; + private Date beginTime;//开始时间 + private Date endTime;//结束时间 + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/HelpCenterDTO.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/HelpCenterDTO.java new file mode 100644 index 0000000..980e6f3 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/HelpCenterDTO.java @@ -0,0 +1,24 @@ +package com.blockchain.server.sysconf.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class HelpCenterDTO { + + private String id; + private String title; + private String content; + private String url; + private Integer rank; + private String userLocal; + private Integer showStatus; + private Date createTime; + private Date modifyTime; + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/NewsDTO.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/NewsDTO.java new file mode 100644 index 0000000..35288ea --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/NewsDTO.java @@ -0,0 +1,19 @@ +package com.blockchain.server.sysconf.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class NewsDTO { + + private String id; + private String title; + private String content; + private String url; + private Date createTime; +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/NewsQueryConditionDTO.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/NewsQueryConditionDTO.java new file mode 100644 index 0000000..5b97c30 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/NewsQueryConditionDTO.java @@ -0,0 +1,18 @@ +package com.blockchain.server.sysconf.dto; + +import lombok.Data; + +import java.util.Date; + +/** + * 获取文章列表的查询条件 + */ +@Data +public class NewsQueryConditionDTO { + + private Integer type;//文章类型 + private String languages;//语种 + private String title;//标题 + private Date beginTime;//开始时间 + private Date endTime;//结束时间 +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/ProjectCenterDto.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/ProjectCenterDto.java new file mode 100644 index 0000000..5f1af18 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/dto/ProjectCenterDto.java @@ -0,0 +1,16 @@ +package com.blockchain.server.sysconf.dto; + +import lombok.Data; + +@Data +public class ProjectCenterDto { + + private String id; + private String currencyName; + private String coinUrl; + private String descr; + private String classifyName; + private int star; + private int starNum; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/AboutUs.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/AboutUs.java new file mode 100644 index 0000000..ddf433d --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/AboutUs.java @@ -0,0 +1,35 @@ +package com.blockchain.server.sysconf.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.Date; + +/** + * 关于我们信息表 + */ +@Table(name = "conf_about_us") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AboutUs implements Serializable{ + + @Id + @Column(name = "id") + private String id; + @Column(name = "text_content") + private String content; + @Column(name = "languages") + private String languages; + @Column(name = "create_time") + private Date createTime; + @Column(name = "modify_time") + private Date modifyTime; + + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/AdSlider.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/AdSlider.java new file mode 100644 index 0000000..c374839 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/AdSlider.java @@ -0,0 +1,33 @@ +package com.blockchain.server.sysconf.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * AboutUsDto 数据传输类 + * + * @version 1.0 + * @date 2018-08-27 14:24:47 + */ +@Table(name = "conf_ad_slider") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AdSlider extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "img_path") + private String imgPath; + @Column(name = "serial_number") + private Integer serialNumber; + @Column(name = "status") + private String status; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/Agreement.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/Agreement.java new file mode 100644 index 0000000..1362bf3 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/Agreement.java @@ -0,0 +1,33 @@ +package com.blockchain.server.sysconf.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.Date; + +/** + *用户协议表 + */ +@Table(name = "conf_user_agreement") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Agreement implements Serializable{ + + @Id + @Column(name = "id") + private String id; + @Column(name = "text_content") + private String textContent; + @Column(name = "languages") + private String languages; + @Column(name = "create_time") + private Date createTime; + @Column(name = "modify_time") + private Date modifyTime; +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ContactUs.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ContactUs.java new file mode 100644 index 0000000..b52fb9e --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ContactUs.java @@ -0,0 +1,40 @@ +package com.blockchain.server.sysconf.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.Date; + +/** + * 联系我们信息表 + */ +@Table(name = "conf_contact_us") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ContactUs implements Serializable { + + @Id + @Column(name = "id") + private String id; + @Column(name = "contact_name") + private String contactName; + @Column(name = "contact_value") + private String contactValue; + @Column(name = "rank") + private Integer rank; + @Column(name = "user_local") + private String userLocal; + @Column(name = "show_status") + private Integer showStatus; + @Column(name = "create_time") + private Date createTime; + @Column(name = "modify_time") + private Date modifyTime; + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/HelpCenter.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/HelpCenter.java new file mode 100644 index 0000000..c07dd6b --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/HelpCenter.java @@ -0,0 +1,45 @@ +package com.blockchain.server.sysconf.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.Date; + +/** + * 帮助中心表 conf_help_center + * + * @author ruoyi + * @date 2018-10-30 + */ +@Table(name = "conf_help_center") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class HelpCenter implements Serializable { + + @Id + @Column(name = "id") + private String id; + @Column(name = "title") + private String title; + @Column(name = "content") + private String content; + @Column(name = "url") + private String url; + @Column(name = "rank") + private Integer rank; + @Column(name = "user_local") + private String userLocal; + @Column(name = "show_status") + private Integer showStatus; + @Column(name = "create_time") + private Date createTime; + @Column(name = "modify_time") + private Date modifyTime; + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/News.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/News.java new file mode 100644 index 0000000..55597d1 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/News.java @@ -0,0 +1,37 @@ +package com.blockchain.server.sysconf.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "conf_news") +public class News { + + @Id + @Column(name = "id") + private String id; + @Column(name = "title") + private String title; + @Column(name = "user_id") + private String userId; + @Column(name = "type") + private Integer type; + @Column(name = "content") + private String content; + @Column(name = "url") + private String url; + @Column(name = "languages") + private String languages; + @Column(name = "create_time") + private Date createTime; + @Column(name = "modify_time") + private Date modifyTime; +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterClassify.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterClassify.java new file mode 100644 index 0000000..4c10ed7 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterClassify.java @@ -0,0 +1,42 @@ +package com.blockchain.server.sysconf.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * CREATE TABLE `conf_project_center_classify` ( + * `id` varchar(36) NOT NULL COMMENT '项目中心类别', + * `name` varchar(255) DEFAULT NULL COMMENT '名称', + * `status` char(1) DEFAULT NULL COMMENT '显示状态: 显示(Y),隐藏(N)', + * `create_time` datetime DEFAULT NULL, + * `modify_time` datetime DEFAULT NULL, + * PRIMARY KEY (`id`) + * ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + */ +@Table(name = "conf_project_center_classify") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ProjectCenterClassify extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "name") + private String name; + @Column(name = "status") + private String status; + @Column(name = "languages") + private String languages; + @Column(name = "create_time") + private Date createTime; + @Column(name = "modify_time") + private Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterInfo.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterInfo.java new file mode 100644 index 0000000..624c0ba --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterInfo.java @@ -0,0 +1,79 @@ +package com.blockchain.server.sysconf.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * CREATE TABLE `conf_project_center_info` ( + * `id` varchar(36) NOT NULL, + * `currency_name` varchar(8) DEFAULT NULL COMMENT '数字货币类型,BTC、ETH、EOS', + * `order_by` int(11) DEFAULT NULL COMMENT '排序', + * `issue_time` varchar(20) DEFAULT NULL COMMENT '发行时间', + * `total_supply` varchar(255) DEFAULT NULL COMMENT '发行总量', + * `total_circulation` varchar(255) DEFAULT NULL COMMENT '流通量', + * `ico_amount` varchar(255) DEFAULT NULL COMMENT '众筹价格', + * `white_paper` varchar(255) DEFAULT NULL COMMENT '白皮书', + * `presentation` text COMMENT '介绍', + * `descr` text COMMENT '简介', + * `coin_url` varchar(255) DEFAULT NULL COMMENT '图标', + * `type` char(1) DEFAULT NULL COMMENT 'W 文件 L 链接', + * `status` char(1) DEFAULT NULL COMMENT '显示状态: 显示(Y),隐藏(N)', + * `languages` varchar(10) DEFAULT NULL COMMENT '国际化标识', + * `classify_id` char(36) DEFAULT NULL COMMENT '分类id', + * `uccn` varchar(255) DEFAULT NULL COMMENT '官网', + * `create_time` datetime DEFAULT NULL, + * `modify_time` datetime DEFAULT NULL, + * PRIMARY KEY (`id`) USING BTREE + * ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + */ +@Table(name = "conf_project_center_info") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ProjectCenterInfo extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "currency_name") + private String currencyName; + @Column(name = "status") + private String status; + @Column(name = "order_by") + private Integer orderBy; + @Column(name = "issue_time") + private String issueTime; + @Column(name = "total_supply") + private String totalSupply; + @Column(name = "total_circulation") + private String totalCirculation; + @Column(name = "ico_amount") + private String icoAmount; + @Column(name = "white_paper") + private String whitePaper; + @Column(name = "coin_url") + private String coinUrl; + @Column(name = "presentation") + private String presentation; + @Column(name = "descr") + private String descr; + @Column(name = "type") + private String type; + @Column(name = "languages") + private String languages; + @Column(name = "classify_id") + private String classifyId; + @Column(name = "uccn") + private String uccn; + @Column(name = "create_time") + private Date createTime; + @Column(name = "modify_time") + private Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterReport.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterReport.java new file mode 100644 index 0000000..de257ee --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterReport.java @@ -0,0 +1,46 @@ +package com.blockchain.server.sysconf.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * CREATE TABLE `conf_project_center_report` ( + * `id` varchar(36) NOT NULL COMMENT '项目点赞表', + * `project_id` varchar(36) DEFAULT NULL COMMENT '项目id', + * `type` varchar(20) DEFAULT NULL COMMENT 'WEEK 周报 MONTH 月报 SPECIAL专题报告 FINANCIAL 财务报告', + * `create_time` datetime DEFAULT NULL, + * `title` varchar(255) DEFAULT NULL COMMENT '标题', + * `file_url` varchar(255) DEFAULT NULL COMMENT '文件地址', + * `status` char(1) DEFAULT NULL COMMENT '显示状态: 显示(Y),隐藏(N)', + * PRIMARY KEY (`id`) + * ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + */ +@Table(name = "conf_project_center_report") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ProjectCenterReport extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "project_id") + private String projectId; + @Column(name = "type") + private String type; + @Column(name = "title") + private String title; + @Column(name = "file_url") + private String fileUrl; + @Column(name = "status") + private String status; + @Column(name = "create_time") + private Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterStar.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterStar.java new file mode 100644 index 0000000..69bf4c0 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/ProjectCenterStar.java @@ -0,0 +1,37 @@ +package com.blockchain.server.sysconf.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * CREATE TABLE `conf_project_center_star` ( + * `id` varchar(36) NOT NULL COMMENT '项目点赞表', + * `project_id` varchar(36) DEFAULT NULL COMMENT '项目id', + * `user_id` varchar(36) DEFAULT NULL COMMENT '点赞人id', + * `create_time` datetime DEFAULT NULL, + * PRIMARY KEY (`id`) + * ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + */ +@Table(name = "conf_project_center_star") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ProjectCenterStar extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "project_id") + private String projectId; + @Column(name = "user_id") + private String userId; + @Column(name = "create_time") + private Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/SmsCount.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/SmsCount.java new file mode 100644 index 0000000..bf47e0c --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/SmsCount.java @@ -0,0 +1,26 @@ +package com.blockchain.server.sysconf.entity; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +@Data +@NoArgsConstructor +@Table(name = "u_sms_count") +public class SmsCount { + @Id + @Column(name = "id") + private String id; + @Column(name = "phone") + private String phone; + @Column(name = "sms_type") + private Integer smsType; + @Column(name = "sms_count") + private Integer smsCount; + @Column(name = "sms_date") + private Date smsDate; +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/SystemImage.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/SystemImage.java new file mode 100644 index 0000000..bf6c21c --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/SystemImage.java @@ -0,0 +1,37 @@ +package com.blockchain.server.sysconf.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * @author: Liusd + * @create: 2019-03-25 13:36 + **/ +@Data +@Table(name = "conf_system_image") +public class SystemImage extends BaseModel { + + @Id + @Column(name = "id") + private String id; + @Column(name = "file_url") + private String fileUrl; + @Column(name = "jump_url") + private String jumpUrl; + @Column(name = "rank") + private Integer rank; + @Column(name = "type") + private String type; + @Column(name = "status") + private String status; + @Column(name = "create_time") + private Date createTime; + @Column(name = "modify_time") + private Date modifyTime; + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/SystemNotice.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/SystemNotice.java new file mode 100644 index 0000000..b1630b9 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/SystemNotice.java @@ -0,0 +1,39 @@ +package com.blockchain.server.sysconf.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * @author: Liusd + * @create: 2019-03-25 13:36 + **/ +@Data +@Table(name = "conf_system_notice") +public class SystemNotice extends BaseModel { + + @Id + @Column(name = "id") + private String id; + @Column(name = "title") + private String title; + @Column(name = "details") + private String details; + @Column(name = "jump_url") + private String jumpUrl; + @Column(name = "rank") + private Integer rank; + @Column(name = "status") + private String status; + @Column(name = "languages") + private String languages; + @Column(name = "create_time") + private Date createTime; + @Column(name = "modify_time") + private Date modifyTime; + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/Version.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/Version.java new file mode 100644 index 0000000..ddc5c66 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/Version.java @@ -0,0 +1,56 @@ +package com.blockchain.server.sysconf.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.Date; + +/** + * app应用版本表 + */ +@Table(name = "conf_version") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Version implements Serializable{ + + @Id + @Column(name = "id") + private String id; + /** + * 版本号 + */ + @Column(name = "version") + private String version; + /** + * app版本路径 + */ + @Column(name = "app_url") + private String appUrl; + /** + * 备注 + */ + @Column(name = "remark") + private String remark; + /** + * 是否强制更新:0-否,1-是 + */ + @Column(name = "compel") + private Integer compel; + /** + * 设备 android | ios + */ + @Column(name = "device") + private String device; + @Column(name = "create_time") + private Date createTime; + @Column(name = "modify_time") + private Date modifyTime; + + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/WgtVersion.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/WgtVersion.java new file mode 100644 index 0000000..8132f3e --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/entity/WgtVersion.java @@ -0,0 +1,47 @@ +package com.blockchain.server.sysconf.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.Date; + +/** + * app补丁版本表 + */ +@Table(name = "conf_wgt_version") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WgtVersion implements Serializable{ + + @Id + @Column(name = "id") + private String id; + /** + * 补丁版本号 + */ + @Column(name = "wgt_version") + private String wgtVersion; + /** + * app补丁路径 + */ + @Column(name = "wgt_url") + private String wgtUrl; + /** + * 备注 + */ + @Column(name = "remark") + private String remark; + + @Column(name = "create_time") + private Date createTime; + @Column(name = "modify_time") + private Date modifyTime; + + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/AboutUsMapper.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/AboutUsMapper.java new file mode 100644 index 0000000..b61b193 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/AboutUsMapper.java @@ -0,0 +1,17 @@ +package com.blockchain.server.sysconf.mapper; + +import com.blockchain.server.sysconf.entity.AboutUs; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +@Repository +public interface AboutUsMapper extends Mapper { + + List listAllOrderByCreateTimeDesc(); + + AboutUs findNewestAboutUs(@Param("languages") String languages); + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/AdSliderMapper.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/AdSliderMapper.java new file mode 100644 index 0000000..601d8e0 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/AdSliderMapper.java @@ -0,0 +1,26 @@ +package com.blockchain.server.sysconf.mapper; + +import com.blockchain.server.sysconf.dto.AdSliderDto; +import com.blockchain.server.sysconf.entity.AdSlider; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * 首页广告轮播 数据层 + * + * @author ruoyi + * @date 2018-09-03 + */ +@Repository +public interface AdSliderMapper extends Mapper { + + /** + * 获取首页轮播广告图片 + * + * @return + */ + List listAdSliderForApp(); + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/AgreementMapper.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/AgreementMapper.java new file mode 100644 index 0000000..55ca6cb --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/AgreementMapper.java @@ -0,0 +1,19 @@ +package com.blockchain.server.sysconf.mapper; + +import com.blockchain.server.sysconf.entity.Agreement; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +@Repository +public interface AgreementMapper extends Mapper { + + /** + * 查询用户协议(客户端) + * @param languages + * @return + */ + Agreement findAgreement(@Param("type") String type,@Param("languages") String languages); + + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/ContactUsMapper.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/ContactUsMapper.java new file mode 100644 index 0000000..dea0e93 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/ContactUsMapper.java @@ -0,0 +1,16 @@ +package com.blockchain.server.sysconf.mapper; + +import com.blockchain.server.sysconf.entity.ContactUs; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +@Repository +public interface ContactUsMapper extends Mapper { + + List listAll(@Param("showStatus") Integer showStatus, @Param("userLocal") String userLocal); + + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/HelpCenterMapper.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/HelpCenterMapper.java new file mode 100644 index 0000000..01fe4ac --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/HelpCenterMapper.java @@ -0,0 +1,24 @@ +package com.blockchain.server.sysconf.mapper; + +import com.blockchain.server.sysconf.entity.HelpCenter; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * 帮助中心 数据层 + * + * @author ruoyi + * @date 2018-10-30 + */ +@Repository +public interface HelpCenterMapper extends Mapper { + + List selectHelpCenterForApp(@Param("showStatus") Integer showStatus,@Param("userLocal") String userLocale); + + List selectHelpCenterForPc(@Param("showStatus") Integer showStatus, @Param("title") String title,@Param("userLocal") String userLocale); + + String selectContentById(@Param("id") String id); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/NewsMapper.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/NewsMapper.java new file mode 100644 index 0000000..12de600 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/NewsMapper.java @@ -0,0 +1,24 @@ +package com.blockchain.server.sysconf.mapper; + +import com.blockchain.server.sysconf.dto.NewsDTO; +import com.blockchain.server.sysconf.dto.NewsQueryConditionDTO; +import com.blockchain.server.sysconf.entity.News; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.BaseMapper; + +import java.util.Date; +import java.util.List; + +@Repository +public interface NewsMapper extends BaseMapper { + /** + * 用于前端展示 + * @param condition 查询条件 + * @return + */ + + List listAll(NewsQueryConditionDTO condition); + + List listNewsDTO(@Param("type") Integer type,@Param("languages") String languages,@Param("beginTime") Date beginTime,@Param("endTime") Date endTime); +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/ProjectCenterMapper.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/ProjectCenterMapper.java new file mode 100644 index 0000000..10b13bb --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/ProjectCenterMapper.java @@ -0,0 +1,20 @@ +package com.blockchain.server.sysconf.mapper; + +import com.blockchain.server.sysconf.dto.ProjectCenterDto; +import com.blockchain.server.sysconf.entity.ProjectCenterClassify; +import com.blockchain.server.sysconf.entity.ProjectCenterInfo; +import com.blockchain.server.sysconf.entity.ProjectCenterReport; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +@Repository +public interface ProjectCenterMapper extends Mapper { + List list(@Param("userId") String userId,@Param("classifyId") String classifyId,@Param("descr") String descr, @Param("status") String status, @Param("languages") String languages); + + List reportList(@Param("reportType") String reportType,@Param("startDate") String startDate,@Param("status") String status, @Param("projectId") String projectId); + + List classifyList(@Param("status") String status, @Param("languages") String languages); +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/ProjectCenterStarMapper.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/ProjectCenterStarMapper.java new file mode 100644 index 0000000..2270ad6 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/ProjectCenterStarMapper.java @@ -0,0 +1,11 @@ +package com.blockchain.server.sysconf.mapper; + +import com.blockchain.server.sysconf.entity.ProjectCenterStar; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +@Repository +public interface ProjectCenterStarMapper extends Mapper { + int countByProjectId(@Param("projectId") String projectId); +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/SystemImageMapper.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/SystemImageMapper.java new file mode 100644 index 0000000..e1e6d90 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/SystemImageMapper.java @@ -0,0 +1,18 @@ +package com.blockchain.server.sysconf.mapper; + +import com.blockchain.server.sysconf.entity.SystemImage; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * @author: Liusd + * @create: 2019-03-04 17:34 + **/ +@Repository +public interface SystemImageMapper extends Mapper { + + List selectByTypeAndStatus(@Param("type") String type, @Param("status") String status); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/SystemNoticeMapper.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/SystemNoticeMapper.java new file mode 100644 index 0000000..24dd790 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/SystemNoticeMapper.java @@ -0,0 +1,21 @@ +package com.blockchain.server.sysconf.mapper; + +import com.blockchain.server.sysconf.entity.SystemNotice; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + + +/** + * @author: Liusd + * @create: 2019-03-04 17:34 + **/ +@Repository +public interface SystemNoticeMapper extends Mapper { + + List selectByStatus(@Param("status") String status); + + List selectByStatusAndLanguages(@Param("status")String status,@Param("languages") String languages); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/VersionMapper.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/VersionMapper.java new file mode 100644 index 0000000..f98c1af --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/VersionMapper.java @@ -0,0 +1,18 @@ +package com.blockchain.server.sysconf.mapper; + +import com.blockchain.server.sysconf.entity.Version; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +@Repository +public interface VersionMapper extends Mapper { + + Version findNewVersion(@Param("device") String device); + + + List listAll(@Param("device") String device); + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/WgtVersionMapper.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/WgtVersionMapper.java new file mode 100644 index 0000000..2d7b1e9 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/mapper/WgtVersionMapper.java @@ -0,0 +1,17 @@ +package com.blockchain.server.sysconf.mapper; + +import com.blockchain.server.sysconf.entity.WgtVersion; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +@Repository +public interface WgtVersionMapper extends Mapper { + + WgtVersion findNewWgtVersion(); + + List listAll(); + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/AboutUsService.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/AboutUsService.java new file mode 100644 index 0000000..f294df4 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/AboutUsService.java @@ -0,0 +1,23 @@ +package com.blockchain.server.sysconf.service; + + +import com.blockchain.server.sysconf.entity.AboutUs; + +import java.util.List; + +public interface AboutUsService { + + /** + * 查询关于我们信息列表 按照创建时间倒叙(后台) + * @return + */ + List listAll(); + + /** + * 查询关于我们信息 (客户端) + * @param languages + * @return + */ + AboutUs findNewestAboutUs(String languages); + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/AdSliderService.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/AdSliderService.java new file mode 100644 index 0000000..aba9a6f --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/AdSliderService.java @@ -0,0 +1,16 @@ +package com.blockchain.server.sysconf.service; + +import com.blockchain.server.sysconf.dto.AdSliderDto; + +import java.util.List; + +public interface AdSliderService { + + /** + * 获取首页轮播广告图片 + * + * @return + */ + List listAdSliderForApp(); + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/AgreementService.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/AgreementService.java new file mode 100644 index 0000000..340b600 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/AgreementService.java @@ -0,0 +1,18 @@ +package com.blockchain.server.sysconf.service; + + + +import com.blockchain.server.sysconf.entity.Agreement; + + +public interface AgreementService { + + /** + * 查询用户协议(客户端) + * @param languages + * @return + */ + Agreement findAgreement(String type,String languages); + + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/ContactUsService.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/ContactUsService.java new file mode 100644 index 0000000..78b72bb --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/ContactUsService.java @@ -0,0 +1,19 @@ +package com.blockchain.server.sysconf.service; + +import com.blockchain.server.sysconf.entity.ContactUs; + +import java.util.List; + +public interface ContactUsService { + + + /** + * 查询联系我们信息列表 + * + * @return + */ + List listAll(Integer showStatus, String userLocal); + + + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/HelpCenterService.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/HelpCenterService.java new file mode 100644 index 0000000..22ddee8 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/HelpCenterService.java @@ -0,0 +1,21 @@ +package com.blockchain.server.sysconf.service; + +import com.blockchain.server.sysconf.entity.HelpCenter; + +import java.util.List; + +/** + * 帮助中心 服务层 + * + * @author ruoyi + * @date 2018-10-30 + */ +public interface HelpCenterService { + + List selectHelpCenterForApp(String userLocale); + + List selectHelpCenterForPc(String title, String userLocale); + + String selectContentById(String id); + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/NewsService.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/NewsService.java new file mode 100644 index 0000000..1a56788 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/NewsService.java @@ -0,0 +1,19 @@ +package com.blockchain.server.sysconf.service; + +import com.blockchain.server.sysconf.dto.NewsDTO; +import com.blockchain.server.sysconf.dto.NewsQueryConditionDTO; +import com.blockchain.server.sysconf.entity.News; + +import java.util.Date; +import java.util.List; + +public interface NewsService { + + List listNews(Integer type, String languages, Date beginTime, Date endTime); + + List listAll(NewsQueryConditionDTO condition); + + News getNewsById(String id); + + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/ProjectCenterService.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/ProjectCenterService.java new file mode 100644 index 0000000..db25a35 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/ProjectCenterService.java @@ -0,0 +1,25 @@ +package com.blockchain.server.sysconf.service; + +import com.blockchain.server.sysconf.dto.ProjectCenterDto; +import com.blockchain.server.sysconf.entity.ProjectCenterClassify; +import com.blockchain.server.sysconf.entity.ProjectCenterInfo; +import com.blockchain.server.sysconf.entity.ProjectCenterReport; + +import java.util.List; + +public interface ProjectCenterService { + /** + * @Description: + * @Param: [status, languages] + * @return: java.util.List + * @Author: Liu.sd + * @Date: 2019/7/5 + */ + List list(String userOpenId, String classifyId, String descr, String status, String languages); + + ProjectCenterInfo selectById(String id); + + List reportList(String reportType, Integer dateType, String status, String projectId); + + List classifyList(String status, String languages); +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/ProjectCenterStarService.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/ProjectCenterStarService.java new file mode 100644 index 0000000..55b748f --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/ProjectCenterStarService.java @@ -0,0 +1,25 @@ +package com.blockchain.server.sysconf.service; + +import com.blockchain.server.sysconf.entity.ProjectCenterStar; + +public interface ProjectCenterStarService { + /** + * @Description: 数量 + * @Param: [projectId] + * @return: int + * @Author: Liu.sd + * @Date: 2019/7/13 + */ + int countByProjectId(String projectId); + + /** + * @Description: 新增 + * @Param: [projectCenterInfo] + * @return: void + * @Author: Liu.sd + * @Date: 2019/6/10 + */ + void add(ProjectCenterStar projectCenterStar); + + int add(String projectId, String userId); +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/SystemImageService.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/SystemImageService.java new file mode 100644 index 0000000..e0b9e6b --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/SystemImageService.java @@ -0,0 +1,13 @@ +package com.blockchain.server.sysconf.service; + +import com.blockchain.server.sysconf.entity.SystemImage; + +import java.util.List; + +/** + * @author: Liusd + * @create: 2019-03-25 13:54 + **/ +public interface SystemImageService { + List systemImageList(String type,String status); +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/SystemNoticeService.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/SystemNoticeService.java new file mode 100644 index 0000000..c202ac4 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/SystemNoticeService.java @@ -0,0 +1,14 @@ +package com.blockchain.server.sysconf.service; + +import com.blockchain.server.sysconf.entity.SystemNotice; + +import java.util.List; + +/** + * @author: Liusd + * @create: 2019-03-25 13:54 + **/ +public interface SystemNoticeService { + + List systemNoticeList(String languages); +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/VersionService.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/VersionService.java new file mode 100644 index 0000000..05ab2fb --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/VersionService.java @@ -0,0 +1,36 @@ +package com.blockchain.server.sysconf.service; + + +import com.blockchain.server.sysconf.entity.Version; + +import java.util.List; +import java.util.Map; + +public interface VersionService { + + /** + * 根据系统类型查询最新app版本信息 + * + * @param device 设备型号 + * @return + */ + Version findNewVersion(String device); + + /** + * 查询所有app版本信息列表 + * + * @param device + * @return + */ + List listAll(String device); + + /** + * 查询所有系统最新的版本信息 + * + * @return + */ + Map findNewVersionAll(); + + + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/WgtVersionService.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/WgtVersionService.java new file mode 100644 index 0000000..00e3ccf --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/WgtVersionService.java @@ -0,0 +1,20 @@ +package com.blockchain.server.sysconf.service; + + +import com.blockchain.server.sysconf.entity.WgtVersion; + +import java.util.List; + +public interface WgtVersionService { + /** + * 根据查询最新补丁版本信息 + * @return + */ + WgtVersion findNewWgtVersion(); + /** + * 查询所有版本版本信息列表 + * @return + */ + List listAll(); + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/AboutUsServiceImpl.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/AboutUsServiceImpl.java new file mode 100644 index 0000000..a8f3666 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/AboutUsServiceImpl.java @@ -0,0 +1,33 @@ +package com.blockchain.server.sysconf.service.impl; + + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.server.sysconf.entity.AboutUs; +import com.blockchain.server.sysconf.mapper.AboutUsMapper; +import com.blockchain.server.sysconf.service.AboutUsService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.util.List; + +@Service("aboutUsService") +public class AboutUsServiceImpl implements AboutUsService { + + @Autowired + private AboutUsMapper aboutUsMapper; + + @Override + public List listAll() { + return aboutUsMapper.listAllOrderByCreateTimeDesc(); + } + + @Override + public AboutUs findNewestAboutUs(String languages) { + if(StringUtils.isEmpty(languages)){ + languages = BaseConstant.USER_LOCALE_DEFAULT; + } + return aboutUsMapper.findNewestAboutUs(languages); + } + + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/AdSliderServiceImpl.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/AdSliderServiceImpl.java new file mode 100644 index 0000000..71e7c6a --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/AdSliderServiceImpl.java @@ -0,0 +1,22 @@ +package com.blockchain.server.sysconf.service.impl; + +import com.blockchain.server.sysconf.dto.AdSliderDto; +import com.blockchain.server.sysconf.mapper.AdSliderMapper; +import com.blockchain.server.sysconf.service.AdSliderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class AdSliderServiceImpl implements AdSliderService { + + @Autowired + private AdSliderMapper adSliderMapper; + + @Override + public List listAdSliderForApp() { + return adSliderMapper.listAdSliderForApp(); + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/AgreementServiceImpl.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/AgreementServiceImpl.java new file mode 100644 index 0000000..e212e31 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/AgreementServiceImpl.java @@ -0,0 +1,28 @@ +package com.blockchain.server.sysconf.service.impl; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.server.sysconf.entity.Agreement; +import com.blockchain.server.sysconf.mapper.AgreementMapper; +import com.blockchain.server.sysconf.service.AgreementService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + + + +@Service +public class AgreementServiceImpl implements AgreementService { + + @Autowired + private AgreementMapper agreementMapper; + + @Override + public Agreement findAgreement(String type,String languages) { + if(StringUtils.isEmpty(languages)){ + languages = BaseConstant.USER_LOCALE_DEFAULT; + } + return agreementMapper.findAgreement(type,languages); + } + + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/ContactUsServiceImpl.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/ContactUsServiceImpl.java new file mode 100644 index 0000000..ec1ae90 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/ContactUsServiceImpl.java @@ -0,0 +1,29 @@ +package com.blockchain.server.sysconf.service.impl; + + +import com.blockchain.server.sysconf.common.constants.UserConstant; +import com.blockchain.server.sysconf.entity.ContactUs; +import com.blockchain.server.sysconf.mapper.ContactUsMapper; +import com.blockchain.server.sysconf.service.ContactUsService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class ContactUsServiceImpl implements ContactUsService{ + + @Autowired + private ContactUsMapper contactUsMapper; + + @Override + public List listAll(Integer showStatus, String userLocal) { + //查询默认为中文语种 + if(StringUtils.isEmpty(userLocal)){ + userLocal = UserConstant.USER_LOCAL_CHINA; + } + return contactUsMapper.listAll(showStatus,userLocal); + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/HelpCenterServiceImpl.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/HelpCenterServiceImpl.java new file mode 100644 index 0000000..7983dbc --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/HelpCenterServiceImpl.java @@ -0,0 +1,37 @@ +package com.blockchain.server.sysconf.service.impl; + +import com.blockchain.server.sysconf.common.constants.ContactUsConstant; +import com.blockchain.server.sysconf.entity.HelpCenter; +import com.blockchain.server.sysconf.mapper.HelpCenterMapper; +import com.blockchain.server.sysconf.service.HelpCenterService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 帮助中心 服务层实现 + * + * @author ruoyi + * @date 2018-10-30 + */ +@Service +public class HelpCenterServiceImpl implements HelpCenterService { + @Autowired + private HelpCenterMapper helpCenterMapper; + + @Override + public List selectHelpCenterForApp(String userLocale) { + return helpCenterMapper.selectHelpCenterForApp(ContactUsConstant.STATUS_SHOW,userLocale); + } + + @Override + public List selectHelpCenterForPc(String title, String userLocale) { + return helpCenterMapper.selectHelpCenterForPc(ContactUsConstant.STATUS_SHOW,title,userLocale); + } + + @Override + public String selectContentById(String id) { + return helpCenterMapper.selectContentById(id); + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/NewsServiceImpl.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/NewsServiceImpl.java new file mode 100644 index 0000000..ec96cef --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/NewsServiceImpl.java @@ -0,0 +1,41 @@ +package com.blockchain.server.sysconf.service.impl; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.server.sysconf.dto.NewsDTO; +import com.blockchain.server.sysconf.dto.NewsQueryConditionDTO; +import com.blockchain.server.sysconf.entity.News; +import com.blockchain.server.sysconf.mapper.NewsMapper; +import com.blockchain.server.sysconf.service.NewsService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Service +public class NewsServiceImpl implements NewsService { + + private NewsMapper newsMapper; + + @Autowired + public void setNewsMapper(NewsMapper newsMapper) { + this.newsMapper = newsMapper; + } + + @Override + public List listNews(Integer type, String languages, Date beginTime, Date endTime) { + List news = newsMapper.listNewsDTO(type,languages,beginTime,endTime); + return news; + } + + @Override + public List listAll(NewsQueryConditionDTO condition) { + return newsMapper.listAll(condition); + } + + @Override + public News getNewsById(String id) { + return newsMapper.selectByPrimaryKey(id); + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/ProjectCenterServiceImpl.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/ProjectCenterServiceImpl.java new file mode 100644 index 0000000..548d639 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/ProjectCenterServiceImpl.java @@ -0,0 +1,53 @@ +package com.blockchain.server.sysconf.service.impl; + +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.sysconf.dto.ProjectCenterDto; +import com.blockchain.server.sysconf.entity.ProjectCenterClassify; +import com.blockchain.server.sysconf.entity.ProjectCenterInfo; +import com.blockchain.server.sysconf.entity.ProjectCenterReport; +import com.blockchain.server.sysconf.mapper.ProjectCenterMapper; +import com.blockchain.server.sysconf.service.ProjectCenterService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +@Service +public class ProjectCenterServiceImpl implements ProjectCenterService { + + @Autowired + private ProjectCenterMapper projectCenterMapper; + + @Override + public List list(String userOpenId, String classifyId, String descr, String status, String languages) { + return projectCenterMapper.list(userOpenId,classifyId,descr,status,languages); + } + + @Override + public ProjectCenterInfo selectById(String id) { + return projectCenterMapper.selectByPrimaryKey(id); + } + + @Override + public List reportList(String reportType, Integer dateType, String status, String projectId) { + String startDate = null; + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + if (dateType!=null){ + Calendar c = Calendar.getInstance(); + c.setTime(new Date()); + c.add(Calendar.DATE, 0-dateType); + Date d = c.getTime(); + startDate = format.format(d); + } + return projectCenterMapper.reportList(reportType,startDate, status,projectId); + } + + @Override + public List classifyList(String status, String languages) { + return projectCenterMapper.classifyList(status,languages); + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/ProjectCenterStarServiceImpl.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/ProjectCenterStarServiceImpl.java new file mode 100644 index 0000000..eb47072 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/ProjectCenterStarServiceImpl.java @@ -0,0 +1,45 @@ +package com.blockchain.server.sysconf.service.impl; + +import com.blockchain.server.sysconf.common.enums.SysConfigEnums; +import com.blockchain.server.sysconf.common.exception.SysConfigException; +import com.blockchain.server.sysconf.entity.ProjectCenterStar; +import com.blockchain.server.sysconf.mapper.ProjectCenterStarMapper; +import com.blockchain.server.sysconf.service.ProjectCenterStarService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; +import java.util.UUID; + +@Service +public class ProjectCenterStarServiceImpl implements ProjectCenterStarService { + + @Autowired + private ProjectCenterStarMapper projectCenterStarMapper; + @Override + public int countByProjectId(String projectId) { + return projectCenterStarMapper.countByProjectId(projectId); + } + + @Override + public void add(ProjectCenterStar projectCenterStar) { + projectCenterStar.setId(UUID.randomUUID().toString()); + projectCenterStar.setCreateTime(new Date()); + projectCenterStarMapper.insert(projectCenterStar); + } + + @Override + public int add(String projectId, String userId) { + ProjectCenterStar projectCenterStar =new ProjectCenterStar(); + projectCenterStar.setProjectId(projectId); + projectCenterStar.setUserId(userId); + List list = projectCenterStarMapper.select(projectCenterStar); + if (list!=null && list.size()>0){ + throw new SysConfigException(SysConfigEnums.IS_STAR); + } + projectCenterStar.setId(UUID.randomUUID().toString()); + projectCenterStar.setCreateTime(new Date()); + return projectCenterStarMapper.insert(projectCenterStar); + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/SystemImageServiceImpl.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/SystemImageServiceImpl.java new file mode 100644 index 0000000..89f2aa0 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/SystemImageServiceImpl.java @@ -0,0 +1,26 @@ +package com.blockchain.server.sysconf.service.impl; + +import com.blockchain.server.sysconf.entity.SystemImage; +import com.blockchain.server.sysconf.mapper.SystemImageMapper; +import com.blockchain.server.sysconf.service.SystemImageService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author: Liusd + * @create: 2019-03-25 13:55 + **/ +@Service +public class SystemImageServiceImpl implements SystemImageService { + + @Autowired + private SystemImageMapper systemImageMapper; + + @Override + public List systemImageList(String type, String status) { + return systemImageMapper.selectByTypeAndStatus(type,status); + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/SystemNoticeServiceImpl.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/SystemNoticeServiceImpl.java new file mode 100644 index 0000000..902a59e --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/SystemNoticeServiceImpl.java @@ -0,0 +1,28 @@ +package com.blockchain.server.sysconf.service.impl; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.server.sysconf.common.constants.NoticeConstant; +import com.blockchain.server.sysconf.entity.SystemNotice; +import com.blockchain.server.sysconf.mapper.SystemNoticeMapper; +import com.blockchain.server.sysconf.service.SystemNoticeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author: Liusd + * @create: 2019-03-25 13:55 + **/ +@Service +public class SystemNoticeServiceImpl implements SystemNoticeService { + + @Autowired + private SystemNoticeMapper systemNoticeMapper; + + + @Override + public List systemNoticeList(String languages) { + return systemNoticeMapper.selectByStatusAndLanguages(NoticeConstant.STATUS_SHOW,languages); + } +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/VersionServiceImpl.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/VersionServiceImpl.java new file mode 100644 index 0000000..ec5913d --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/VersionServiceImpl.java @@ -0,0 +1,48 @@ +package com.blockchain.server.sysconf.service.impl; + +import com.blockchain.common.base.enums.BaseResultEnums; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.server.sysconf.common.constants.VersionConstant; +import com.blockchain.server.sysconf.entity.Version; +import com.blockchain.server.sysconf.mapper.VersionMapper; +import com.blockchain.server.sysconf.service.VersionService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Service +public class VersionServiceImpl implements VersionService { + + @Autowired + private VersionMapper versionMapper; + + @Override + public Version findNewVersion(String device) { + if (StringUtils.isBlank(device)) { + throw new BaseException(BaseResultEnums.PARAMS_ERROR); + } + return versionMapper.findNewVersion(device); + } + + @Override + public List listAll(String device) { + return versionMapper.listAll(device); + } + + @Override + public Map findNewVersionAll() { + Version versionIos = versionMapper.findNewVersion(VersionConstant.SYSTEMTYPE_IOS); + Version versionAndroid = versionMapper.findNewVersion(VersionConstant.SYSTEMTYPE_ANDROID); + Map VersionMap = new HashMap(); + VersionMap.put(VersionConstant.SYSTEMTYPE_IOS, versionIos); + VersionMap.put(VersionConstant.SYSTEMTYPE_ANDROID, versionAndroid); + return VersionMap; + } + + + + + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/WgtVersionServiceImpl.java b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/WgtVersionServiceImpl.java new file mode 100644 index 0000000..a87cdab --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/java/com/blockchain/server/sysconf/service/impl/WgtVersionServiceImpl.java @@ -0,0 +1,28 @@ +package com.blockchain.server.sysconf.service.impl; + + +import com.blockchain.server.sysconf.entity.WgtVersion; +import com.blockchain.server.sysconf.mapper.WgtVersionMapper; +import com.blockchain.server.sysconf.service.WgtVersionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Service +public class WgtVersionServiceImpl implements WgtVersionService { + @Autowired + private WgtVersionMapper wgtVersionMapper; + + + @Override + public WgtVersion findNewWgtVersion() { + return wgtVersionMapper.findNewWgtVersion(); + } + + @Override + public List listAll() { + return wgtVersionMapper.listAll(); + } + +} diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/application.yml b/blockchain-server/blockchain-server-sysconf/src/main/resources/application.yml new file mode 100644 index 0000000..95aad49 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/application.yml @@ -0,0 +1,3 @@ +#日志配置路径 +logging: + config: classpath:logback/logback-${spring.cloud.config.profile}.xml \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/bootstrap.yml b/blockchain-server/blockchain-server-sysconf/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..872e651 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/bootstrap.yml @@ -0,0 +1,22 @@ +server: + port: 8121 +#注册中心 +eureka: + client: + service-url: + defaultZone: http://eureka:8001/eureka/ + instance: + prefer-ip-address: true + instance-id: ${spring.cloud.client.ip-address}:${server.port} + hostname: ${spring.cloud.client.ip-address} +spring: + cloud: + #配置中心 + config: + discovery: + service-id: dapp-config-server + enabled: true + profile: dev + name: springconf,springcloudconf,txconf,xssconf,dbconf,redisconf,ipconf + application: + name: dapp-sysconf-server diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/i18n/MessgesBundle_zh_CN.properties b/blockchain-server/blockchain-server-sysconf/src/main/resources/i18n/MessgesBundle_zh_CN.properties new file mode 100644 index 0000000..f5db252 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/i18n/MessgesBundle_zh_CN.properties @@ -0,0 +1,5 @@ +#\u516C\u5171 +ERROR_PARAMETER_NOTNULL=\u53C2\u6570\u4E0D\u80FD\u4E3A\u7A7A,\u8BF7\u68C0\u67E5\u53C2\u6570 +RESULT_ERROR_MSG=\u672A\u77E5\u5F02\u5E38 +LOGIN_INVALID_MSG=\u767B\u5F55\u4FE1\u606F\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55 +SYSTEM_BUSY_MSG=\u7F51\u7EDC\u7E41\u5FD9\uFF01 \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/logback/logback-dev.xml b/blockchain-server/blockchain-server-sysconf/src/main/resources/logback/logback-dev.xml new file mode 100644 index 0000000..9a0e324 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/logback/logback-dev.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/logback/logback-pro.xml b/blockchain-server/blockchain-server-sysconf/src/main/resources/logback/logback-pro.xml new file mode 100644 index 0000000..5b55059 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/logback/logback-pro.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/AboutUsMapper.xml b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/AboutUsMapper.xml new file mode 100644 index 0000000..c1b7bd2 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/AboutUsMapper.xml @@ -0,0 +1,26 @@ + + + + + conf_about_us + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/AdSliderMapper.xml b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/AdSliderMapper.xml new file mode 100644 index 0000000..313daa9 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/AdSliderMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/ContactUsMapper.xml b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/ContactUsMapper.xml new file mode 100644 index 0000000..a916cb4 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/ContactUsMapper.xml @@ -0,0 +1,34 @@ + + + + + conf_contact_us + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/HelpCenterMapper.xml b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/HelpCenterMapper.xml new file mode 100644 index 0000000..ca8026a --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/HelpCenterMapper.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/NewsMapper.xml b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/NewsMapper.xml new file mode 100644 index 0000000..1eb9719 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/NewsMapper.xml @@ -0,0 +1,84 @@ + + + + + + id + + conf_news + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/ProjectCenterMapper.xml b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/ProjectCenterMapper.xml new file mode 100644 index 0000000..bab288a --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/ProjectCenterMapper.xml @@ -0,0 +1,49 @@ + + + + conf_project_center_info + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/ProjectCenterStarMapper.xml b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/ProjectCenterStarMapper.xml new file mode 100644 index 0000000..c4ee138 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/ProjectCenterStarMapper.xml @@ -0,0 +1,15 @@ + + + + conf_project_center_star + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/SystemImageMapper.xml b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/SystemImageMapper.xml new file mode 100644 index 0000000..9453e01 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/SystemImageMapper.xml @@ -0,0 +1,17 @@ + + + + conf_system_image + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/SystemNoticeMapper.xml b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/SystemNoticeMapper.xml new file mode 100644 index 0000000..06fac6d --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/SystemNoticeMapper.xml @@ -0,0 +1,17 @@ + + + + conf_system_notice + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/UserAgreementMapper.xml b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/UserAgreementMapper.xml new file mode 100644 index 0000000..0d51b5a --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/UserAgreementMapper.xml @@ -0,0 +1,22 @@ + + + + + conf_user_agreement + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/VersionMapper.xml b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/VersionMapper.xml new file mode 100644 index 0000000..36e877c --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/VersionMapper.xml @@ -0,0 +1,39 @@ + + + + + conf_version + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/WgtVersionMapper.xml b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/WgtVersionMapper.xml new file mode 100644 index 0000000..8201921 --- /dev/null +++ b/blockchain-server/blockchain-server-sysconf/src/main/resources/mapper/WgtVersionMapper.xml @@ -0,0 +1,28 @@ + + + + + conf_wgt_version + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/pom.xml b/blockchain-server/blockchain-server-user/pom.xml new file mode 100644 index 0000000..a503ff3 --- /dev/null +++ b/blockchain-server/blockchain-server-user/pom.xml @@ -0,0 +1,68 @@ + + + + blockchain-server + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + blockchain-server-user + + + com.blockchain + blockchain-server-base + 1.0-SNAPSHOT + + + com.blockchain + blockchain-common-tx + 1.0-SNAPSHOT + + + com.blockchain + blockchain-common-base + 1.0-SNAPSHOT + + + + + com.sun.mail + javax.mail + 1.6.2 + + + + + com.aliyun + aliyun-java-sdk-core + 4.0.3 + + + + + com.gexin.platform + gexin-rp-sdk-http + 4.1.0.3 + + + + + + getui-nexus + http://mvn.gt.igexin.com/nexus/content/repositories/releases/ + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/UserApplication.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/UserApplication.java new file mode 100644 index 0000000..24ad0b8 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/UserApplication.java @@ -0,0 +1,20 @@ +package com.blockchain.server.user; + +import com.blockchain.server.base.BaseConf; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.scheduling.annotation.EnableAsync; + +/** + * @author xusm + * @data 2019/2/21 14:06 + */ +@SpringBootApplication(scanBasePackageClasses = {BaseConf.class, UserApplication.class}) +@EnableConfigurationProperties +@EnableAsync +public class UserApplication { + public static void main(String[] args) { + SpringApplication.run(UserApplication.class,args); + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/config/InterceptorConf.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/config/InterceptorConf.java new file mode 100644 index 0000000..5e99cb0 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/config/InterceptorConf.java @@ -0,0 +1,33 @@ +package com.blockchain.server.user.common.config; + +import com.blockchain.server.base.interceptor.LoginInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class InterceptorConf implements WebMvcConfigurer { + @Bean + public LoginInterceptor loginInterceptor() { + return new LoginInterceptor(); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(loginInterceptor()) + .addPathPatterns("/**") + .excludePathPatterns("/inner/**") + .excludePathPatterns("/login/**") + .excludePathPatterns("/webjars/**") + .excludePathPatterns("/swagger-ui.html") + .excludePathPatterns("/swagger-resources/**") + .excludePathPatterns("/v2/api-docs/**"); + } + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**").allowedOrigins("*"); + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/InternationalConstant.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/InternationalConstant.java new file mode 100644 index 0000000..ccb15f3 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/InternationalConstant.java @@ -0,0 +1,35 @@ +package com.blockchain.server.user.common.constants.other; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.HashMap; + +/** + * @author huangxl + * @create 2019-03-08 14:50 + */ +public class InternationalConstant { + private static final Logger LOG = LoggerFactory.getLogger(InternationalConstant.country_code); + private static final String country_code = "{\"86\":\"中国\",\"93\":\"阿富汗\",\"355\":\"阿尔巴尼亚\",\"213\":\"阿尔及利亚\",\"1684\":\"美属萨摩亚\",\"376\":\"安道尔\",\"244\":\"安哥拉\",\"1264\":\"安圭拉\",\"1268\":\"安提瓜和巴布达\",\"54\":\"阿根廷\",\"374\":\"亚美尼亚\",\"297\":\"阿鲁巴\",\"61\":\"澳大利亚\",\"43\":\"奥地利\",\"994\":\"阿塞拜疆\",\"1242\":\"巴哈马\",\"973\":\"巴林\",\"880\":\"孟加拉国\",\"1246\":\"巴巴多斯\",\"375\":\"白俄罗斯\",\"32\":\"比利时\",\"501\":\"伯利兹\",\"229\":\"贝宁\",\"1441\":\"百慕大群岛\",\"975\":\"不丹\",\"591\":\"玻利维亚\",\"387\":\"波斯尼亚和黑塞哥维那\",\"267\":\"博茨瓦纳\",\"55\":\"巴西\",\"673\":\"文莱\",\"359\":\"保加利亚\",\"226\":\"布基纳法索\",\"257\":\"布隆迪\",\"855\":\"柬埔寨\",\"237\":\"喀麦隆\",\"1\":\"加拿大\",\"238\":\"开普\",\"1345\":\"开曼群岛\",\"236\":\"中非共和国\",\"235\":\"乍得\",\"56\":\"智利\",\"57\":\"哥伦比亚\",\"269\":\"科摩罗\",\"682\":\"库克群岛\",\"506\":\"哥斯达黎加\",\"385\":\"克罗地亚\",\"53\":\"古巴\",\"599\":\"库拉索\",\"357\":\"塞浦路斯\",\"420\":\"捷克\",\"243\":\"刚果民主共和国\",\"45\":\"丹麦\",\"253\":\"吉布提\",\"1767\":\"多米尼加\",\"1809\":\"多米尼加共和国\",\"670\":\"东帝汶\",\"593\":\"厄瓜多尔\",\"20\":\"埃及\",\"503\":\"萨尔瓦多\",\"240\":\"赤道几内亚\",\"291\":\"厄立特里亚\",\"372\":\"爱沙尼亚\",\"251\":\"埃塞俄比亚\",\"298\":\"法罗群岛\",\"679\":\"斐济\",\"358\":\"芬兰\",\"33\":\"法国\",\"594\":\"法属圭亚那\",\"689\":\"法属波利尼西亚\",\"241\":\"加蓬\",\"220\":\"冈比亚\",\"995\":\"格鲁吉亚\",\"49\":\"德国\",\"233\":\"加纳\",\"350\":\"直布罗陀\",\"30\":\"希腊\",\"299\":\"格陵兰岛\",\"1473\":\"格林纳达\",\"590\":\"瓜德罗普岛\",\"1671\":\"关岛\",\"502\":\"瓜地马拉\",\"224\":\"几内亚\",\"245\":\"几内亚比绍共和国\",\"592\":\"圭亚那\",\"509\":\"海地\",\"504\":\"洪都拉斯\",\"852\":\"中国香港\",\"36\":\"匈牙利\",\"354\":\"冰岛\",\"91\":\"印度\",\"62\":\"印度尼西亚\",\"98\":\"伊朗\",\"964\":\"伊拉克\",\"353\":\"爱尔兰\",\"972\":\"以色列\",\"39\":\"意大利\",\"225\":\"象牙海岸\",\"1876\":\"牙买加\",\"81\":\"日本\",\"962\":\"约旦\",\"7\":\"哈萨克斯坦\",\"254\":\"肯尼亚\",\"686\":\"基里巴斯\",\"965\":\"科威特\",\"996\":\"吉尔吉斯斯坦\",\"856\":\"老挝\",\"371\":\"拉脱维亚\",\"961\":\"黎巴嫩\",\"266\":\"莱索托\",\"231\":\"利比里亚\",\"218\":\"利比亚\",\"423\":\"列支敦士登\",\"370\":\"立陶宛\",\"352\":\"卢森堡\",\"853\":\"中国澳门\",\"389\":\"马其顿\",\"261\":\"马达加斯加\",\"265\":\"马拉维\",\"60\":\"马来西亚\",\"960\":\"马尔代夫\",\"223\":\"马里\",\"356\":\"马耳他\",\"596\":\"马提尼克\",\"222\":\"毛里塔尼亚\",\"230\":\"毛里求斯\",\"269\":\"马约特\",\"52\":\"墨西哥\",\"373\":\"摩尔多瓦\",\"377\":\"摩纳哥\",\"976\":\"蒙古\",\"382\":\"黑山\",\"1664\":\"蒙特塞拉特岛\",\"212\":\"摩洛哥\",\"258\":\"莫桑比克\",\"95\":\"缅甸\",\"264\":\"纳米比亚\",\"977\":\"尼泊尔\",\"31\":\"荷兰\",\"687\":\"新喀里多尼亚\",\"64\":\"新西兰\",\"505\":\"尼加拉瓜\",\"227\":\"尼日尔\",\"234\":\"尼日利亚\",\"47\":\"挪威\",\"968\":\"阿曼\",\"92\":\"巴基斯坦\",\"680\":\"帕劳\",\"970\":\"巴勒斯坦\",\"507\":\"巴拿马\",\"675\":\"巴布亚新几内亚\",\"595\":\"巴拉圭\",\"51\":\"秘鲁\",\"63\":\"菲律宾\",\"48\":\"波兰\",\"351\":\"葡萄牙\",\"1787\":\"波多黎各\",\"974\":\"卡塔尔\",\"242\":\"刚果共和国\",\"262\":\"留尼汪\",\"40\":\"罗马尼亚\",\"7\":\"俄罗斯\",\"250\":\"卢旺达\",\"1869\":\"圣基茨和尼维斯\",\"1758\":\"圣露西亚\",\"508\":\"圣彼埃尔和密克隆岛\",\"1784\":\"圣文森特和格林纳丁斯\",\"685\":\"萨摩亚\",\"378\":\"圣马力诺\",\"239\":\"圣多美和普林西比\",\"966\":\"沙特阿拉伯\",\"221\":\"塞内加尔\",\"381\":\"塞尔维亚\",\"248\":\"塞舌尔\",\"232\":\"塞拉利昂\",\"65\":\"新加坡\",\"1721\":\"圣马丁岛(荷兰部分)\",\"421\":\"斯洛伐克\",\"386\":\"斯洛文尼亚\",\"677\":\"所罗门群岛\",\"252\":\"索马里\",\"27\":\"南非\",\"82\":\"韩国\",\"34\":\"西班牙\",\"94\":\"斯里兰卡\",\"249\":\"苏丹\",\"597\":\"苏里南\",\"268\":\"斯威士兰\",\"46\":\"瑞典\",\"41\":\"瑞士\",\"963\":\"叙利亚\",\"886\":\"中国台湾\",\"992\":\"塔吉克斯坦\",\"255\":\"坦桑尼亚\",\"66\":\"泰国\",\"670\":\"东帝汶\",\"228\":\"多哥\",\"676\":\"汤加\",\"1868\":\"特立尼达和多巴哥\",\"216\":\"突尼斯\",\"90\":\"土耳其\",\"993\":\"土库曼斯坦\",\"1649\":\"特克斯和凯科斯群岛\",\"256\":\"乌干达\",\"380\":\"乌克兰\",\"971\":\"阿拉伯联合酋长国\",\"44\":\"英国\",\"1\":\"美国\",\"598\":\"乌拉圭\",\"998\":\"乌兹别克斯坦\",\"678\":\"瓦努阿图\",\"58\":\"委内瑞拉\",\"84\":\"越南\",\"1340\":\"英属处女群岛\",\"1284\":\"美属维尔京群岛\",\"967\":\"也门\",\"260\":\"赞比亚\",\"263\":\"津巴布韦\"}"; + private static HashMap map; + public static final String DEFAULT_CODE = "86"; + + static { + ObjectMapper objectMapper = new ObjectMapper(); + try { + map = objectMapper.readValue(country_code, new TypeReference>() { + }); + } catch (IOException e) { + LOG.error("无法转换国际信息"); + map = new HashMap<>(); + } + } + + public static String getInternational(String code) { + return map.get(code); + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/RedisConstant.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/RedisConstant.java new file mode 100644 index 0000000..2c8a763 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/RedisConstant.java @@ -0,0 +1,43 @@ +package com.blockchain.server.user.common.constants.other; + +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * 缓存到redis的key的规则信息 + * @author huangxl + * @create 2019-02-23 15:45 + */ +public class RedisConstant { + private static final String SMS_HASH_KEY = "user:sms:{0}:{1}";//短信缓存值,第一个是短信类型,第二个是手机号 + private static final String FILE_UPLOAD_TIMES_KEY = "user:upload:times:{0}:{1}";//上传文件次数的值,第一个是用户id,第二个是日期格式yyyyMMdd + + private static final String EMAIL_CODE_KEY = "user:email:{0}";//绑定邮箱时存放验证码的key + /** + * 获取手机验证码的redis key + * + * @param mobilePhone 手机号 + * @param type 类型 + */ + public static String getSmsHashKey(String type, String mobilePhone) { + return MessageFormat.format(SMS_HASH_KEY, type, mobilePhone); + } + + /** + * 获取文件上传次数的key + * @param userId 用户id + */ + public static String getFileUploadTimesKey(String userId){ + String date = new SimpleDateFormat("yyyyMMdd").format(new Date()); + return MessageFormat.format(FILE_UPLOAD_TIMES_KEY,userId,date); + } + + /** + * 获取绑定邮箱时缓存的key + * @param email 邮箱 + */ + public static String getEmailCodeKey(String email) { + return MessageFormat.format(EMAIL_CODE_KEY,email); + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/StringFormatConstant.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/StringFormatConstant.java new file mode 100644 index 0000000..8ebe017 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/StringFormatConstant.java @@ -0,0 +1,37 @@ +package com.blockchain.server.user.common.constants.other; + +import java.text.MessageFormat; + +/** + * 存放一些字符串格式的类 + * + * @author huangxl + * @create 2019-02-24 11:06 + */ +public class StringFormatConstant { + private static final String USER_CHANGE_ACCOUNT_FORMAT = "用户【{0}】修改手机号,手机号「{1}」-->「{2}」,国际标识号「{3}」-->「{4}」"; + private static final String USER_BIND_GOOGLE_AUTHENTICATOR = "用户【{0}】绑定了谷歌验证器"; + private static final String USER_BIND_EMAIL = "用户【{0}】绑定了邮箱"; + private static final String USER_BIND_LOGIN_PASSWORD = "用户【{0}】首次设置了登录密码"; + private static final String USER_CHANGE_LOGIN_PASSWORD = "用户【{0}】修改了登录密码"; + + public static String getUserChangeAccountFormat(String userId, String oldTel, String newTel, String oldCode, String newCode) { + return MessageFormat.format(USER_CHANGE_ACCOUNT_FORMAT, userId, oldTel, newTel, oldCode, newCode); + } + + public static String getUserBindGoogleAuthenticator(String userId) { + return MessageFormat.format(USER_BIND_GOOGLE_AUTHENTICATOR, userId); + } + + public static String getUserBindEmail(String userId) { + return MessageFormat.format(USER_BIND_EMAIL, userId); + } + + public static String getUserBindLoginPassword(String userId) { + return MessageFormat.format(USER_BIND_LOGIN_PASSWORD, userId); + } + + public static String getUserChangeLoginPassword(String userId) { + return MessageFormat.format(USER_CHANGE_LOGIN_PASSWORD, userId); + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/UserFileUploadConstant.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/UserFileUploadConstant.java new file mode 100644 index 0000000..1b1bad8 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/other/UserFileUploadConstant.java @@ -0,0 +1,10 @@ +package com.blockchain.server.user.common.constants.other; + +/** + * 上传文件地址类 + */ +public class UserFileUploadConstant { + public static final String UPLOAD_HEAD_IMG = "headImg/"; //头像上传文件前缀 + public static final String UPLOAD_IDENTITY = "identity/"; //初级认证文件上传路径 + public static final String UPLOAD_HIGH_AUTH = "highAuth/"; //高级认证文件上传路径 +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/AuthenticationApplyConstant.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/AuthenticationApplyConstant.java new file mode 100644 index 0000000..b6de7ea --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/AuthenticationApplyConstant.java @@ -0,0 +1,14 @@ +package com.blockchain.server.user.common.constants.sql; + +/** + * @author huangxl + * @create 2019-02-26 14:29 + */ +public class AuthenticationApplyConstant { + public static final String TYPE_DEFAULT = "IDCARD";//证件类型:身份证 + + public static final String STATUS_WAIT = GlobalConstant.STATUS_WAIT;//等待状态 + public static final String STATUS_YES = GlobalConstant.STATUS_YES;//同意状态 + public static final String STATUS_NO = GlobalConstant.STATUS_NO;//拒绝、新建状态 + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/ConfigConstant.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/ConfigConstant.java new file mode 100644 index 0000000..dd01680 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/ConfigConstant.java @@ -0,0 +1,12 @@ +package com.blockchain.server.user.common.constants.sql; + +/** + * @author huangxl + * @create 2019-02-25 17:55 + */ +public class ConfigConstant { + public static final String STATUS_YES = GlobalConstant.STATUS_YES;//有效状态 + public static final String STATUS_NO = GlobalConstant.STATUS_NO;//无效状态 + + public static final String KEY_UPLOAD_LIMIT = "UPLOAD_LIMIT";//上传文件限制次数配置 +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/GlobalConstant.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/GlobalConstant.java new file mode 100644 index 0000000..0dcc027 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/GlobalConstant.java @@ -0,0 +1,13 @@ +package com.blockchain.server.user.common.constants.sql; + +/** + * @author huangxl + * @create 2019-02-23 14:28 + */ +public class GlobalConstant { + + public static final String STATUS_WAIT = "W";//等待状态 + public static final String STATUS_YES = "Y";//同意状态 + public static final String STATUS_NO = "N";//拒绝、新建状态 + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/SmsCountConstant.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/SmsCountConstant.java new file mode 100644 index 0000000..ef7cd5a --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/SmsCountConstant.java @@ -0,0 +1,15 @@ +package com.blockchain.server.user.common.constants.sql; + +/** + * @author huangxl + * @create 2019-02-23 16:50 + */ +public class SmsCountConstant { + public static final String SMS_TYPE_REGISTER = "REGISTER"; + public static final String SMS_TYPE_SET_PASSWORD = "UPDATE_PW"; + public static final String SMS_TYPE_UPDATE_ACCOUNT = "UPDATE_ACCOUNT"; + public static final String SMS_TYPE_LOGIN = "LOGIN"; + public static final String SMS_TYPE_FORGET_PASSWORD = "FORGET_PASSWORD"; + public static final String SMS_TYPE_CHANGE_WALLET_PASSWORD = "UPDATE_WALLET_PASSWORD"; + public static final String SMS_TYPE_BIND = "BIND"; +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserAuthenticationConstant.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserAuthenticationConstant.java new file mode 100644 index 0000000..baf9e61 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserAuthenticationConstant.java @@ -0,0 +1,21 @@ +package com.blockchain.server.user.common.constants.sql; + +/** + * @author Harvey + * @date 2019/3/8 17:17 + * @user WIN10 + */ +public class UserAuthenticationConstant { + // 通过 + public static final String SUCCESS = "Y"; + // 驳回 + public static final String REJECT = "N"; + //失败 + public static final String FAILED = "F"; + // 等待 + public static final String WAITING = "W"; + // 初级 + public static final String LOW = "low"; + // 高级 + public static final String HIGH = "high"; +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserInfoConstant.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserInfoConstant.java new file mode 100644 index 0000000..6807bb8 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserInfoConstant.java @@ -0,0 +1,16 @@ +package com.blockchain.server.user.common.constants.sql; + +/** + * @author huangxl + * @create 2019-02-23 14:39 + */ +public class UserInfoConstant { + //初级认证状态 + public static final String STATUS_LOW_AUTH_WAIT = GlobalConstant.STATUS_WAIT;//等待状态 + public static final String STATUS_LOW_AUTH_YES = GlobalConstant.STATUS_YES;//同意状态 + public static final String STATUS_LOW_AUTH_NO = GlobalConstant.STATUS_NO;//拒绝、新建状态 + //高级认证状态 + public static final String STATUS_HIGHT_AUTH_WAIT = GlobalConstant.STATUS_WAIT;//等待状态 + public static final String STATUS_HIGHT_AUTH_YES = GlobalConstant.STATUS_YES;//同意状态 + public static final String STATUS_HIGHT_AUTH_NO = GlobalConstant.STATUS_NO;//拒绝、新建状态 +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserListConstant.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserListConstant.java new file mode 100644 index 0000000..2bf8721 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserListConstant.java @@ -0,0 +1,21 @@ +package com.blockchain.server.user.common.constants.sql; + +/** + * @author huangxl + * @create 2019-02-23 14:35 + */ +public class UserListConstant { + //名单类型 + public static final String LIST_TYPE_BLACK = "BLACK"; + public static final String LIST_TYPE_WHITE = "WHITE"; + + + public static final String TYPE_BAN_OUT = "BAN_TX";//禁止提现 + public static final String TYPE_BAN_LOGIN = "BAN_LOGIN";//禁止登录 + public static final String TYPE_BAN_TRANSACTION = "BAN_TRANSACTION";//禁止交易 + public static final String TYPE_FREE_TRANSACTION = "FREE_TRANSACTION";//免交易手续费 + public static final String TYPE_FREE_TX = "FREE_TX";//免提现手续费 + public static final String TYPE_PUSH_AD = "FREE_PUSH_AD";//发布广告 + + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserLoginLogConstant.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserLoginLogConstant.java new file mode 100644 index 0000000..ae52834 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserLoginLogConstant.java @@ -0,0 +1,10 @@ +package com.blockchain.server.user.common.constants.sql; + +/** + * @author huangxl + * @create 2019-02-24 16:05 + */ +public class UserLoginLogConstant { + public static final String STATUS_SUCCESS = "SUCCESS";//等待状态 + public static final String STATUS_FAIL = "FAIL";//同意状态 +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserMainConstant.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserMainConstant.java new file mode 100644 index 0000000..0858b77 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserMainConstant.java @@ -0,0 +1,10 @@ +package com.blockchain.server.user.common.constants.sql; + +/** + * @author huangxl + * @create 2019-02-25 11:08 + */ +public class UserMainConstant { + public static final String INTERNATIONAL_CODE_DEFAULT = "86"; + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserOptConstant.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserOptConstant.java new file mode 100644 index 0000000..b32ce82 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/constants/sql/UserOptConstant.java @@ -0,0 +1,14 @@ +package com.blockchain.server.user.common.constants.sql; + +/** + * @author huangxl + * @create 2019-02-24 11:31 + */ +public class UserOptConstant { + public static final String TYPE_CHANGE_TEL = "CHANGE_TEL";//修改手机号 + public static final String TYPE_CHANGE_EMAIL = "CHANGE_EMAIL";//修改邮箱 + public static final String TYPE_BIND_EMAIL = "CHANGE_EMAIL";//修改邮箱 + public static final String TYPE_CHANGE_LOGIN_PASSWORD = "CHANGE_LOGIN_PASSWORD";//修改登录密码 + public static final String TYPE_SET_LOGIN_PASSWORD = "SET_LOGIN_PASSWORD";//首次设置登录密码 + public static final String TYPE_BIND_GOOGLE_AUTHENTICATOR = "BIND_AUTHENTICATOR";//绑定谷歌验证器 +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/enums/SmsCountEnum.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/enums/SmsCountEnum.java new file mode 100644 index 0000000..053addb --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/enums/SmsCountEnum.java @@ -0,0 +1,58 @@ +package com.blockchain.server.user.common.enums; + +import com.blockchain.server.user.common.constants.sql.SmsCountConstant; + +import java.util.HashMap; +import java.util.Map; + +public enum SmsCountEnum { + + SMS_COUNT_REGISTER(SmsCountConstant.SMS_TYPE_REGISTER, 10, "register", "SMS_165691638"), //单天最多收10条注册短信 + SMS_COUNT_SET_PASSWORD(SmsCountConstant.SMS_TYPE_SET_PASSWORD, 10, "setPassword", "SMS_165691638"), //设置密码验证码 + SMS_COUNT_UPDATE_ACCOUNT(SmsCountConstant.SMS_TYPE_UPDATE_ACCOUNT, 5, "updateAccount", "SMS_165691638"), //更新账号信息,修改手机号(用户名) + SMS_COUNT_LOGIN(SmsCountConstant.SMS_TYPE_LOGIN, 10, "login", "SMS_165691638"), //登陆,短信验证码 + SMS_COUNT_FORGET_PASSWORD(SmsCountConstant.SMS_TYPE_FORGET_PASSWORD, 10, "forgetPassword", "SMS_165691638"), //找回密码验证码 + SMS_COUNT_UPDATE_WALLET_PASSWORD(SmsCountConstant.SMS_TYPE_CHANGE_WALLET_PASSWORD, 10, "changeWalletPassword", "SMS_165691638"),//重置、修改资金密码 + SMS_COUNT_BIND(SmsCountConstant.SMS_TYPE_BIND, 10, "bind", "SMS_165691638"),//绑定 + ; + + private String type;//类型,用于保存到数据库 + private int maxCount;//每天最大短信 + private String key;//存到缓存的key标识 + private String templeteId;//模板id + + SmsCountEnum(String type, int maxCount, String key, String templeteId) { + this.type = type; + this.maxCount = maxCount; + this.key = key; + this.templeteId = templeteId; + } + + private static final Map map = new HashMap<>(); + + static { + for (SmsCountEnum smsCountEnum : values()) { + map.put(smsCountEnum.type, smsCountEnum); + } + } + + public static SmsCountEnum parse(int type) { + return map.get(type); + } + + public String getType() { + return type; + } + + public int getMaxCount() { + return this.maxCount; + } + + public String getKey() { + return key; + } + + public String getTempleteId() { + return templeteId; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/enums/UserEnums.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/enums/UserEnums.java new file mode 100644 index 0000000..5c52234 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/enums/UserEnums.java @@ -0,0 +1,93 @@ +package com.blockchain.server.user.common.enums; + +/** + * @author huangxl + * @data 2019/2/21 20:53 + */ +public enum UserEnums { + USER_EXISTS(1100, "该用户已存在", "The user already exists", "該用戶已存在"), + USER_NOT_EXISTS(1101, "不存在该用户", "The user does not exist", "不存在該用戶"), + LOGIN_PASSWORD_ERROR(1102, "用户名密码错误", "Wrong username and password", "用戶名密碼錯誤"), + LOGIN_FORBIDDEN(1103, "你被列入黑名单,禁止登录", "You are blacklisted from logging in", "你被列入黑名單,禁止登錄"), + SMS_VERIFY_FAIL(1104, "手机号验证码错误", "Wrong verification code for mobile phone number", "手機號驗證碼錯誤"), + SMS_CODE_NOT_EXIST(1105, "请先获取验证码信息", "Please get the captcha information first", "請先獲取驗證碼資訊"), + PHONE_FORMAT_ERROR(1106, "手机号格式不正确", "The phone number format is not correct", "手機號格式不正確"), + FORMAT_ERROR(1106, "手机号或邮箱格式不正确", "The phone number or email format is not correct", "手機號或郵箱格式不正確"), + VERIFY_CODE_TYPE_ERROR(1107, "没有此类型短信", "There is no text message of this type", "沒有此類型短信"), + VERIFY_CODE_OVER_COUNT(1108, "今日获取验证码到达上限", "The upper limit of the captcha is reached today", "今日獲取驗證碼到達上限"), + USER_PHONE_EXISTS(1109, "该手机号或邮箱已存在", "The phone number or email address already exists", "該手機號或郵箱已存在"), + USER_PASSWORD_ERROR_FORMAT(1110, "密码只能是6-16位数字、字母和特殊字符的组合", "Passwords can only be 6-16 digit combinations of Numbers, letters, and special characters", "密碼只能是6-16位數字、字母和特殊字元的組合"), + INVALID_INVITATION_CODE(1111, "无效的邀请码信息", "Invalid invitation code information", "無效的邀請碼資訊"), + INVALID_NICK_NAME(1112, "昵称不能包含特殊字符", "Nicknames cannot contain special characters", "昵稱不能包含特殊字元"), + PHONE_NOT_CHANGE(1113, "手机号码与当前用户一致", "The phone number is the same as the current user", "手機號碼與當前用戶一致"), + GOOGLE_SECRET_KEY_FAIL(1114, "获取谷歌安全码失败", "Failed to get Google security code", "獲取穀歌安全碼失敗"), + GOOGLE_SECRET_KEY_EXIST(1115, "你已绑定谷歌验证器,请勿重复绑定", "You have bound the Google validator. Do not repeat the binding", "你已綁定穀歌驗證器,請勿重複綁定"), + GOOGLE_AUTH_FAIL(1116, "谷歌验证器验证失败", "Google verifier validation failed", "穀歌驗證器驗證失敗"), + FILE_UPLOAD_ERROR(1117, "文件上传失败", "File upload failed", "文檔上傳失敗"), + FILE_UPLOAD_LIMIT(1118, "文件上传失败,上传文件次数过多!", "File upload failed, too many times!", "文檔上傳失敗,上傳檔次數過多!"), + FILE_UPLOAD_FORMAT_ERROR(1119, "文件地址错误", "File address error", "文檔地址錯誤"), + FILE_UPLOAD_DEFICIENCY(1120, "请先上传文件", "Please upload the file first", "請先上傳文檔"), + AUTH_WAIT(1121, "认证审核中,请勿重复申请!", "Please do not repeat the application during the certification audit!", "認證審核中,請勿重複申請!"), + AUTH_YES(1122, "你已完成认证!", "You have completed the certification!", "你已完成認證!"), + AUTH_BASIC_BEFORE(1123, "请先完成初级认证!", "Please complete the primary certification first!", "請先完成初級認證!"), + EMAIL_FORMAT_ERROR(1124, "无效的邮箱格式!", "Invalid mailbox format!", "無效的郵箱格式!"), + EMAIL_VERIFY_FAIL(1125, "邮箱验证失败!", "Mailbox authentication failed!", "郵箱驗證失敗!"), + EMAIL_BIND_REPEAT(1126, "绑定失败,你已绑定邮箱", "Failed to bind. You have bound your mailbox", "綁定失敗,你已綁定郵箱"), + PASSWORD_EXIST(1127, "设置失败,你已经设置过密码了", "Setup failed. You have already set the password", "設置失敗,你已經設置過密碼了"), + EMAIL_EXIST(1128, "绑定失败,该邮箱已被绑定!", "The mailbox has been bound!", "綁定失敗,該郵箱已被綁定!"), + PASSWORD_NOT_MATCH(1129, "密码不匹配", "Password mismatch", "密碼不匹配"), + TRANSACTION_FORBIDDEN(1130, "你被列入黑名单,禁止交易!", "You're blacklisted! No trading!", "你被列入黑名單,禁止交易!"), + CANNOT_FOUND_INTERNATIONAL(1131, "找不到该国家信息", "Country information not available", "找不到該國家信息"), + VERIFY_CODE_DID_NOT_FIND(1034, "验证码已过期或没有获取", "The captcha is expired or not available", "驗證碼已過期或沒有獲取"), + VERIFY_CODE_DID_NOT_MATCH(1035, "您输入的验证码不匹配", "The verification code you entered does not match", "您輸入的驗證碼不匹配"), + WITHDRAW_FORBIDDEN(1136, "你被列入黑名单,禁止提现!", "You are blacklisted and no withdrawal is allowed!", "你被列入黑名單,禁止提現!"), + TRANSACTION_NOT_PASS_HIGH_AUTH(1137, "操作失败,您未通过高级认证!", "Operation failed. You did not pass advanced certification!", "操作失敗,您未通過高級認證!"), + TRANSACTION_NOT_PASS_LOW_AUTH(1137, "操作失败,您未通过初级认证!", "Operation failed. You did not pass primary certification!", "操作失敗,您未通過初級認證!"), + SEND_CODE_ERROR(1138, "验证码发送失败!", "Verification code failed to send!", "驗證碼發送失敗!"), + ; + + + private int code; + private String hkmsg; + private String enMsg; + private String cnmsg; + + UserEnums(int code, String cnmsg, String enMsg, String hkmsg) { + this.code = code; + this.cnmsg = cnmsg; + this.enMsg = enMsg; + this.hkmsg = hkmsg; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getHkmsg() { + return hkmsg; + } + + public void setHkmsg(String hkmsg) { + this.hkmsg = hkmsg; + } + + public String getEnMsg() { + return enMsg; + } + + public void setEnMsg(String enMsg) { + this.enMsg = enMsg; + } + + public String getCnmsg() { + return cnmsg; + } + + public void setCnmsg(String cnmsg) { + this.cnmsg = cnmsg; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/exceprion/UserException.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/exceprion/UserException.java new file mode 100644 index 0000000..2aef9ca --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/exceprion/UserException.java @@ -0,0 +1,52 @@ +package com.blockchain.server.user.common.exceprion; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.user.common.enums.UserEnums; +import lombok.Data; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author huangxl + * @data 2019/2/21 20:51 + */ +@Data +public class UserException extends BaseException { + protected int code; + protected String msg; + + public UserException(int code, String msg) { + this.code = code; + this.msg = msg; + } + + public UserException(UserEnums rs) { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + //可能是定时器调用,避免获取request空指针 + if (servletRequestAttributes == null) { + this.code = rs.getCode(); + this.msg = rs.getCnmsg(); + } else { + HttpServletRequest request = servletRequestAttributes.getRequest(); + String userLocale = HttpRequestUtil.getUserLocale(request); + String msg = ""; + switch (userLocale) { + case BaseConstant.USER_LOCALE_EN_US: + msg = rs.getEnMsg(); + break; + case BaseConstant.USER_LOCALE_ZH_HK: + msg = rs.getHkmsg(); + break; + default: + msg = rs.getCnmsg(); + break; + } + this.code = rs.getCode(); + this.msg = msg; + } + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/CheckUtils.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/CheckUtils.java new file mode 100644 index 0000000..78040c7 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/CheckUtils.java @@ -0,0 +1,148 @@ +package com.blockchain.server.user.common.utils; + +import org.apache.commons.lang3.StringUtils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * \*

Desciption:数据校检验证工具类

+ * \* + * \* CreateTime: 2018/8/10 0010 15:49 + * \* User: YeHao + * \ + */ +public class CheckUtils { + /** + * 校验手机号 + * + * @param phoneNum 手机号 + * @return + */ + public static boolean checkMobilePhone(String phoneNum) { + if (!StringUtils.isEmpty(phoneNum)) { + String reg = "\\d+"; + return phoneNum.matches(reg); + } + return false; + } + + /** + * 校验密码 + * @param password 密码 + * @return + */ + public static boolean checkPassword(String password) { + String pattern = "^([A-Z]|[a-z]|[0-9]|[`~!@#$%^&*.]){6,16}$"; + if (password.matches(pattern)) { + return true; + } + return false; + } + + /** + * 验证邮箱地址是否正确 + * + * @param email 邮箱 + * @return + */ + public static boolean checkEmail(String email) { + boolean flag = false; + try { + String check = "^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\\.[a-zA-Z0-9_-]{2,3}){1,2})$"; + Pattern regex = Pattern.compile(check); + Matcher matcher = regex.matcher(email); + flag = matcher.matches(); + } catch (Exception e) { + flag = false; + } + return flag; + } + + /** + * 校验微信账号 + * + * @param wxCode 微信账号 + * @return + */ + public static boolean checkWeixin(String wxCode) { + + boolean flag = false; + if (!StringUtils.isEmpty(wxCode)) { + + if (!StringUtils.isEmpty(wxCode)) { + if (wxCode.contains("@")) { //验证邮箱号 + String check = "^([a-z0-9A-Z]+[-|.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?.)+[a-zA-Z]{2,}$"; + Pattern regex = Pattern.compile(check); + Matcher matcher = regex.matcher(wxCode); + flag = matcher.matches(); + } else { + String reg1 = "[1-9]\\d{5,19}"; //qq号 6 - 20 + String reg2 = "1[3-9]\\d{9}"; //qq号或者手机号 11 + String reg3 = "[a-zA-Z][-_a-zA-Z0-9]{5,19}"; //微信号带字母的 6-20 + flag = wxCode.matches(reg1) || wxCode.matches(reg2) || wxCode.matches(reg3); + } + } + + } + return flag; + + } + + /** + * 校验支付宝账号 + * + * @param zgbCode 支付宝账号 + * @return + */ + public static boolean checkZhiFuBao(String zgbCode) { + boolean flag = false; + if (checkEmail(zgbCode) || checkWeixin(zgbCode)) { + flag = true; + } + return flag; + } + + /** + * 校检银行卡号 + * + * @param bankCard 银行卡号 + * @return + */ + public static boolean checkBankCard(String bankCard) { + if (bankCard.length() < 15 || bankCard.length() > 19) { + return false; + } + char bit = getBankCardCheckCode(bankCard.substring(0, bankCard.length() - 1)); + if (bit == 'N') { + return false; + } + return bankCard.charAt(bankCard.length() - 1) == bit; + } + + /** + * 从不含校验位的银行卡卡号采用 Luhm 校验算法获得校验位 + * + * @param nonCheckCodeBankCard + * @return + */ + private static char getBankCardCheckCode(String nonCheckCodeBankCard) { + if (nonCheckCodeBankCard == null || nonCheckCodeBankCard.trim().length() == 0 + || !nonCheckCodeBankCard.matches("\\d+")) { + //如果传的不是数据返回N + return 'N'; + } + char[] chs = nonCheckCodeBankCard.trim().toCharArray(); + int luhmSum = 0; + for (int i = chs.length - 1, j = 0; i >= 0; i--, j++) { + int k = chs[i] - '0'; + if (j % 2 == 0) { + k *= 2; + k = k / 10 + k % 10; + } + luhmSum += k; + } + return (luhmSum % 10 == 0) ? '0' : (char) ((10 - luhmSum % 10) + '0'); + } + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/CommonUtils.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/CommonUtils.java new file mode 100644 index 0000000..eb14e84 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/CommonUtils.java @@ -0,0 +1,47 @@ +package com.blockchain.server.user.common.utils; + +/** + * 存放一些规则性的方法 + * + * @author huangxl + * @create 2019-02-23 20:38 + */ +public class CommonUtils { + + /** + * 通过邀请码获取自增码 + * + * @param invitationCode 邀请码 + */ + public static Integer getUserIncrCodeFromInvitationCode(String invitationCode) { + try { + Integer number = Integer.parseInt(invitationCode, 16); + String numberStr = number + ""; + String substring = numberStr.substring(2); + return Integer.parseInt(substring); + } catch (Exception e) { + return null; + } + } + + /** + * 根据自增码和随机数值生成邀请码 + * + * @param incrCode 自增码 + * @param randomNumber 随机数字 + * @return + */ + public static String generateInvitationCode(int incrCode, Integer randomNumber) { + if (randomNumber == null) { + return null; + } + try { + String hexStr = randomNumber + "" + incrCode; + Integer hexNumber = Integer.parseInt(hexStr); + return Integer.toHexString(hexNumber); + } catch (Exception e) { + return null; + } + } + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/EMailTransmitHelper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/EMailTransmitHelper.java new file mode 100644 index 0000000..c086fe1 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/EMailTransmitHelper.java @@ -0,0 +1,137 @@ +package com.blockchain.server.user.common.utils; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.server.user.common.constants.other.RedisConstant; +import com.blockchain.server.user.common.enums.UserEnums; +import com.blockchain.server.user.common.exceprion.UserException; +import lombok.Data; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import javax.mail.Message; +import javax.mail.PasswordAuthentication; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeUtility; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.TimeUnit; + +/** + * @author huangxl + * @create 2019-01-18 18:05 + */ +@Data +@Component +@ConfigurationProperties(prefix = "email") +public class EMailTransmitHelper { + private static final Logger LOG = LoggerFactory.getLogger(EMailTransmitHelper.class); + private String fromAccount;//账号 + private String hostName;//smtp服务器 + private String userName;//发件人姓名 + private String authCode;//授权码、密码 + private int timeout = 30;//超时时间,分钟 + private boolean closed; + @Autowired + private RedisTemplate redisTemplate; + + //邮箱验证码 一般发送 + @Async + public void sendEmail(String emailaddress, String code, String userLocale,String redisKey) { + if (closed) { + //判断是否关闭了邮箱发送功能,如果是关闭状态,直接返回结果 + return; + } + Properties props = new Properties(); + props.put("mail.smtp.host", hostName); + props.put("mail.smtp.socketFactory.port", "465"); + props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); + props.put("mail.smtp.auth", "true"); + props.put("mail.smtp.port", "465"); + Session session = Session.getDefaultInstance(props, + new javax.mail.Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(fromAccount, authCode); + } + }); + try { + String sendUserName = MimeUtility.encodeText(userName);//发件人姓名 + Message message = new MimeMessage(session); + message.setFrom(new InternetAddress(fromAccount,sendUserName)); + message.setRecipients(Message.RecipientType.TO, + InternetAddress.parse(emailaddress)); + message.setSubject(Subject.getSubject(userLocale)); + message.setText(MessageFormat.format(Msg.getSubject(userLocale), code)); + Transport.send(message); + } catch (Exception e) { + LOG.error("发送邮件失败,邮箱地址为:" + emailaddress); + redisTemplate.delete(redisKey);//移除缓存的key + e.printStackTrace(); + throw new UserException(UserEnums.SEND_CODE_ERROR); + } + } + + /** + * 邮箱主题 + */ + private enum Subject { + ZH_CN("【FLAMEX】安全验证"), + ZH_HK("【FLAMEX】安全驗證"), + EN_US("【FLAMEX】Safety Verification"); + private static Map map = new HashMap<>(); + + static { + map.put(BaseConstant.USER_LOCALE_ZH_HK, ZH_HK); + map.put(BaseConstant.USER_LOCALE_ZH_CN, ZH_CN); + map.put(BaseConstant.USER_LOCALE_EN_US, EN_US); + } + + private String value; + + Subject(String value) { + this.value = value; + } + + public static String getSubject(String userLocale) { + return map.getOrDefault(userLocale, map.get(BaseConstant.USER_LOCALE_DEFAULT)).value; + } + + } + + /** + * 邮箱主题 + */ + private enum Msg { + ZH_CN("您好: \n【FLAMEX】本次安全验证为: {0} ,验证码仅用于FLAMEX官网,如非本人操作,请忽略!"), + ZH_HK("您好: \n【FLAMEX】本次安全驗證為: {0} ,驗證碼僅用於FLAMEX官網,如非本人操作,請忽略!"), + EN_US("Dear: \n【FLAMEX】your verification code is: {0} ,The verification code is only used for FLAMEX official website. If you do not operate the code, please ignore it!"); + private static Map map = new HashMap<>(); + + static { + map.put(BaseConstant.USER_LOCALE_ZH_HK, ZH_HK); + map.put(BaseConstant.USER_LOCALE_ZH_CN, ZH_CN); + map.put(BaseConstant.USER_LOCALE_EN_US, EN_US); + } + + private String value; + + Msg(String value) { + this.value = value; + } + + public static String getSubject(String userLocale) { + return map.getOrDefault(userLocale, map.get(BaseConstant.USER_LOCALE_DEFAULT)).value; + } + + } + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/EmailCodeUtils.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/EmailCodeUtils.java new file mode 100644 index 0000000..a5c311f --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/EmailCodeUtils.java @@ -0,0 +1,71 @@ +package com.blockchain.server.user.common.utils; + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.user.common.constants.other.RedisConstant; +import com.blockchain.server.user.common.enums.SmsCountEnum; +import com.blockchain.server.user.common.enums.UserEnums; +import com.blockchain.server.user.common.exceprion.UserException; +import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +@Component +public class EmailCodeUtils { + + + @Autowired + private EMailTransmitHelper eMailTransmitHelper; + @Autowired + private RedisTemplate redisTemplate; + + /** + * 发送短信并保存到缓存中 + * + * @param email 邮箱 + * @param smsCountEnum 枚举类型 + */ + public void sendSmsCodeAndStoreToCache(String email,String userLocale, SmsCountEnum smsCountEnum) { + ExceptionPreconditionUtils.notEmpty(email); //检查参数是否为空 + String key = RedisConstant.getSmsHashKey(smsCountEnum.getKey(),email); + //判断是否存在这个key + Object exist = redisTemplate.opsForValue().get(key); + if (exist != null) { + Long expire = redisTemplate.getExpire(key); + //判断剩余超时时间是否已经过了一半,如果还剩余超过一半的时间,则不发送验证码 + if (expire != null && expire != -1 && ((expire / 1000 / 60) >= (eMailTransmitHelper.getTimeout() / 2))) { + return; + } + } + String code; + if(!eMailTransmitHelper.isClosed()){ + code = RandomStringUtils.random(6, false, true); + redisTemplate.opsForValue().set(RedisConstant.getSmsHashKey(smsCountEnum.getKey(),email),code,eMailTransmitHelper.getTimeout(), TimeUnit.MINUTES); + eMailTransmitHelper.sendEmail(email,code,userLocale,key); + } + } + + /** + * @Description: 验证邮箱与验证码 + * @Param: [verifyCode, emailaddress] + * @return: void + * @Author: Liu.sd + * @Date: 2019/6/26 + */ + public void validateVerifyCode(String verifyCode, String emailaddress, SmsCountEnum smsCountEnum) { + if(eMailTransmitHelper.isClosed()){ + return; + } + ExceptionPreconditionUtils.notEmpty(verifyCode, emailaddress); //检查参数是否为空 + String key = RedisConstant.getSmsHashKey(smsCountEnum.getKey(),emailaddress); + if(!redisTemplate.hasKey(key)){ + throw new UserException(UserEnums.VERIFY_CODE_DID_NOT_FIND); + } + String code = (String) redisTemplate.opsForValue().get(key); + if(!verifyCode.equals(code)){ + throw new UserException(UserEnums.VERIFY_CODE_DID_NOT_MATCH); + } + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/FileUploadHelper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/FileUploadHelper.java new file mode 100644 index 0000000..d59043a --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/FileUploadHelper.java @@ -0,0 +1,174 @@ +package com.blockchain.server.user.common.utils; + +import com.blockchain.server.user.common.enums.UserEnums; +import com.blockchain.server.user.common.exceprion.UserException; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.multipart.MultipartFile; +import sun.misc.BASE64Decoder; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class FileUploadHelper { + /** + * 保存文件 + * + * @param file 文件 + * @param relativeDir 文件类型 + * @throws IOException 存储异常 + */ + public static String saveFile(MultipartFile file, String rootDir, String relativeDir) throws IOException { + //源文件名 + String name = file.getOriginalFilename(); + //文件后缀名 + String suffix = name.substring(name.lastIndexOf(".")); + //格式化文件路径 + rootDir = formatSeparator(rootDir); + relativeDir = formatSeparator(relativeDir); + //申诉文件保存到本地/服务器 + String fileRelativePath = generateRelativePath(rootDir, relativeDir, suffix); + file.transferTo(new File(rootDir + fileRelativePath)); + return fileRelativePath; + } + + /** + * 将base64字符串存储为jpg文件 + * + * @param imgBase64 base64字符串 + * @param relativeDir 文件相对路径 + * @return 文件名 + * @throws Exception 存储异常 + */ + public static String generateImage(String imgBase64, String rootDir, String relativeDir) throws IOException { //对字节数组字符串进行Base64解码并生成图片 + if (StringUtils.isEmpty(imgBase64)) { + throw new RuntimeException("file base64 string is empty"); + } + BASE64Decoder decoder = new BASE64Decoder(); + //Base64解码 + byte[] b = decoder.decodeBuffer(imgBase64); + for (int i = 0; i < b.length; ++i) { + if (b[i] < 0) {//调整异常数据 + b[i] += 256; + } + } + //格式化文件路径 + rootDir = formatSeparator(rootDir); + relativeDir = formatSeparator(relativeDir); + //文件存放路径 + String relativePath = generateRelativePath(rootDir, relativeDir, ".jpg"); + OutputStream out = null; + try { + out = new FileOutputStream(rootDir + relativePath); + out.write(b); + out.flush(); + return relativePath; + } catch (IOException e) { + throw e; + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + /** + * 检验上传文件的格式,防止恶意插入数据 + * @param filereRelativePaths 多个文件路径,以「,」作为分隔符 + * @param count 总数 + */ + public static void verifyFileNameList(String filereRelativePaths,Integer count){ + String[] files = filereRelativePaths.split(","); + int total = files.length; + if(total == 0){ + throw new UserException(UserEnums.FILE_UPLOAD_DEFICIENCY); + } + if(count!= null && count != total){ + throw new UserException(UserEnums.FILE_UPLOAD_DEFICIENCY); + } + for (int i = 0; i < total; i++) { + verifyFileName(files[i]); + } + } + + /** + * 检验上传文件的格式,防止恶意插入数据 + * @param filereRelativePath 文件的相对路径 + */ + public static void verifyFileName(String filereRelativePath) { + String regex = "\\d{25}"; + String separator = File.separator; + int lastSeparatorIndex = filereRelativePath.lastIndexOf(separator); + int lastPointIndex = filereRelativePath.lastIndexOf("."); + if (lastSeparatorIndex == -1 || lastPointIndex == -1) { + throw new UserException(UserEnums.FILE_UPLOAD_FORMAT_ERROR); + } + filereRelativePath = filereRelativePath.substring(lastSeparatorIndex + 1,lastPointIndex); + if (!filereRelativePath.matches(regex)) { + throw new UserException(UserEnums.FILE_UPLOAD_FORMAT_ERROR); + } + } + + /** + * 文件名命名规则,时间戳+随机数 + * + * @return 文件名 + */ + private static String createFileName() { + String timeFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + + RandomStringUtils.random(8, false, true); + return timeFormat; + } + + /** + * 生成文件所在目录,并返回文件的相对地址 + * + * @param rootPath 文件根目录 + * @param relativeDir 文件相对目录 + * @param suffix 文件后缀名 + * @return 文件相对地址 + */ + private static String generateRelativePath(String rootPath, String relativeDir, String suffix) { + String separator = File.separator; + if (relativeDir.startsWith(separator)) { + relativeDir = relativeDir.substring(1); + } + //格式化后缀 + if (!suffix.startsWith(".")) { + suffix = "." + suffix; + } + String fileDir = rootPath + relativeDir; + File file = new File(fileDir); + if (!file.exists()) { + file.mkdirs(); + } + return relativeDir + createFileName() + suffix; + } + + /** + * 格式化文件分隔符 + * + * @param dir 目录 + * @return 格式化后的目录名 + */ + private static String formatSeparator(String dir) { + String separator = File.separator; + if (StringUtils.isNotEmpty(dir)) { + String replace = separator.equals("\\") ? "\\\\" : "/"; + String format = dir.replaceAll("[\\\\/]+", replace); + if (!format.endsWith(separator)) { + format += separator; + } + return format; + } + return ""; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/GoogleAuthenticatorUtils.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/GoogleAuthenticatorUtils.java new file mode 100644 index 0000000..041753c --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/GoogleAuthenticatorUtils.java @@ -0,0 +1,149 @@ +package com.blockchain.server.user.common.utils; + +import org.apache.commons.codec.binary.Base32; +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +/** + * google身份验证器,java服务端实现 + */ +public class GoogleAuthenticatorUtils { + + // 生成的key长度( Generate secret key length) + public static final int SECRET_SIZE = 10; + + public static final String SEED = "b4d2dfba9791485083e9-259de09f0e42";//g8GjEvTbW5oVSV7avL47357438reyhreyuryetredLDVKs2m0QN7vxRs2im5MDaNCWGmcD2rvcZx + // Java实现随机数算法 + public static final String RANDOM_NUMBER_ALGORITHM = "SHA1PRNG"; + // 最多可偏移的时间 + private static int window_size = 3; // default 3 - max 17 + + /** + * set the windows size. This is an integer value representing the number of + * 30 second windows we allow The bigger the window, the more tolerant of + * clock skew we are. + * + * @param s window size - must be >=1 and <=17. Other values are ignored + */ + public void setWindowSize(int s) { + if (s >= 1 && s <= 17) + window_size = s; + } + + /** + * Generate a random secret key. This must be saved by the server and + * associated with the users account to verify the code displayed by Google + * Authenticator. The user must register this secret on their device. + * 生成一个随机秘钥 + * + * @return secret key + */ + public static String generateSecretKey() { + SecureRandom sr = null; + try { + sr = SecureRandom.getInstance(RANDOM_NUMBER_ALGORITHM); + sr.setSeed(Base64.decodeBase64(SEED)); + byte[] buffer = sr.generateSeed(SECRET_SIZE); + Base32 codec = new Base32(); + byte[] bEncodedKey = codec.encode(buffer); + String encodedKey = new String(bEncodedKey); + return encodedKey; + } catch (NoSuchAlgorithmException e) { +// should never occur... configuration error + } + return null; + } + + /** + * Return a URL that generates and displays a QR barcode. The user scans + * this bar code with the Google Authenticator application on their + * smartphone to register the auth code. They can also manually enter the + * secret if desired + * + * @param user user id (e.g. fflinstone) + * @param host host or system that the code is for (e.g. myapp.com) + * @param secret the secret that was previously generated for this user + * @return the URL for the QR code to scan + */ + public static String getQRBarcodeURL(String user, String host, String secret) { + String format = "http://www.google.com/chart?chs=200x200&chld=M%%7C0&cht=qr&chl=otpauth://totp/%s@%s?secret=%s"; + return String.format(format, user, host, secret); + } + + /** + * 生成一个google身份验证器,识别的字符串,只需要把该方法返回值生成二维码扫描就可以了。 + * + * @param user 账号 + * @param secret 密钥 + * @return + */ + public static String getQRBarcode(String user, String secret) { + String format = "otpauth://totp/%s?secret=%s"; + return String.format(format, user, secret); + } + + /** + * Check the code entered by the user to see if it is valid 验证code是否合法 + * + * @param secret The users secret. + * @param code The code displayed on the users device + * @param timeMsec The time in msec (System.currentTimeMillis() for example) + * @return + */ + public static boolean check_code(String secret, long code, long timeMsec) { + Base32 codec = new Base32(); + byte[] decodedKey = codec.decode(secret); +// convert unix msec time into a 30 second "window" +// this is per the TOTP spec (see the RFC for details) + long t = (timeMsec / 1000L) / 30L; +// Window is used to check codes generated in the near past. +// You can use this value to tune how far you're willing to go. + for (int i = -window_size; i <= window_size; ++i) { + long hash; + try { + hash = verify_code(decodedKey, t + i); + } catch (Exception e) { +// Yes, this is bad form - but +// the exceptions thrown would be rare and a static +// configuration problem + e.printStackTrace(); + throw new RuntimeException(e.getMessage()); +// return false; + } + if (hash == code) { + return true; + } + } +// The validation code is invalid. + return false; + } + + private static int verify_code(byte[] key, long t) throws NoSuchAlgorithmException, InvalidKeyException { + byte[] data = new byte[8]; + long value = t; + for (int i = 8; i-- > 0; value >>>= 8) { + data[i] = (byte) value; + } + SecretKeySpec signKey = new SecretKeySpec(key, "HmacSHA1"); + Mac mac = Mac.getInstance("HmacSHA1"); + mac.init(signKey); + byte[] hash = mac.doFinal(data); + int offset = hash[20 - 1] & 0xF; +// We're using a long because Java hasn't got unsigned int. + long truncatedHash = 0; + for (int i = 0; i < 4; ++i) { + truncatedHash <<= 8; +// We are dealing with signed bytes: +// we just keep the first byte. + truncatedHash |= (hash[offset + i] & 0xFF); + } + truncatedHash &= 0x7FFFFFFF; + truncatedHash %= 1000000; + return (int) truncatedHash; + } +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/SendSmsgCode.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/SendSmsgCode.java new file mode 100644 index 0000000..1b55a85 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/SendSmsgCode.java @@ -0,0 +1,126 @@ +package com.blockchain.server.user.common.utils; + +import com.aliyuncs.CommonRequest; +import com.aliyuncs.CommonResponse; +import com.aliyuncs.DefaultAcsClient; +import com.aliyuncs.IAcsClient; +import com.aliyuncs.http.MethodType; +import com.aliyuncs.profile.DefaultProfile; +import lombok.Data; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +/** + * 短信API服务调用 - 阿里云短信 + **/ +@Data +@Configuration +@Component +@ConfigurationProperties(prefix = "aliyunsms") +public class SendSmsgCode { + @Autowired + private RestTemplate restTemplate; + + private static final Logger LOG = LoggerFactory.getLogger(SendSmsgCode.class); + + private boolean closed;//是否开启 + private long timeout;//超时时间,单位:分钟 + + //阿里云 + private String ACCESS_KEY_ID; + private String ACCESS_SECRET; + private String ENGLISH_MSG; + private String BIG5_MSG; + private String SginName; + private String TemplateCode; + + private String MSG_CODE_PLACEHOLDER = "${code}"; + private String CHINA_CODE = "86"; + private String BIG5_CODE = "852,853,886"; + + /** + * 请求阿里云短信接口,发送短信验证码 + * + * @param mobile 手机号 + * @param smsCode 短信验证码 + * @param templateId 模板ID + * @return + */ + @Async + public void sendSmsg(String mobile, String smsCode, String internationalCode, String templateId) { + if (closed) {//如果没有开启短信,则不发送短信 + return; + } + + DefaultProfile profile = DefaultProfile.getProfile("ap-southeast-1", ACCESS_KEY_ID, ACCESS_SECRET); + IAcsClient client = new DefaultAcsClient(profile); + CommonRequest request = new CommonRequest(); + request.setMethod(MethodType.POST); + //域名,请勿修改 + request.setDomain("dysmsapi.ap-southeast-1.aliyuncs.com"); + //API版本号,请勿修改 + request.setVersion("2018-05-01"); + //接收号码,格式为:国际码+号码,必填 + request.putQueryParameter("To", internationalCode + mobile); + + //国际代码 + if (CHINA_CODE.equals(internationalCode)) { + //请求国内服务器发送短信 + sendTelCode(mobile, smsCode, internationalCode, templateId); + +// //API名称 +// request.setAction("SendMessageWithTemplate"); +// +// request.putQueryParameter("From", SginName); +// request.putQueryParameter("TemplateCode", TemplateCode); +// request.putQueryParameter("TemplateParam", "{\"code\":\"" + smsCode + "\"}"); + } else { + //API名称 + request.setAction("SendMessageToGlobe"); + //短信内容,必填 + if (BIG5_CODE.contains(internationalCode)) { + //香港,用繁体 + request.putQueryParameter("Message", BIG5_MSG.replace(MSG_CODE_PLACEHOLDER, smsCode)); + } else { + //其他,用英文 + request.putQueryParameter("Message", ENGLISH_MSG.replace(MSG_CODE_PLACEHOLDER, smsCode)); + } + + try { + CommonResponse response = client.getCommonResponse(request); + LOG.info("===send sms end==== : {} ", response.getData()); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + } + + /** + * 请求国内服务器发送短信 + */ + private void sendTelCode(String mobile, String smsCode, String internationalCode, String templateId) { + String url = "http://47.112.194.145:9220/sendsms"; + MultiValueMap multiValueMap = new LinkedMultiValueMap<>(); + multiValueMap.add("mobile", mobile); + multiValueMap.add("smsCode", smsCode); + multiValueMap.add("internationalCode", internationalCode); + multiValueMap.add("templateId", templateId); + try { + String res = restTemplate.postForEntity(url, multiValueMap, String.class).getBody(); + LOG.info("===send sms end==== : {} ", res); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/SmsCodeUtils.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/SmsCodeUtils.java new file mode 100644 index 0000000..47d2d91 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/SmsCodeUtils.java @@ -0,0 +1,77 @@ +package com.blockchain.server.user.common.utils; + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.user.common.constants.other.RedisConstant; +import com.blockchain.server.user.common.enums.SmsCountEnum; +import com.blockchain.server.user.common.enums.UserEnums; +import com.blockchain.server.user.common.exceprion.UserException; +import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +@Component +public class SmsCodeUtils { + + @Autowired + private SendSmsgCode sendSmsgCode; + @Autowired + private RedisTemplate redisTemplate; + @Autowired + private EmailCodeUtils emailCodeUtils; + + /** + * 发送短信并保存到缓存中 + * + * @param phone 手机号 + * @param smsCountEnum 枚举类型 + */ + public void sendSmsCodeAndStoreToCache(String phone,String internationalCode, SmsCountEnum smsCountEnum) { + ExceptionPreconditionUtils.notEmpty(phone); //检查参数是否为空 + String code = "666666"; + if(!sendSmsgCode.isClosed()){ + code = RandomStringUtils.random(6, false, true); + } + redisTemplate.opsForValue().set(RedisConstant.getSmsHashKey(smsCountEnum.getKey(),phone),code,sendSmsgCode.getTimeout(), TimeUnit.MINUTES); + sendSmsgCode.sendSmsg(phone,code,internationalCode,smsCountEnum.getTempleteId()); + } + + /** + * 验证手机号和验证码 + * + * @param verifyCode 输入的验证码信息 + * @param phoneNum 手机号 + * @param smsCountEnum 枚举 + */ + public void validateVerifyCode(String verifyCode, String phoneNum, SmsCountEnum smsCountEnum) { + if (CheckUtils.checkEmail(phoneNum)){ + emailCodeUtils.validateVerifyCode(verifyCode, phoneNum, smsCountEnum); + return; + } + if(sendSmsgCode.isClosed()){ + return; + } + ExceptionPreconditionUtils.notEmpty(verifyCode, phoneNum, smsCountEnum); //检查参数是否为空 + String redisKey = RedisConstant.getSmsHashKey(smsCountEnum.getKey(),phoneNum); + if(!redisTemplate.hasKey(redisKey)){ + throw new UserException(UserEnums.SMS_CODE_NOT_EXIST); + } + String code = (String) redisTemplate.opsForValue().get(redisKey); + if(!verifyCode.equals(code)){ + throw new UserException(UserEnums.SMS_VERIFY_FAIL); + } + } + + /** + * 删除缓存的key + * @param phoneNum 手机号 + * @param smsCountEnum 验证码枚举类型 + */ + public void removeKey(String phoneNum, SmsCountEnum smsCountEnum){ + String redisKey = RedisConstant.getSmsHashKey(smsCountEnum.getKey(),phoneNum); + //验证完成,删除缓存信息 + redisTemplate.delete(redisKey); + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/push/PushUtils.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/push/PushUtils.java new file mode 100644 index 0000000..7833b2f --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/push/PushUtils.java @@ -0,0 +1,83 @@ +package com.blockchain.server.user.common.utils.push; + +import com.blockchain.server.user.service.impl.UserServiceImpl; +import com.gexin.rp.sdk.base.IPushResult; +import com.gexin.rp.sdk.base.impl.SingleMessage; +import com.gexin.rp.sdk.base.impl.Target; +import com.gexin.rp.sdk.exceptions.RequestException; +import com.gexin.rp.sdk.http.IGtPush; +import com.gexin.rp.sdk.template.TransmissionTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Component +public class PushUtils { + + @Value("${getuipush.appId}") + private String appId; + @Value("${getuipush.appKey}") + private String appKey; + @Value("${getuipush.masterSecret}") + private String masterSecret; + @Value("${getuipush.host}") + private String host; + @Value("${getuipush.open}") + private boolean open; + + //日志 + private static final Logger LOG = LoggerFactory.getLogger(UserServiceImpl.class); + //是否可离线发送 + private static final boolean OFFLINE = true; + //1为wifi,0为不限制网络环境。根据手机处于的网络情况,决定是否下发 + private static final int PUSH_NETWORK_TYPE = 0; + + + /*** + * 对单个用户推送消息 + * @param clientId + * @param title + * @param content + * @param payLoadMap + */ + public void pushToSingle(String clientId, String title, String content, Map payLoadMap) { + //open为true才发送通知 + if (open) { + //推送对象 + IGtPush push = new IGtPush(host, appKey, masterSecret); + //构建透传消息模板 + TransmissionTemplate template = TemplateUtils.getTransmissionTemplate(appId, appKey, title, content, payLoadMap); + //消息对象 + SingleMessage message = new SingleMessage(); + //是否可离线发送 + message.setOffline(OFFLINE); + //发送模板 + message.setData(template); + //可选,1为wifi,0为不限制网络环境。根据手机处于的网络情况,决定是否下发 + message.setPushNetWorkType(PUSH_NETWORK_TYPE); + //关闭快速域名 + System.setProperty("needOSAsigned", "true"); + //推送目标对象 + Target target = new Target(); + target.setAppId(appId); + //设置发送的客户端Id + target.setClientId(clientId); + //请求返回标识 + IPushResult ret = null; + try { + ret = push.pushMessageToSingle(message, target); + } catch (RequestException e) { + e.printStackTrace(); + ret = push.pushMessageToSingle(message, target, e.getRequestId()); + } + if (ret != null) { + LOG.info(ret.getResponse().toString()); + } else { + LOG.info("PUSH通知推送失败!"); + } + } + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/push/TemplateUtils.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/push/TemplateUtils.java new file mode 100644 index 0000000..d03e2bd --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/common/utils/push/TemplateUtils.java @@ -0,0 +1,108 @@ +package com.blockchain.server.user.common.utils.push; + +import com.gexin.rp.sdk.base.payload.APNPayload; +import com.gexin.rp.sdk.template.TransmissionTemplate; + +import java.text.MessageFormat; +import java.util.Map; + +public class TemplateUtils { + + //透传消息体 + private static final String TRANSMISSION_CONTENT = "title:\"{0}\",content:\"{1}\",payload:{2}"; + //透传消息设置,1为强制启动应用,客户端接收到消息后就会立即启动应用;2为等待应用启动 + private static final int TRANSMISSION_TYPE = 2; + + public static TransmissionTemplate getTransmissionTemplate(String appId, String appKey, String title, + String content, Map payLoadMap) { + //封装透传数据为JSON格式 + String payLoad = getPayLoad(payLoadMap); + //封装IOS APNs通知参数 + APNPayload apnPayLoad = getAPNPayload(content, payLoadMap); + //封装透传消息体 + String transmissionContent = getTransmissionContent(title, content, payLoad); + //构建透传模板 + return getTransmissionTemplate(appId, appKey, transmissionContent, apnPayLoad); + } + + /**** + * 透传消息模板 + * @param appId 应用Id + * @param appKey 应用key + * @param transmissionContent 透传消息 + * @return + */ + private static TransmissionTemplate getTransmissionTemplate(String appId, String appKey, String transmissionContent, + APNPayload apnPayload) { + TransmissionTemplate template = new TransmissionTemplate(); + template.setAppId(appId); + template.setAppkey(appKey); + //透传消息设置 + template.setTransmissionType(TRANSMISSION_TYPE); + //透传消息设置 + template.setTransmissionContent(transmissionContent); + //设置苹果离线发送 + template.setAPNInfo(apnPayload); + return template; + } + + /*** + * 封装透传消息体 + * @param title + * @param content + * @param payload + * @return + */ + private static String getTransmissionContent(String title, String content, String payload) { + String format = MessageFormat.format(TRANSMISSION_CONTENT, title, content, payload); + return "{" + format + "}"; + } + + /*** + * 封装透传数据 + * @param payload + * @return + */ + private static String getPayLoad(Map payload) { + StringBuilder str = new StringBuilder(); + str.append("{"); + if (payload != null && payload.size() > 0) { + //循环传递参数 + for (Map.Entry entry : payload.entrySet()) { + str.append(entry.getKey()); + str.append(":"); + //String类型的数据加"" + if (entry.getValue() instanceof String) { + str.append("\"" + entry.getValue() + "\""); + } + str.append(","); + } + //删除最后一个逗号 + str.deleteCharAt(str.lastIndexOf(",")); + } + str.append("}"); + return str.toString(); + } + + /*** + * 构建IOS APNs通知参数 + * @param content + * @param payload + * @return + */ + private static APNPayload getAPNPayload(String content, Map payload) { + APNPayload apnPayload = new APNPayload(); + //在已有数字基础上加1显示,设置为-1时,在已有数字上减1显示,设置为数字时,显示指定数字 + apnPayload.setAutoBadge("+1"); + //推送直接带有透传数据 + apnPayload.setContentAvailable(1); + //添加透传数据 + for (Map.Entry entry : payload.entrySet()) { + //透传数据 + apnPayload.addCustomMsg(entry.getKey(), entry.getValue()); + } + //简单模式APNPayload.SimpleMsg + apnPayload.setAlertMsg(new APNPayload.SimpleAlertMsg(content)); + return apnPayload; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/LoginController.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/LoginController.java new file mode 100644 index 0000000..5b38663 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/LoginController.java @@ -0,0 +1,314 @@ +package com.blockchain.server.user.controller; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.constant.TokenTypeEnums; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.SessionUserDTO; +import com.blockchain.common.base.dto.TokenDTO; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.common.base.util.RSACoderUtils; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.user.common.constants.other.InternationalConstant; +import com.blockchain.server.user.common.enums.SmsCountEnum; +import com.blockchain.server.user.common.enums.UserEnums; +import com.blockchain.server.user.common.exceprion.UserException; +import com.blockchain.server.user.common.utils.CheckUtils; +import com.blockchain.server.user.common.utils.EmailCodeUtils; +import com.blockchain.server.user.common.utils.SmsCodeUtils; +import com.blockchain.server.user.controller.api.LoginApi; +import com.blockchain.server.user.dto.UserBaseDTO; +import com.blockchain.server.user.entity.UserMain; +import com.blockchain.server.user.service.PushUserService; +import com.blockchain.server.user.service.SmsCountService; +import com.blockchain.server.user.service.UserLoginService; +import com.blockchain.server.user.service.UserMainService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.Date; + +/** + * @author huangxl + * @data 2019/2/21 15:06 + * 用户注册, 登录控制器 + */ +@RestController +@Api(LoginApi.CONTROLLER_API) +@RequestMapping("/login") +public class LoginController { + + private static final Logger LOG = LoggerFactory.getLogger(LoginController.class); + + @Autowired + private UserLoginService userLoginService; + @Autowired + private UserMainService userMainService; + @Autowired + private SmsCountService smsCountService; + @Autowired + private EmailCodeUtils emailCodeUtils; + @Autowired + private PushUserService pushUserService; + @Autowired + private SmsCodeUtils smsCodeUtils; + @Autowired + private RedisTemplate redisTemplate; + + @PostMapping("/password") + @ApiOperation(value = LoginApi.PassWorldLogin.METHOD_NAME, + notes = LoginApi.PassWorldLogin.METHOD_NOTE) + public ResultDTO loginByPassword(@ApiParam(LoginApi.PassWorldLogin.METHOD_API_TEL) @RequestParam(name = "tel") String tel, + @ApiParam(LoginApi.PassWorldLogin.METHOD_API_PASS) @RequestParam(name = "password") String password, + @ApiParam(LoginApi.PassWorldLogin.METHOD_API_CLIENT_ID) @RequestParam(name = "clientId", required = false) String clientId, + HttpServletRequest request) { + UserMain userMain = userLoginService.handleLoginByPassword(tel, password); + return handleAppAfterLogin(userMain, clientId, getUserLocale(request)); + } + + @PostMapping("/loginByCode") + @ApiOperation(value = LoginApi.SmsCodeLogin.METHOD_NAME, + notes = LoginApi.SmsCodeLogin.METHOD_NOTE) + public ResultDTO loginBysmsCode(@ApiParam(LoginApi.SmsCodeLogin.METHOD_API_TEL) @RequestParam(name = "tel") String tel, + @ApiParam(LoginApi.SmsCodeLogin.METHOD_API_CODE) @RequestParam(name = "code") String code, + @ApiParam(LoginApi.SmsCodeLogin.METHOD_API_CLIENT_ID) @RequestParam(name = "clientId", required = false) String clientId, + HttpServletRequest request) { + smsCodeUtils.validateVerifyCode(code, tel, SmsCountEnum.SMS_COUNT_LOGIN); + UserMain userMain = userLoginService.handleLoginByPhoneCode(tel); +// userMainService.selectByMobilePhone(tel); + smsCodeUtils.removeKey(tel, SmsCountEnum.SMS_COUNT_LOGIN); + return handleAppAfterLogin(userMain, clientId, getUserLocale(request)); + } + + @PostMapping("/register") + @ApiOperation(value = LoginApi.Register.METHOD_NAME, + notes = LoginApi.Register.METHOD_NOTE) + public ResultDTO register(@ApiParam(LoginApi.Register.METHOD_API_TEL) @RequestParam(name = "tel") String tel, + @ApiParam(LoginApi.Register.METHOD_API_CODE) @RequestParam(name = "code") String code, + @ApiParam(LoginApi.Register.METHOD_API_INVITATION_CODE) @RequestParam(value = "invitationCode") String invitationCode, + @ApiParam(LoginApi.Register.METHOD_API_PASSWORD) @RequestParam(value = "password", required = false) String password, + @ApiParam(LoginApi.Register.METHOD_API_INTERNATIONAL_CODE) @RequestParam(value = "internationalCode", required = false, defaultValue = InternationalConstant.DEFAULT_CODE) String internationalCode, + @ApiParam(LoginApi.Register.METHOD_API_NICK_NAME) @RequestParam(value = "nickName", required = false) String nickName, + @ApiParam(LoginApi.Register.METHOD_API_CLIENT_ID) @RequestParam(name = "clientId", required = false) String clientId, + HttpServletRequest request + ) { + smsCodeUtils.validateVerifyCode(code, tel, SmsCountEnum.SMS_COUNT_REGISTER); + UserMain userMain = userMainService.handleRegister(tel, invitationCode, internationalCode, password, nickName); + smsCodeUtils.removeKey(tel, SmsCountEnum.SMS_COUNT_REGISTER); + return handleAppAfterLogin(userMain, clientId, getUserLocale(request)); + } + + @PostMapping("/sendLoginCode") + @ApiOperation(value = LoginApi.SendLoginSmsCode.METHOD_NAME, + notes = LoginApi.SendLoginSmsCode.METHOD_NOTE) + public ResultDTO sendLoginSmsCode(@ApiParam(LoginApi.SendLoginSmsCode.METHOD_API_TEL) @RequestParam(name = "tel") String tel, + HttpServletRequest request, @ApiParam(LoginApi.SendLoginSmsCode.METHOD_API_INTERNATIONAL_CODE) @RequestParam(value = "internationalCode", required = false, defaultValue = InternationalConstant.DEFAULT_CODE) String internationalCode) { + UserMain userMain = userMainService.selectByMobilePhone(tel); + if (userMain == null) { + throw new UserException(UserEnums.USER_NOT_EXISTS); + } + if (CheckUtils.checkMobilePhone(tel)) { + smsCountService.handleInsertSmsCode(tel, internationalCode, SmsCountEnum.SMS_COUNT_LOGIN); + } else if (CheckUtils.checkEmail(tel)) { + emailCodeUtils.sendSmsCodeAndStoreToCache(tel, getUserLocale(request), SmsCountEnum.SMS_COUNT_LOGIN); + } else { + throw new UserException(UserEnums.FORMAT_ERROR); + } +// smsCountService.handleInsertSmsCode(tel, internationalCode, SmsCountEnum.SMS_COUNT_LOGIN); + return ResultDTO.requstSuccess(); + } + + @PostMapping("/sendRegisterCode") + @ApiOperation(value = LoginApi.SendRegisterSmsCode.METHOD_NAME, + notes = LoginApi.SendRegisterSmsCode.METHOD_NOTE) + public ResultDTO sendRegisterSmsCode(@ApiParam(LoginApi.SendRegisterSmsCode.METHOD_API_TEL) @RequestParam(name = "tel") String tel, + HttpServletRequest request, @ApiParam(LoginApi.SendRegisterSmsCode.METHOD_API_INTERNATIONAL_CODE) @RequestParam(value = "internationalCode", required = false, defaultValue = InternationalConstant.DEFAULT_CODE) String internationalCode) { + UserMain userMain = userMainService.selectByMobilePhone(tel); + if (userMain != null) { + throw new UserException(UserEnums.USER_PHONE_EXISTS); + } + if (CheckUtils.checkMobilePhone(tel)) { + smsCountService.handleInsertSmsCode(tel, internationalCode, SmsCountEnum.SMS_COUNT_REGISTER); + } else if (CheckUtils.checkEmail(tel)) { + emailCodeUtils.sendSmsCodeAndStoreToCache(tel, getUserLocale(request), SmsCountEnum.SMS_COUNT_REGISTER); + } else { + throw new UserException(UserEnums.FORMAT_ERROR); + } +// smsCountService.handleInsertSmsCode(tel, internationalCode, SmsCountEnum.SMS_COUNT_REGISTER); + return ResultDTO.requstSuccess(); + } + + @PostMapping("/loginout") + @ApiOperation(value = LoginApi.Loginout.METHOD_NAME, + notes = LoginApi.Loginout.METHOD_NOTE) + public ResultDTO loginout(@RequestParam(value = "tokenType", required = false) String tokenType, + HttpServletRequest request) { + //不为空的时候,代表APP调用,APP调用时清空用户客户端信息 + //PC不需要清空 + if (StringUtils.isNotBlank(tokenType)) { + String userId = SSOHelper.getUserIdIsExits(redisTemplate, request); + if (userId != null) { + //退出登录时,清空用户客户端信息,不再推送 + pushUserService.insertOrUpadteUser(userId, null, null); + } + } + SSOHelper.removeUser(request, redisTemplate); + return ResultDTO.requstSuccess(); + } + + @PostMapping("/password2") + @ApiOperation(value = LoginApi.PassWorldLoginPC.METHOD_NAME, + notes = LoginApi.PassWorldLoginPC.METHOD_NOTE) + public ResultDTO loginByPasswordPC(@ApiParam(LoginApi.PassWorldLoginPC.METHOD_API_TEL) @RequestParam(name = "tel") String tel, + @ApiParam(LoginApi.PassWorldLoginPC.METHOD_API_PASS) @RequestParam(name = "password") String password) { + UserMain userMain = userLoginService.handleLoginByPassword(tel, password); + return handleAfterLogin(userMain, TokenTypeEnums.PC.getValue()); + } + + @PostMapping("/loginByCode2") + @ApiOperation(value = LoginApi.SmsCodeLoginPC.METHOD_NAME, + notes = LoginApi.SmsCodeLoginPC.METHOD_NOTE) + public ResultDTO loginBysmsCodePC(@ApiParam(LoginApi.SmsCodeLoginPC.METHOD_API_TEL) @RequestParam(name = "tel") String tel, + @ApiParam(LoginApi.SmsCodeLoginPC.METHOD_API_CODE) @RequestParam(name = "code") String code) { + smsCodeUtils.validateVerifyCode(code, tel, SmsCountEnum.SMS_COUNT_LOGIN); + UserMain userMain = userLoginService.handleLoginByPhoneCode(tel); +// userMainService.selectByMobilePhone(tel); + smsCodeUtils.removeKey(tel, SmsCountEnum.SMS_COUNT_LOGIN); + return handleAfterLogin(userMain, TokenTypeEnums.PC.getValue()); + } + + @PostMapping("/register2") + @ApiOperation(value = LoginApi.RegisterPC.METHOD_NAME, + notes = LoginApi.RegisterPC.METHOD_NOTE) + public ResultDTO registerPC(@ApiParam(LoginApi.RegisterPC.METHOD_API_TEL) @RequestParam(name = "tel") String tel, + @ApiParam(LoginApi.RegisterPC.METHOD_API_CODE) @RequestParam(name = "code") String code, + @ApiParam(LoginApi.RegisterPC.METHOD_API_INVITATION_CODE) @RequestParam(value = "invitationCode", required = false) String invitationCode, + @ApiParam(LoginApi.RegisterPC.METHOD_API_PASSWORD) @RequestParam(value = "password", required = false) String password, + @ApiParam(LoginApi.RegisterPC.METHOD_API_INTERNATIONAL_CODE) @RequestParam(value = "internationalCode", required = false, defaultValue = InternationalConstant.DEFAULT_CODE) String internationalCode, + @ApiParam(LoginApi.RegisterPC.METHOD_API_NICK_NAME) @RequestParam(value = "nickName", required = false) String nickName + ) { + smsCodeUtils.validateVerifyCode(code, tel, SmsCountEnum.SMS_COUNT_REGISTER); + UserMain userMain = userMainService.handleRegister(tel, invitationCode, internationalCode, password, nickName); + smsCodeUtils.removeKey(tel, SmsCountEnum.SMS_COUNT_REGISTER); + return handleAfterLogin(userMain, TokenTypeEnums.PC.getValue()); + } + + @PostMapping("/loginout2") + @ApiOperation(value = LoginApi.LoginoutPC.METHOD_NAME, + notes = LoginApi.LoginoutPC.METHOD_NOTE) + public ResultDTO loginout2(HttpServletRequest request) { + SSOHelper.removeUser(request, redisTemplate, TokenTypeEnums.PC.getValue()); + return ResultDTO.requstSuccess(); + } + + /** + * 设置用户信息到redis + * + * @param id 用户id + * @param tel 手机号 + * @param timestamp 时间撮 + */ + private void setUserToRedis(String id, String tel, long timestamp, String tokenType) { + SessionUserDTO userDTO = new SessionUserDTO(); + userDTO.setId(id); + userDTO.setTel(tel); + userDTO.setTimestamp(timestamp); + SSOHelper.setUser(userDTO, redisTemplate, tokenType); + } + + /** + * 生成token返回前端 + * + * @param tel 手机号 + * @param timestamp 时间撮 + * @return token + */ + private String generateToken(String tel, long timestamp, String tokenType) { + TokenDTO tokenDTO = new TokenDTO(); + tokenDTO.setTel(tel); + tokenDTO.setTimestamp(timestamp); + tokenDTO.setTokenType(tokenType); + return RSACoderUtils.encryptToken(tokenDTO); + } + + /** + * App登录成功之后的处理 + */ + private ResultDTO handleAppAfterLogin(UserMain userMain, String clientId, String userLocale) { + //保存用户客户端信息,用于消息通知 + handleAfterLoginToSavePushUser(userMain.getId(), clientId, userLocale); + return handleAfterLogin(userMain, TokenTypeEnums.APP.getValue()); + } + + /*** + * 获取用户语种 + * @param request + * @return + */ + private String getUserLocale(HttpServletRequest request) { + String userLocale = HttpRequestUtil.getUserLocale(request); + return userLocale; + } + + /*** + * APP登录成功后保存用户客户端信息 + * 用于消息通知 + */ + private void handleAfterLoginToSavePushUser(String userId, String clientId, String userLocale) { + pushUserService.insertOrUpadteUser(userId, clientId, userLocale); + } + + /** + * 登录成功之后的处理 + */ + private ResultDTO handleAfterLogin(UserMain userMain, String tokenType) { + long timestamp = System.currentTimeMillis(); + setUserToRedis(userMain.getId(), userMain.getMobilePhone(), timestamp, tokenType); + String token = generateToken(userMain.getMobilePhone(), timestamp, tokenType); + UserBaseDTO userBaseDTO = userMainService.selectUserInfoById(userMain.getId()); + userBaseDTO.setToken(token); + return ResultDTO.requstSuccess(userBaseDTO); + } + + @GetMapping("/resetPassword") + @ApiOperation(value = LoginApi.ForgetPassword.METHOD_NAME, + notes = LoginApi.ForgetPassword.METHOD_NOTE) + public ResultDTO resetPassword( + @ApiParam(LoginApi.ForgetPassword.METHOD_API_TEL) @RequestParam("tel") String tel, + @ApiParam(LoginApi.ForgetPassword.METHOD_API_PASSWORD) @RequestParam("password") String password, + @ApiParam(LoginApi.ForgetPassword.METHOD_API_CODE) @RequestParam("code") String code) { + smsCodeUtils.validateVerifyCode(code, tel, SmsCountEnum.SMS_COUNT_FORGET_PASSWORD); + UserMain user = userMainService.selectByMobilePhone(tel); + userLoginService.updatePassword(user.getId(), password); + smsCodeUtils.removeKey(tel, SmsCountEnum.SMS_COUNT_FORGET_PASSWORD); + return ResultDTO.requstSuccess(); + } + + @PostMapping("/setForgetCode") + @ApiOperation(value = LoginApi.SetForgetPasswordCode.METHOD_NAME, + notes = LoginApi.SetForgetPasswordCode.METHOD_NOTE) + public ResultDTO sendForgetPwCode(@ApiParam(LoginApi.SetForgetPasswordCode.METHOD_API_CODE) @RequestParam(value = "internationalCode", required = false, defaultValue = InternationalConstant.DEFAULT_CODE) String internationalCode, + HttpServletRequest request, @ApiParam(LoginApi.SetForgetPasswordCode.METHOD_API_TEL) @RequestParam("tel") String tel) { + UserMain userMain = userMainService.selectByMobilePhone(tel); + if (userMain == null) { + throw new UserException(UserEnums.USER_NOT_EXISTS); + } + if (CheckUtils.checkMobilePhone(tel)) { + smsCountService.handleInsertSmsCode(tel, internationalCode, SmsCountEnum.SMS_COUNT_FORGET_PASSWORD); + } else if (CheckUtils.checkEmail(tel)) { + emailCodeUtils.sendSmsCodeAndStoreToCache(tel, getUserLocale(request), SmsCountEnum.SMS_COUNT_FORGET_PASSWORD); + } else { + throw new UserException(UserEnums.FORMAT_ERROR); + } +// smsCountService.handleInsertSmsCode(tel, internationalCode, SmsCountEnum.SMS_COUNT_FORGET_PASSWORD); + return ResultDTO.requstSuccess(); + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/PushUserController.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/PushUserController.java new file mode 100644 index 0000000..f9f650f --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/PushUserController.java @@ -0,0 +1,38 @@ +package com.blockchain.server.user.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.user.controller.api.PushUserApi; +import com.blockchain.server.user.service.PushUserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +@Api(PushUserApi.PUSH_USER_API) +@RestController +@RequestMapping("/pushUser") +public class PushUserController { + + @Autowired + private PushUserService pushUserService; + @Autowired + private RedisTemplate redisTemplate; + + @ApiOperation(value = PushUserApi.UpdateUserlangue.METHOD_NAME, + notes = PushUserApi.UpdateUserlangue.METHOD_NOTE) + @GetMapping("/updateUserLanguage") + public ResultDTO updateUserLanguage(HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + String userLocale = HttpRequestUtil.getUserLocale(request); + pushUserService.updateUserLanguage(userId, userLocale); + return ResultDTO.requstSuccess(); + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/UserController.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/UserController.java new file mode 100644 index 0000000..8c603e9 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/UserController.java @@ -0,0 +1,430 @@ +package com.blockchain.server.user.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.SessionUserDTO; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.user.common.constants.other.InternationalConstant; +import com.blockchain.server.user.common.constants.other.RedisConstant; +import com.blockchain.server.user.common.constants.other.UserFileUploadConstant; +import com.blockchain.server.user.common.constants.sql.ConfigConstant; +import com.blockchain.server.user.common.enums.SmsCountEnum; +import com.blockchain.server.user.common.enums.UserEnums; +import com.blockchain.server.user.common.exceprion.UserException; +import com.blockchain.server.user.common.utils.*; +import com.blockchain.server.user.controller.api.UserApi; +import com.blockchain.server.user.controller.api.UserAuthenticationApi; +import com.blockchain.server.user.dto.UserBaseDTO; +import com.blockchain.server.user.entity.UserMain; +import com.blockchain.server.user.service.*; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +/** + * @author huangxl + * @create 2019-02-24 10:36 + */ +@RestController +@Api(UserApi.CONTROLLER_API) +@RequestMapping("/user") +public class UserController { + @Autowired + private UserInfoService userInfoService; + @Autowired + private UserLoginService userLoginService; + @Autowired + private UserMainService userMainService; + @Autowired + private SmsCountService smsCountService; + @Autowired + private SmsCodeUtils smsCodeUtils; + @Autowired + private EmailCodeUtils emailCodeUtils; + @Autowired + private ConfigService configService; + @Autowired + private AuthenticationApplyService authenticationApplyService; + + @Autowired + private EMailTransmitHelper eMailTransmitHelper; + @Autowired + private RedisTemplate redisTemplate; + + @Value("${FILES_DIR.ROOT}") + private String FILE_ROOT_PATH;//文件上传根目录 + + + /** + * 查询用户高级未认证原因 + * + * @param userId + * @return + */ + // @RequiresPermissions("app:userAuthentication:selectLowAuth") + @ApiOperation(value = UserAuthenticationApi.SelectLowAuth.METHOD_API_NAME, notes = UserAuthenticationApi.SelectLowAuth.METHOD_API_NOTE) + @PostMapping("/selectHighRemark") + public ResultDTO selectHighRemark(@ApiParam(UserAuthenticationApi.SelectHighAuth.METHOD_API_USER_ID) @RequestParam("userId") String userId) { + return ResultDTO.requstSuccess(authenticationApplyService.selectHighRemarkByUserId(userId)); + } + + + /** + * 查询初级未认证原因 + * + * @param userId + * @return + */ + // @RequiresPermissions("app:userAuthentication:selectLowAuth") + @ApiOperation(value = UserAuthenticationApi.SelectLowAuth.METHOD_API_NAME, notes = UserAuthenticationApi.SelectLowAuth.METHOD_API_NOTE) + @PostMapping("/selectLowRemark") + public ResultDTO selectLowRemark(@ApiParam(UserAuthenticationApi.SelectLowAuth.METHOD_API_USER_ID) @RequestParam("userId") String userId) { + return ResultDTO.requstSuccess(authenticationApplyService.selectLowRemarkByUserId(userId)); + } + + @PostMapping("/updateNickName") + @ApiOperation(value = UserApi.UpdateNickName.METHOD_NAME, + notes = UserApi.UpdateNickName.METHOD_NOTE) + public ResultDTO updateNickName(@ApiParam(UserApi.UpdateNickName.METHOD_API_NICKNAME) @RequestParam(name = "nickName") String nickName, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + userMainService.updateNickName(userId, nickName); + return ResultDTO.requstSuccess(); + } + + @PostMapping("/updateTel") + @ApiOperation(value = UserApi.UpdateTel.METHOD_NAME, + notes = UserApi.UpdateTel.METHOD_NOTE) + public ResultDTO updateTel(@ApiParam(UserApi.UpdateTel.METHOD_API_TEL) @RequestParam(name = "tel") String tel, + @ApiParam(UserApi.UpdateTel.METHOD_API_CODE) @RequestParam(name = "code") String code, + @ApiParam(UserApi.UpdateTel.METHOD_API_INTERNATIONAL_CODE) @RequestParam(value = "internationalCode", required = false, defaultValue = InternationalConstant.DEFAULT_CODE) String internationalCode, + HttpServletRequest request) { + smsCodeUtils.validateVerifyCode(code, tel, SmsCountEnum.SMS_COUNT_UPDATE_ACCOUNT);//校验验证码是否正确 + SessionUserDTO user = SSOHelper.getUser(redisTemplate, request); + userMainService.updateTel(user.getId(), tel, internationalCode); + smsCodeUtils.removeKey(tel, SmsCountEnum.SMS_COUNT_UPDATE_ACCOUNT);//删除验证码key + //更新session信息 + user.setTel(tel); + SSOHelper.setUser(user, redisTemplate); + return ResultDTO.requstSuccess(); + } + + @PostMapping("/sendChangePhoneCode") + @ApiOperation(value = UserApi.SendChangePhoneCode.METHOD_NAME, + notes = UserApi.SendChangePhoneCode.METHOD_NOTE) + public ResultDTO sendChangePhoneCode(@ApiParam(UserApi.SendChangePhoneCode.METHOD_API_TEL) @RequestParam(name = "tel") String tel, + @ApiParam(UserApi.SendChangePhoneCode.METHOD_API_INTERNATIONAL_CODE) @RequestParam(value = "internationalCode", required = false, defaultValue = InternationalConstant.DEFAULT_CODE) String internationalCode, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + userMainService.handleSendChangePhoneCodeBefore(userId, tel); + smsCountService.handleInsertSmsCode(tel, internationalCode, SmsCountEnum.SMS_COUNT_UPDATE_ACCOUNT); + return ResultDTO.requstSuccess(); + } + + @PostMapping("/setPwCode") + @ApiOperation(value = UserApi.SendPasswordCode.METHOD_NAME, + notes = UserApi.SendPasswordCode.METHOD_NOTE) + public ResultDTO sendFirstPwCode(HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + UserMain userMain = userMainService.selectById(userId); +// smsCountService.handleInsertSmsCode(userMain.getMobilePhone(), userMain.getInternationalCode(), SmsCountEnum.SMS_COUNT_SET_PASSWORD); + + if (CheckUtils.checkMobilePhone(userMain.getMobilePhone())){ + smsCountService.handleInsertSmsCode(userMain.getMobilePhone(), userMain.getInternationalCode(), SmsCountEnum.SMS_COUNT_SET_PASSWORD); + }else if (CheckUtils.checkEmail(userMain.getMobilePhone())){ + emailCodeUtils.sendSmsCodeAndStoreToCache(userMain.getMobilePhone(), getUserLocale(request), SmsCountEnum.SMS_COUNT_SET_PASSWORD); + }else { + throw new UserException(UserEnums.FORMAT_ERROR); + } + return ResultDTO.requstSuccess(); + } + + @GetMapping("/getGoogleKey") + @ApiOperation(value = UserApi.GenerateGoogleKey.METHOD_NAME, + notes = UserApi.GenerateGoogleKey.METHOD_NOTE) + public ResultDTO getGoogleKey() { + String key = GoogleAuthenticatorUtils.generateSecretKey(); + if (key == null) { + throw new UserException(UserEnums.GOOGLE_SECRET_KEY_FAIL); + } + return ResultDTO.requstSuccess(key); + } + + @PostMapping("/bindGoogle") + @ApiOperation(value = UserApi.BindGoogleKey.METHOD_NAME, + notes = UserApi.BindGoogleKey.METHOD_NOTE) + public ResultDTO bindGoogleKey(@ApiParam(UserApi.BindGoogleKey.METHOD_API_KEY) @RequestParam(name = "key") String key, + @ApiParam(UserApi.BindGoogleKey.METHOD_API_CODE) @RequestParam(value = "code") Long code, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + userInfoService.handleBindGoogleAuthenticator(userId, key, code); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = UserApi.UploadIdentityImgFile.METHOD_NAME, notes = UserApi.UploadIdentityImgFile.METHOD_NOTE) + @PostMapping(value = "/identityUploadFile") + public ResultDTO identityUploadFile(@ApiParam(UserApi.UploadIdentityImgFile.METHOD_API_IMG) @RequestParam(value = "img") String img, HttpServletRequest request) { + //身份认证文件保存到本地/服务器 + String fileNames = generateImage(img, UserFileUploadConstant.UPLOAD_IDENTITY, request); + return ResultDTO.requstSuccess(fileNames); + } + + @ApiOperation(value = UserApi.UploadIdentityImgFile.METHOD_NAME, notes = UserApi.UploadIdentityImgFile.METHOD_NOTE) + @PostMapping(value = "/pcIdentityUploadFile") + public ResultDTO pcIdentityUploadFile(@ApiParam(UserApi.UploadIdentityImgFile.METHOD_API_IMG) @RequestParam(value = "file") MultipartFile file, HttpServletRequest request) { + //身份认证文件保存到本地/服务器 + String fileNames = uploadFile(file, UserFileUploadConstant.UPLOAD_IDENTITY, request);// generateImage(img, UserFileUploadConstant.UPLOAD_IDENTITY, request); + return ResultDTO.requstSuccess(fileNames); + } + + @ApiOperation(value = UserApi.HeadImgUploadFile.METHOD_NAME, notes = UserApi.HeadImgUploadFile.METHOD_NOTE) + @RequestMapping(value = "/headImgUploadFile", method = RequestMethod.POST) + public ResultDTO handleHeadImgUploadFile(@ApiParam(UserApi.HeadImgUploadFile.METHOD_API_IMG) @RequestParam(value = "img") String img, HttpServletRequest request) { + //返回的状态信息 + //获取图片文件 + String imgName = generateImage(img, UserFileUploadConstant.UPLOAD_HEAD_IMG, request); + return ResultDTO.requstSuccess(imgName); + } + + @ApiOperation(value = UserApi.ReplaceHeadImg.METHOD_NAME, notes = UserApi.ReplaceHeadImg.METHOD_NOTE) + @RequestMapping(value = "/replaceHeadImg", method = RequestMethod.POST) + public ResultDTO replaceHeadImg(@ApiParam(UserApi.ReplaceHeadImg.METHOD_API_IMG) @RequestParam(value = "img") String img, HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + userInfoService.updateUserHeadImg(userId, img); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = UserApi.CommitPrimaryAuthApply.METHOD_NAME, notes = UserApi.CommitPrimaryAuthApply.METHOD_NOTE) + @RequestMapping(value = "/saveIdcardVali", method = RequestMethod.POST) + public ResultDTO commitPrimaryAuthApply(@ApiParam(UserApi.CommitPrimaryAuthApply.METHOD_API_REAL_NAME) @RequestParam("realName") String realName, + @ApiParam(UserApi.CommitPrimaryAuthApply.METHOD_API_ID_NUMBER) @RequestParam("idNumber") String idNumber, + @ApiParam(UserApi.CommitPrimaryAuthApply.METHOD_API_TYPE) @RequestParam(value = "type", required = false) String type, + @ApiParam(UserApi.CommitPrimaryAuthApply.METHOD_API_IMGS) @RequestParam("imgs") String imgs, HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + authenticationApplyService.insertBasicAuth(userId, realName, idNumber, type, imgs); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = UserApi.HighAuthApply.METHOD_NAME, notes = UserApi.HighAuthApply.METHOD_NOTE) + @RequestMapping(value = "/saveHighAuth", method = RequestMethod.POST) + public ResultDTO commitHighAuthApply(@ApiParam(UserApi.HighAuthApply.METHOD_API_IMG) @RequestParam("img") String img, HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + authenticationApplyService.insertHighAuth(userId, img); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = UserApi.Me.METHOD_NAME, notes = UserApi.Me.METHOD_NOTE) + @RequestMapping(value = "/me", method = RequestMethod.POST) + public ResultDTO me(HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + UserBaseDTO userBaseDTO = userMainService.selectUserInfoById(userId); + return ResultDTO.requstSuccess(userBaseDTO); + } + + @GetMapping("/sendEmailCode") + @ApiOperation(value = UserApi.SendEmailCode.METHOD_NAME, + notes = UserApi.SendEmailCode.METHOD_NOTE) + public ResultDTO sendEmailCode(@ApiParam(UserApi.SendEmailCode.METHOD_API_EMAIL) @RequestParam("email") String email, HttpServletRequest request) { + boolean check = CheckUtils.checkEmail(email); + if (!check) { + throw new UserException(UserEnums.EMAIL_FORMAT_ERROR); + } + //检查是否有重复邮箱信息 + userInfoService.checkRepeatEmail(email); + emailCodeUtils.sendSmsCodeAndStoreToCache(email,HttpRequestUtil.getUserLocale(request),SmsCountEnum.SMS_COUNT_BIND); + return ResultDTO.requstSuccess(); + } + + @GetMapping("/bindEmail") + @ApiOperation(value = UserApi.BindEmail.METHOD_NAME, + notes = UserApi.BindEmail.METHOD_NOTE) + public ResultDTO verifyEmailCode(@ApiParam(UserApi.BindEmail.METHOD_API_EMAIL) @RequestParam("email") String email, + @ApiParam(UserApi.BindEmail.METHOD_API_CODE) @RequestParam("code") String code, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + userInfoService.handleBindEmail(userId, email, code); + return ResultDTO.requstSuccess(); + } + + @GetMapping("/setPassword") + @ApiOperation(value = UserApi.SetPassword.METHOD_NAME, + notes = UserApi.SetPassword.METHOD_NOTE) + public ResultDTO setPassword(@ApiParam(UserApi.SetPassword.METHOD_API_PASSWORD) @RequestParam("password") String password, + @ApiParam(UserApi.SetPassword.METHOD_API_CODE) @RequestParam("code") String code, + HttpServletRequest request) { + SessionUserDTO user = SSOHelper.getUser(redisTemplate, request); + smsCodeUtils.validateVerifyCode(code, user.getTel(), SmsCountEnum.SMS_COUNT_SET_PASSWORD); + userLoginService.insertPassword(user.getId(), password); + smsCodeUtils.removeKey(user.getTel(), SmsCountEnum.SMS_COUNT_SET_PASSWORD); + return ResultDTO.requstSuccess(); + } + + @GetMapping("/updatePassword") + @ApiOperation(value = UserApi.UpdatePassword.METHOD_NAME, + notes = UserApi.UpdatePassword.METHOD_NOTE) + public ResultDTO updatePassword(@ApiParam(UserApi.UpdatePassword.METHOD_API_PASSWORD) @RequestParam("password") String password, + @ApiParam(UserApi.UpdatePassword.METHOD_API_OLD_PASSWORD) @RequestParam("oldPassword") String oldPassword, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + userLoginService.updatePassword(userId, password, oldPassword); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = UserApi.UpdateHighAuth.METHOD_NAME, notes = UserApi.UpdateHighAuth.METHOD_NOTE) + @RequestMapping(value = "/uploadHighAuthFile", method = RequestMethod.POST) + public ResultDTO uploadHighAuthFile(@ApiParam(value = UserApi.UpdateHighAuth.METHOD_API_FILE) @RequestParam("file") String img, HttpServletRequest request) { + String fileNames = generateImage(img, UserFileUploadConstant.UPLOAD_HIGH_AUTH, request); + return ResultDTO.requstSuccess(fileNames); + } + + @ApiOperation(value = UserApi.UpdateHighAuth.METHOD_NAME, notes = UserApi.UpdateHighAuth.METHOD_NOTE) + @RequestMapping(value = "/pcUploadHighAuthFile", method = RequestMethod.POST) + public ResultDTO pcUploadHighAuthFile(@ApiParam(value = UserApi.UpdateHighAuth.METHOD_API_FILE) @RequestParam(value = "file") MultipartFile file, HttpServletRequest request) { + String fileNames = uploadFile(file, UserFileUploadConstant.UPLOAD_HIGH_AUTH, request); + return ResultDTO.requstSuccess(fileNames); + } + + @ApiOperation(value = UserApi.SendMoneyPasswordSmsg.METHOD_TITLE_NAME, notes = UserApi.SendMoneyPasswordSmsg.METHOD_TITLE_NOTE) + @GetMapping("/sendMoneyPasswordSmsg") + public ResultDTO sendMoneyPasswordSmsg(HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + UserMain userMain = userMainService.selectById(userId); + if (CheckUtils.checkMobilePhone(userMain.getMobilePhone())){ + smsCountService.handleInsertSmsCode(userMain.getMobilePhone(), userMain.getInternationalCode(), SmsCountEnum.SMS_COUNT_UPDATE_WALLET_PASSWORD); + }else if (CheckUtils.checkEmail(userMain.getMobilePhone())){ + emailCodeUtils.sendSmsCodeAndStoreToCache(userMain.getMobilePhone(), getUserLocale(request), SmsCountEnum.SMS_COUNT_UPDATE_WALLET_PASSWORD); + }else { + throw new UserException(UserEnums.FORMAT_ERROR); + } +// smsCodeUtils.sendSmsCodeAndStoreToCache(userMain.getMobilePhone(), userMain.getInternationalCode(), SmsCountEnum.SMS_COUNT_UPDATE_WALLET_PASSWORD); + return ResultDTO.requstSuccess(); + } + + /** + * 登陆后的忘记密码,可以直接从redis拿到用户手机信息 + */ + @GetMapping("/resetPassword") + @ApiOperation(value = UserApi.ForgetPassword.METHOD_NAME, + notes = UserApi.ForgetPassword.METHOD_NOTE) + public ResultDTO resetPassword(@ApiParam(UserApi.ForgetPassword.METHOD_API_PASSWORD) @RequestParam("password") String password, + @ApiParam(UserApi.ForgetPassword.METHOD_API_CODE) @RequestParam("code") String code, + HttpServletRequest request) { + SessionUserDTO user = SSOHelper.getUser(redisTemplate, request); + smsCodeUtils.validateVerifyCode(code, user.getTel(), SmsCountEnum.SMS_COUNT_FORGET_PASSWORD); + userLoginService.updatePassword(user.getId(), password); + smsCodeUtils.removeKey(user.getTel(), SmsCountEnum.SMS_COUNT_FORGET_PASSWORD); + return ResultDTO.requstSuccess(); + } + + /** + * 登陆后的忘记密码,可以直接从redis拿到用户手机信息和国际区号 + */ + @PostMapping("/setForgetCode") + @ApiOperation(value = UserApi.SetForgetPasswordCode.METHOD_NAME, + notes = UserApi.SetForgetPasswordCode.METHOD_NOTE) + public ResultDTO sendForgetPwCode(HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + UserMain userMain = userMainService.selectById(userId); + if (CheckUtils.checkMobilePhone(userMain.getMobilePhone())){ + smsCountService.handleInsertSmsCode(userMain.getMobilePhone(), userMain.getInternationalCode(), SmsCountEnum.SMS_COUNT_FORGET_PASSWORD); + }else if (CheckUtils.checkEmail(userMain.getMobilePhone())){ + emailCodeUtils.sendSmsCodeAndStoreToCache(userMain.getMobilePhone(), getUserLocale(request), SmsCountEnum.SMS_COUNT_FORGET_PASSWORD); + }else { + throw new UserException(UserEnums.FORMAT_ERROR); + } +// smsCountService.handleInsertSmsCode(userMain.getMobilePhone(), userMain.getInternationalCode(), SmsCountEnum.SMS_COUNT_FORGET_PASSWORD); + return ResultDTO.requstSuccess(); + } + + /** + * 判断用户认证状态接口 + * @param userId + * @param authenticationType + * @return + */ + @ApiOperation(value = UserApi.JudgeAuthentication.METHOD_API_NAME, notes = UserApi.JudgeAuthentication.METHOD_API_NOTE) + @PostMapping("/judgeAuthentication") + public ResultDTO judgeAuthentication(@ApiParam(UserApi.JudgeAuthentication.METHOD_API_USER_ID) @RequestParam("userId") String userId, @ApiParam(UserApi.JudgeAuthentication.METHOD_API_AUTHENTICATION_TYPE) @RequestParam("authenticationType") String authenticationType) { + String authenticationStatus = authenticationApplyService.judgeAuthentication(userId, authenticationType); + return ResultDTO.requstSuccess(authenticationStatus); + } + + private String uploadFile(MultipartFile file, String relativePath, HttpServletRequest request) { + checkFileUploadLimit(request); + String fileNames = null; + try { + fileNames = FileUploadHelper.saveFile(file, FILE_ROOT_PATH, relativePath); + incrUploadTimes(request); + return fileNames; + } catch (IOException e) { + throw new UserException(UserEnums.FILE_UPLOAD_ERROR); + } + } + + /** + * 保存base64字符串 + * + * @param imgBase64 base64字符串 + * @param relativeDir 相对目录 + * @return 文件相对路径 + * @throws IOException + */ + private String generateImage(String imgBase64, String relativeDir, HttpServletRequest request) { + checkFileUploadLimit(request); + try { + String filePath = FileUploadHelper.generateImage(imgBase64, FILE_ROOT_PATH, relativeDir); + incrUploadTimes(request); + return filePath; + } catch (IOException e) { + throw new UserException(UserEnums.FILE_UPLOAD_ERROR); + } + } + + /** + * 检查上传文件次数限制 + */ + private void checkFileUploadLimit(HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + String key = RedisConstant.getFileUploadTimesKey(userId); + if (redisTemplate.hasKey(key)) { + Integer times = (Integer) redisTemplate.opsForValue().get(key); + //如果上传文件超过限制次数,报错 + String limitTimes = configService.getValidValueByKey(ConfigConstant.KEY_UPLOAD_LIMIT); + if (limitTimes != null) { + if (Integer.parseInt(limitTimes) < times) { + throw new UserException(UserEnums.FILE_UPLOAD_LIMIT); + } + } + } + } + + /** + * 上传文件次数+1 + */ + private void incrUploadTimes(HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + String key = RedisConstant.getFileUploadTimesKey(userId); + redisTemplate.opsForValue().increment(key, 1);//自增+1 + redisTemplate.expire(key, 1, TimeUnit.DAYS);//设置超时时间为一天 + } + + /*** + * 获取用户语种 + * @param request + * @return + */ + private String getUserLocale(HttpServletRequest request) { + String userLocale = HttpRequestUtil.getUserLocale(request); + return userLocale; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/UserLoginLogController.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/UserLoginLogController.java new file mode 100644 index 0000000..72d9e3c --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/UserLoginLogController.java @@ -0,0 +1,56 @@ +package com.blockchain.server.user.controller; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.base.controller.BaseController; +import com.blockchain.server.user.controller.api.UserLoginLogApi; +import com.blockchain.server.user.dto.UserLoginLogDto; +import com.blockchain.server.user.service.UserLoginLogService; +import com.github.pagehelper.PageHelper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +/** + * @author Harvey + * @date 2019/3/5 15:25 + * @user WIN10 + */ +@RestController +@Api(UserLoginLogApi.USER_LOGIN_LOG_API) +@RequestMapping("/userLoginLog") +public class UserLoginLogController extends BaseController { + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private UserLoginLogService userLoginLogService; + + /** + * 查询用户登录日志 + * + * @param pageNum + * @param pageSize + * @return + */ + @ApiOperation(value = UserLoginLogApi.ListUserLoginLog.MATHOD_API_NAME, notes = UserLoginLogApi.ListUserLoginLog.MATHOD_API_NOTE) + @PostMapping("/listUserLoginLog") + public ResultDTO listUserLoginLog(@ApiParam(UserLoginLogApi.METHOD_API_PAGE_NUM) @RequestParam(value = "pageNum", required = false, defaultValue = "0") Integer pageNum, + @ApiParam(UserLoginLogApi.METHOD_API_PAGE_SIZE) @RequestParam(value = "pageSize", required = false, defaultValue = "10") Integer pageSize, + HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + PageHelper.startPage(pageNum, pageSize); + List loginLogs = userLoginLogService.listUserLoginLogByUserId(userId); + return generatePage(loginLogs); + } + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/LoginApi.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/LoginApi.java new file mode 100644 index 0000000..4c7c71f --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/LoginApi.java @@ -0,0 +1,102 @@ +package com.blockchain.server.user.controller.api; + +/** + * @author huangxl + * @create 2018-11-16 14:51 + */ +public class LoginApi { + public static final String CONTROLLER_API = "登陆注册控制器"; + + public static class PassWorldLogin { + public static final String METHOD_NAME = "密码登陆接口"; + public static final String METHOD_NOTE = "手机号+密码登陆接口"; + public static final String METHOD_API_TEL = "手机号"; + public static final String METHOD_API_PASS = "密码"; + public static final String METHOD_API_CLIENT_ID = "客户端标识"; + } + + public static class SmsCodeLogin { + public static final String METHOD_NAME = "验证码登陆接口"; + public static final String METHOD_NOTE = "手机号+验证码登陆接口"; + public static final String METHOD_API_TEL = "手机号"; + public static final String METHOD_API_CODE = "验证码"; + public static final String METHOD_API_CLIENT_ID = "客户端标识"; + } + + public static class Register { + public static final String METHOD_NAME = "注册接口"; + public static final String METHOD_NOTE = "注册接口"; + public static final String METHOD_API_TEL = "手机号"; + public static final String METHOD_API_CODE = "验证码"; + public static final String METHOD_API_PASSWORD = "密码"; + public static final String METHOD_API_INVITATION_CODE = "邀请码"; + public static final String METHOD_API_INTERNATIONAL_CODE = "国际标识号"; + public static final String METHOD_API_NICK_NAME = "昵称"; + public static final String METHOD_API_CLIENT_ID = "客户端标识"; + } + + public static class SendRegisterSmsCode { + public static final String METHOD_NAME = "发送注册验证码"; + public static final String METHOD_NOTE = "发送注册验证码"; + public static final String METHOD_API_TEL = "手机号"; + public static final String METHOD_API_INTERNATIONAL_CODE = "国际标识号"; + } + + public static class SendLoginSmsCode { + public static final String METHOD_NAME = "发送登录验证码"; + public static final String METHOD_NOTE = "发送登录验证码"; + public static final String METHOD_API_TEL = "手机号"; + public static final String METHOD_API_INTERNATIONAL_CODE = "国际标识号"; + } + + public static class Loginout { + public static final String METHOD_NAME = "退出登录"; + public static final String METHOD_NOTE = "退出登录"; + } + + public static class PassWorldLoginPC { + public static final String METHOD_NAME = "PC端密码登陆接口"; + public static final String METHOD_NOTE = "手机号+密码登陆接口"; + public static final String METHOD_API_TEL = "手机号"; + public static final String METHOD_API_PASS = "密码"; + } + + public static class SmsCodeLoginPC { + public static final String METHOD_NAME = "PC端验证码登陆接口"; + public static final String METHOD_NOTE = "手机号+验证码登陆接口"; + public static final String METHOD_API_TEL = "手机号"; + public static final String METHOD_API_CODE = "验证码"; + } + + public static class RegisterPC { + public static final String METHOD_NAME = "PC端注册接口"; + public static final String METHOD_NOTE = "PC端注册接口"; + public static final String METHOD_API_TEL = "手机号"; + public static final String METHOD_API_CODE = "验证码"; + public static final String METHOD_API_PASSWORD = "密码"; + public static final String METHOD_API_INVITATION_CODE = "邀请码"; + public static final String METHOD_API_INTERNATIONAL_CODE = "国际标识号"; + public static final String METHOD_API_NICK_NAME = "昵称"; + } + + public static class LoginoutPC { + public static final String METHOD_NAME = "PC端退出登录"; + public static final String METHOD_NOTE = "PC端退出登录"; + } + + public static class SetForgetPasswordCode { + public static final String METHOD_NAME = "忘记密码发送验证码接口"; + public static final String METHOD_NOTE = "忘记密码发送验证码"; + public static final String METHOD_API_CODE = "国际区号"; + public static final String METHOD_API_TEL = "手机号"; + } + + public static class ForgetPassword { + public static final String METHOD_NAME = "忘记密码,修改密码接口"; + public static final String METHOD_NOTE = "忘记密码,修改密码接口"; + public static final String METHOD_API_PASSWORD = "密码"; + public static final String METHOD_API_CODE = "验证码"; + public static final String METHOD_API_TEL = "手机号"; + } + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/PushUserApi.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/PushUserApi.java new file mode 100644 index 0000000..46700e5 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/PushUserApi.java @@ -0,0 +1,10 @@ +package com.blockchain.server.user.controller.api; + +public class PushUserApi { + public static final String PUSH_USER_API = "推送用户控制器"; + + public static class UpdateUserlangue { + public static final String METHOD_NAME = "更新推送用户语种"; + public static final String METHOD_NOTE = "更新推送用户语种"; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/UserApi.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/UserApi.java new file mode 100644 index 0000000..3cdc6a1 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/UserApi.java @@ -0,0 +1,144 @@ +package com.blockchain.server.user.controller.api; + +/** + * @author xusm + * @data 2019/2/21 20:10 + */ +public class UserApi { + public static final String CONTROLLER_API = "登陆注册控制器"; + + public static class UpdateNickName { + public static final String METHOD_NAME = "修改昵称接口"; + public static final String METHOD_NOTE = "根据当前登录用户修改昵称信息"; + public static final String METHOD_API_NICKNAME = "昵称"; + } + + public static class UpdateTel { + public static final String METHOD_NAME = "修改手机号接口"; + public static final String METHOD_NOTE = "根据当前登录用户修改手机号信息"; + public static final String METHOD_API_TEL = "昵称"; + public static final String METHOD_API_INTERNATIONAL_CODE = "国际标识号"; + public static final String METHOD_API_CODE = "验证码"; + } + + public static class SendChangePhoneCode { + public static final String METHOD_NAME = "发送更换手机号验证码"; + public static final String METHOD_NOTE = "发送更换手机号验证码"; + public static final String METHOD_API_TEL = "手机号"; + public static final String METHOD_API_INTERNATIONAL_CODE = "国际标识号"; + } + + public static class GenerateGoogleKey { + public static final String METHOD_NAME = "获取google验证码key"; + public static final String METHOD_NOTE = "获取google验证码key"; + } + + public static class BindGoogleKey { + public static final String METHOD_NAME = "绑定谷歌验证器"; + public static final String METHOD_NOTE = "绑定谷歌验证器"; + public static final String METHOD_API_KEY = "验证器安全码"; + public static final String METHOD_API_CODE = "验证码"; + } + + public static class UploadIdentityImgFile { + public static final String METHOD_NAME = "身份认证图片上传"; + public static final String METHOD_NOTE = "身份认证图片上传"; + public static final String METHOD_API_IMG = "上传身份证"; + } + + public static class HeadImgUploadFile { + public static final String METHOD_NAME = "头像上传"; + public static final String METHOD_NOTE = "头像上传"; + public static final String METHOD_API_IMG = "上传头像"; + } + + public static class ReplaceHeadImg { + public static final String METHOD_NAME = "更换头像"; + public static final String METHOD_NOTE = "更换头像"; + public static final String METHOD_API_IMG = "头像地址"; + } + + public static class Me { + public static final String METHOD_NAME = "查询我的个人信息"; + public static final String METHOD_NOTE = "查询我的个人信息"; + } + + public static class CommitPrimaryAuthApply { + public static final String METHOD_NAME = "提交初级认证申请接口"; + public static final String METHOD_NOTE = "提交初级认证申请接口"; + public static final String METHOD_API_REAL_NAME = "真实姓名"; + public static final String METHOD_API_ID_NUMBER = "身份证号码"; + public static final String METHOD_API_TYPE = "证件类型"; + public static final String METHOD_API_NATIONALITY = "国籍"; + public static final String METHOD_API_IMGS = "身份证保存地址,以「,」分隔"; + } + + public static class HighAuthApply { + public static final String METHOD_NAME = "提交高级认证申请接口"; + public static final String METHOD_NOTE = "提交高级认证申请接口"; + public static final String METHOD_API_IMG = "文件保存地址"; + } + + public static class SendEmailCode { + public static final String METHOD_NAME = "获取email验证码接口"; + public static final String METHOD_NOTE = "获取email验证码接口"; + public static final String METHOD_API_EMAIL = "email"; + } + + public static class BindEmail { + public static final String METHOD_NAME = "获取email验证码接口"; + public static final String METHOD_NOTE = "获取email验证码接口"; + public static final String METHOD_API_EMAIL = "email"; + public static final String METHOD_API_CODE = "邮箱验证码"; + } + + public static class SendPasswordCode { + public static final String METHOD_NAME = "首次设置密码发送验证码"; + public static final String METHOD_NOTE = "首次设置密码发送验证码"; + } + + public static class SetPassword { + public static final String METHOD_NAME = "首次设置密码接口"; + public static final String METHOD_NOTE = "首次设置密码接口"; + public static final String METHOD_API_PASSWORD = "密码"; + public static final String METHOD_API_CODE = "验证码"; + } + + public static class UpdatePassword { + public static final String METHOD_NAME = "修改密码接口"; + public static final String METHOD_NOTE = "修改密码接口"; + public static final String METHOD_API_PASSWORD = "密码"; + public static final String METHOD_API_OLD_PASSWORD = "旧密码"; + } + + public static class UpdateHighAuth { + public static final String METHOD_NAME = "上传高级认证文件接口"; + public static final String METHOD_NOTE = "上传高级认证文件接口"; + public static final String METHOD_API_FILE = "文件"; + } + + public static class SendMoneyPasswordSmsg { + public static final String METHOD_TITLE_NAME = "发送修改资金密码短信验证码接口"; + public static final String METHOD_TITLE_NOTE = "发送修改资金密码短信验证码接口"; + } + + public static class SetForgetPasswordCode { + public static final String METHOD_NAME = "忘记密码发送验证码接口"; + public static final String METHOD_NOTE = "忘记密码发送验证码"; + } + + public static class ForgetPassword { + public static final String METHOD_NAME = "忘记密码,修改密码接口"; + public static final String METHOD_NOTE = "忘记密码,修改密码接口"; + public static final String METHOD_API_PASSWORD = "密码"; + public static final String METHOD_API_CODE = "验证码"; + } + + public static class JudgeAuthentication { + public static final String METHOD_API_NAME = "判断用户是否认证"; + public static final String METHOD_API_NOTE = "判断用户是否认证"; + + public static final String METHOD_API_USER_ID = "用户id"; + public static final String METHOD_API_AUTHENTICATION_TYPE = "审核标识符"; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/UserAuthenticationApi.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/UserAuthenticationApi.java new file mode 100644 index 0000000..828a142 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/UserAuthenticationApi.java @@ -0,0 +1,72 @@ +package com.blockchain.server.user.controller.api; + +/** + * @author Harvey + * @date 2019/3/7 10:27 + * @user WIN10 + */ +public class UserAuthenticationApi { + public static final String USER_AUTHENTICATION_API = "用户信息认证管理器"; + + public static class SelectLowAuth { + public static final String METHOD_API_NAME = "查询用户初级审核申请"; + public static final String METHOD_API_NOTE = "根据用户id查询用户初级审核申请"; + + public static final String METHOD_API_USER_ID = "用户id"; + } + + public static class SelectHighAuth { + public static final String METHOD_API_NAME = "查询用户高级审核申请"; + public static final String METHOD_API_NOTE = "根据用户id查询用户高级审核申请"; + + public static final String METHOD_API_USER_ID = "用户id"; + } + + public static class PassLowAuth { + public static final String METHOD_API_NAME = "通过用户初级审核申请"; + public static final String METHOD_API_NOTE = "通过用户初级审核申请"; + + public static final String METHOD_API_USER_ID = "用户id"; + public static final String METHOD_API_APPLY_ID = "申请表单id"; + } + + public static class RejectLowAuth { + public static final String METHOD_API_NAME = "驳回用户初级审核申请"; + public static final String METHOD_API_NOTE = "驳回用户初级审核申请"; + + public static final String METHOD_API_USER_ID = "用户id"; + public static final String METHOD_API_APPLY_ID = "申请表单id"; + public static final String METHOD_API_REMARK = "驳回原因(备注)"; + } + + public static class PassHighAuth { + public static final String METHOD_API_NAME = "通过用户高级审核申请"; + public static final String METHOD_API_NOTE = "通过用户高级审核申请"; + + public static final String METHOD_API_USER_ID = "用户id"; + public static final String METHOD_API_APPLY_ID = "申请表单id"; + } + + public static class RejectHighAuth { + public static final String METHOD_API_NAME = "驳回用户高级审核申请"; + public static final String METHOD_API_NOTE = "驳回用户高级审核申请"; + + public static final String METHOD_API_USER_ID = "用户id"; + public static final String METHOD_API_APPLY_ID = "申请表单id"; + public static final String METHOD_API_REMARK = "驳回原因(备注)"; + } + + public static class SelectUserAuthentication { + public static final String METHOD_API_NAME = "查询用户认证信息"; + public static final String METHOD_API_NOTE = "查询用户认证信息"; + + public static final String METHOD_API_USER_ID = "用户id"; + } + + public static class SelectUserAuthenticationInfo { + public static final String METHOD_API_NAME = "查询用户基本认证信息"; + public static final String METHOD_API_NOTE = "查询用户基本认证信息"; + + public static final String METHOD_API_USER_ID = "用户id"; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/UserLoginLogApi.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/UserLoginLogApi.java new file mode 100644 index 0000000..3d35f64 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/controller/api/UserLoginLogApi.java @@ -0,0 +1,21 @@ +package com.blockchain.server.user.controller.api; + +/** + * @author Harvey + * @date 2019/3/5 15:26 + * @user WIN10 + */ +public class UserLoginLogApi { + public static final String USER_LOGIN_LOG_API = "登录日志控制器"; + + public static final String METHOD_API_PAGE_NUM = "当前页数"; + public static final String METHOD_API_PAGE_SIZE = "当前页面内容"; + + public static class ListUserLoginLog { + public static final String MATHOD_API_NAME = "查询用户登录日志"; + public static final String MATHOD_API_NOTE = "查询用户登录日志"; + + public static final String MATHOD_API_USER_ID = "用户id"; + } + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/dto/PushInfoDTO.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/dto/PushInfoDTO.java new file mode 100644 index 0000000..13509ae --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/dto/PushInfoDTO.java @@ -0,0 +1,31 @@ +package com.blockchain.server.user.dto; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.enums.PushEnums; +import lombok.Data; + +@Data +public class PushInfoDTO { + + private String pushType; + private String pushTitle; + private String pushContent; + + public PushInfoDTO(PushEnums pushEnums, String language) { + String title = ""; + String content = ""; + switch (language) { + case BaseConstant.USER_LOCALE_EN_US: + title = pushEnums.getEnTitle(); + content = pushEnums.getEnContent(); + break; + default: + title = pushEnums.getCnTitle(); + content = pushEnums.getCnContent(); + break; + } + this.pushType = pushEnums.getPushType(); + this.pushTitle = title; + this.pushContent = content; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/dto/UserBaseDTO.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/dto/UserBaseDTO.java new file mode 100644 index 0000000..71f7c73 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/dto/UserBaseDTO.java @@ -0,0 +1,24 @@ +package com.blockchain.server.user.dto; + +import lombok.Data; + +/** + * 用户基本信息 + */ +@Data +public class UserBaseDTO { + private String id;//用户id + private String mobilePhone;//手机号 + private String nickName;//昵称 + private String avatar;//头像 + private String email;//email + private String lowAuth;//低级认证 + private String highAuth;//高级认证 + private String grade;//用户等级 + + + private boolean hasPassword;//是否设置了登录密码 + private String token;//token信息 + private String invitationCode;//邀请码 + private boolean bindGoogleAuth;//谷歌验证器 +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/dto/UserLoginLogDto.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/dto/UserLoginLogDto.java new file mode 100644 index 0000000..98ae001 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/dto/UserLoginLogDto.java @@ -0,0 +1,19 @@ +package com.blockchain.server.user.dto; + +import lombok.Data; + +/** + * @author Harvey + * @date 2019/3/5 15:37 + * @user WIN10 + */ +@Data +public class UserLoginLogDto { + private String id; + private String nickName; + private String realName; + private String mobilePhone; + private String ipAddress; + private String createTime; + private String status; +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/AuthenticationApply.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/AuthenticationApply.java new file mode 100644 index 0000000..3e378c0 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/AuthenticationApply.java @@ -0,0 +1,81 @@ +package com.blockchain.server.user.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * AuthenticationApply 数据传输类 + * @date 2019-02-21 13:37:17 + * @version 1.0 + */ +@Table(name = "dapp_u_authentication_apply") +@Data +public class AuthenticationApply extends BaseModel { + @Id + @Column(name = "id") + private String id; + /** + * 用户id + */ + @Column(name = "user_id") + private String userId; + /** + * 证件类型 + */ + @Column(name = "type") + private String type; + /** + * 证件号 + */ + @Column(name = "id_number") + private String idNumber; + /** + * 真是姓名 + */ + @Column(name = "real_name") + private String realName; + /** + * 文件路径1 + */ + @Column(name = "file_url1") + private String fileUrl1; + + /** + * 文件路径2 + */ + @Column(name = "file_url2") + private String fileUrl2; + /** + * 文件路径3 + */ + @Column(name = "file_url3") + private String fileUrl3; + /** + * 审核状态 + */ + @Column(name = "status") + private String status; + /** + * 审核备注 + */ + @Column(name = "remark") + private String remark; + + /** + * 创建时间 + */ + @Column(name = "create_time") + private java.util.Date createTime; + /** + * 修改时间 + */ + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/AuthenticationApplyLog.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/AuthenticationApplyLog.java new file mode 100644 index 0000000..a841286 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/AuthenticationApplyLog.java @@ -0,0 +1,39 @@ +package com.blockchain.server.user.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * AuthenticationApplyLog + * + * @version 1.0 + * @date 2019-02-21 13:37:18 + */ +@Table(name = "pc_u_authentication_apply_log") +@Data +public class AuthenticationApplyLog extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "ip_address") + private String ipAddress; + @Column(name = "sys_user_id") + private String SysUserId; + @Column(name = "user_id") + private String userId; + @Column(name = "apply_id") + private String applyId; + @Column(name = "type") + private String type; + @Column(name = "apply_result") + private String applyResult; + @Column(name = "remark") + private String remark; + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/Config.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/Config.java new file mode 100644 index 0000000..c7b5212 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/Config.java @@ -0,0 +1,32 @@ +package com.blockchain.server.user.entity; + +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * @author huangxl + * @create 2019-02-25 17:45 + */ +@Data +@Table(name = "dapp_u_config") +public class Config { + @Id + @Column(name = "id") + private String id; + @Column(name = "data_tag") + private String dataTag; + @Column(name = "data_key") + private String dataKey; + @Column(name = "data_value") + private String dataValue; + @Column(name = "data_status") + private String dataStatus; + @Column(name = "create_time") + private Date createTime; + @Column(name = "modify_time") + private Date modifyTime; +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/HighAuthenticationApply.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/HighAuthenticationApply.java new file mode 100644 index 0000000..a8af360 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/HighAuthenticationApply.java @@ -0,0 +1,59 @@ +package com.blockchain.server.user.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * HighAuthenticationApply 数据传输类 + * @date 2019-02-21 13:37:18 + * @version 1.0 + * 高级认证申请对象 + */ +@Table(name = "dapp_u_high_authentication_apply") +@Data +public class HighAuthenticationApply extends BaseModel { + @Id + @Column(name = "id") + private String id; + /** + * 用户ID + */ + @Column(name = "user_id") + private String userId; + + /** + * 文件路径 + */ + @Column(name = "file_url") + private String fileUrl; + + /** + * 认证状态 + */ + @Column(name = "status") + private String status; + /** + * 审核备注 + */ + @Column(name = "remark") + private String remark; + + /** + * 创建时间 + */ + @Column(name = "create_time") + private java.util.Date createTime; + + /** + * 修改时间 + */ + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/PushUser.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/PushUser.java new file mode 100644 index 0000000..488832e --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/PushUser.java @@ -0,0 +1,37 @@ +package com.blockchain.server.user.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * User 数据传输类 + * + * @version 1.0 + * @date 2019-06-18 17:27:35 + */ +@Table(name = "push_user") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PushUser extends BaseModel { + @Id + @Column(name = "id") + private String id; + @Column(name = "user_id") + private String userId; + @Column(name = "client_id") + private String clientId; + @Column(name = "language") + private String language; + @Column(name = "create_time") + private java.util.Date createTime; + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/SmsCount.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/SmsCount.java new file mode 100644 index 0000000..244d0cf --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/SmsCount.java @@ -0,0 +1,24 @@ +package com.blockchain.server.user.entity; + +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +@Data +@Table(name = "dapp_u_sms_count") +public class SmsCount { + @Id + @Column(name = "id") + private String id; + @Column(name = "phone") + private String phone; + @Column(name = "sms_type") + private String smsType; + @Column(name = "sms_count") + private Integer smsCount; + @Column(name = "sms_date") + private Date smsDate; +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserAuthentication.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserAuthentication.java new file mode 100644 index 0000000..86c9551 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserAuthentication.java @@ -0,0 +1,80 @@ +package com.blockchain.server.user.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * UserAuthentication 数据传输类 + * @date 2019-02-21 13:37:18 + * @version 1.0 + */ +@Table(name = "dapp_u_user_authentication") +@Data +public class UserAuthentication extends BaseModel { + @Id + @Column(name = "id") + private String id; + /** + * 用户id + */ + @Column(name = "user_id") + private String userId; + + /** + * 证件类型 + */ + @Column(name = "type") + private String type; + /** + * 证件号 + */ + @Column(name = "id_number") + private String idNumber; + + /** + * 真实姓名 + */ + @Column(name = "real_name") + private String realName; + /** + * 文件路径1 + */ + @Column(name = "file_url1") + private String fileUrl1; + + /** + * 文件路径2 + */ + @Column(name = "file_url2") + private String fileUrl2; + + /** + * 文件路径3 + */ + @Column(name = "file_url3") + private String fileUrl3; + /** + * 高级认证文件路径 + */ + @Column(name = "file_url4") + private String fileUrl4; + + /** + * 创建时间 + */ + @Column(name = "create_time") + private java.util.Date createTime; + + /** + * 修改时间 + */ + @Column(name = "midify_time") + private java.util.Date midifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserInfo.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserInfo.java new file mode 100644 index 0000000..0af34a2 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserInfo.java @@ -0,0 +1,89 @@ +package com.blockchain.server.user.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * UserInfo 用户登录信息,数据传输类 + * + * @version 1.0 + * @date 2019-02-21 13:37:18 + */ +@Table(name = "dapp_u_user_info") +@Data +public class UserInfo extends BaseModel { + @Id + @Column(name = "id") + private String id; + /** + * 用户id + */ + @Column(name = "user_id") + private String userId; + + /** + * 用户邮箱 + */ + @Column(name = "email") + private String email; + /** + * 用户头像 + */ + @Column(name = "avatar") + private String avatar; + + /** + * 谷歌验证器 + */ + @Column(name = "google_auth") + private String googleAuth; + + /** + * 初级认证状态 + */ + @Column(name = "low_auth") + private String lowAuth; + + /** + * 高级认证状态 + */ + @Column(name = "high_auth") + private String highAuth; + + /** + * 自增码 + */ + @Column(name = "incr_code") + private Integer incrCode; + + /** + * 随机数 + */ + @Column(name = "random_number") + private Integer randomNumber; + + /** + * 用户等级 + */ + @Column(name = "grade") + private String grade; + + /** + * 创建时间 + */ + @Column(name = "create_time") + private java.util.Date createTime; + + /** + * 修改时间 + */ + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserList.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserList.java new file mode 100644 index 0000000..d49bdfc --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserList.java @@ -0,0 +1,47 @@ +package com.blockchain.server.user.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * UserList 用户黑白名单,数据传输类 + * @date 2019-02-21 13:37:18 + * @version 1.0 + */ +@Table(name = "dapp_u_user_list") +@Data +public class UserList extends BaseModel { + @Id + @Column(name = "id") + private String id; + /** + * 用户id + */ + @Column(name = "user_id") + private String userId; + + /** + * 名单类型 + */ + @Column(name = "list_type") + private String listType; + + /** + * 类型 + */ + @Column(name = "type") + private String type; + + /** + * 创建时间 + */ + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserLogin.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserLogin.java new file mode 100644 index 0000000..ae89157 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserLogin.java @@ -0,0 +1,47 @@ +package com.blockchain.server.user.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * UserLogin 数据传输类 + * @date 2019-02-21 13:37:18 + * @version 1.0 + */ +@Table(name = "dapp_u_user_login") +@Data +public class UserLogin extends BaseModel { + @Id + @Column(name = "id") + private String id; + /** + * 用户id + */ + @Column(name = "user_id") + private String userId; + + /** + * 密码 + */ + @Column(name = "password") + private String password; + + /** + * 创建时间 + */ + @Column(name = "create_time") + private java.util.Date createTime; + + /** + * 修改时间 + */ + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserLoginLog.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserLoginLog.java new file mode 100644 index 0000000..7f4c089 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserLoginLog.java @@ -0,0 +1,46 @@ +package com.blockchain.server.user.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * UserLoginLog 数据传输类 + * @date 2019-02-21 13:37:18 + * @version 1.0 + */ +@Table(name = "dapp_u_user_login_log") +@Data +public class UserLoginLog extends BaseModel { + @Id + @Column(name = "id") + private String id; + /** + * 用户id + */ + @Column(name = "user_id") + private String userId; + /** + * ip地址 + */ + @Column(name = "ip_address") + private String ipAddress; + + /** + * ip地址 + */ + @Column(name = "status") + private String status; + + /** + * 创建时间 + */ + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserMain.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserMain.java new file mode 100644 index 0000000..15b11ea --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserMain.java @@ -0,0 +1,58 @@ +package com.blockchain.server.user.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * UserMain 数据传输类 + * @date 2019-02-21 13:37:18 + * @version 1.0 + */ +@Table(name = "dapp_u_user_main") +@Data +public class UserMain extends BaseModel { + @Id + @Column(name = "id") + private String id; + /** + * 昵称 + */ + @Column(name = "nick_name") + private String nickName; + /** + * 手机国际区号 + */ + @Column(name = "international_code") + private String internationalCode; + + /** + * 手机国际区号 + */ + @Column(name = "international") + private String international; + + /** + * 手机号 + */ + @Column(name = "mobile_phone") + private String mobilePhone; + + /** + * 创建时间 + */ + @Column(name = "create_time") + private java.util.Date createTime; + + /** + * 修改时间 + */ + @Column(name = "modify_time") + private java.util.Date modifyTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserOptLog.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserOptLog.java new file mode 100644 index 0000000..4b93a84 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserOptLog.java @@ -0,0 +1,53 @@ +package com.blockchain.server.user.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * UserOptLog 用户操作记录,数据传输类 + * @date 2019-02-21 13:37:18 + * @version 1.0 + */ +@Table(name = "dapp_u_user_opt_log") +@Data +public class UserOptLog extends BaseModel { + @Id + @Column(name = "id") + private String id; + /** + * 用户id + */ + @Column(name = "user_id") + private String userId; + + /** + * 用户ip + */ + @Column(name = "ip_address") + private String ipAddress; + + /** + * 操作类型 + */ + @Column(name = "opt_type") + private String optType; + + /** + * 操作内容 + */ + @Column(name = "content") + private String content; + + /** + * 创建时间 + */ + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserRelation.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserRelation.java new file mode 100644 index 0000000..5010be2 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/entity/UserRelation.java @@ -0,0 +1,52 @@ +package com.blockchain.server.user.entity; + +import com.blockchain.common.base.entity.BaseModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * UserRelation 用户关系, 数据传输类 + * + * @version 1.0 + * @date 2019-02-21 13:37:18 + */ +@Table(name = "dapp_u_user_relation") +@Data +public class UserRelation extends BaseModel { + @Id + @Column(name = "id") + private String id; + /** + * 当前用户id + */ + @Column(name = "user_id") + private String userId; + /** + * 父id + */ + @Column(name = "pid") + private String pid; + /** + * 关系链信息 + */ + @Column(name = "relation_chain") + private String relationChain; + + /** + * 关系深度 + */ + @Column(name = "tree_depth") + private Integer treeDepth; + + /** + * 创建时间 + */ + @Column(name = "create_time") + private java.util.Date createTime; + +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/feign/BtcFeign.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/feign/BtcFeign.java new file mode 100644 index 0000000..a098cc9 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/feign/BtcFeign.java @@ -0,0 +1,16 @@ +package com.blockchain.server.user.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author huangxl + * @create 2019-02-28 17:40 + */ +@FeignClient("dapp-btc-server") +public interface BtcFeign { + @GetMapping("/inner/wallet/createWallet") + ResultDTO createWallet(@RequestParam("userOpenId") String userOpenId); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/feign/EosFeign.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/feign/EosFeign.java new file mode 100644 index 0000000..e31639e --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/feign/EosFeign.java @@ -0,0 +1,16 @@ +package com.blockchain.server.user.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author huangxl + * @create 2019-02-28 17:39 + */ +@FeignClient("dapp-eos-server") +public interface EosFeign { + @GetMapping("/inner/walletTx/initEosWallet") + ResultDTO initEosWallet(@RequestParam("userOpenId") String userOpenId); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/feign/EthFeign.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/feign/EthFeign.java new file mode 100644 index 0000000..177a386 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/feign/EthFeign.java @@ -0,0 +1,16 @@ +package com.blockchain.server.user.feign; + +import com.blockchain.common.base.dto.ResultDTO; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author huangxl + * @create 2019-02-28 17:39 + */ +@FeignClient("dapp-eth-server") +public interface EthFeign { + @GetMapping("/inner/wallet/initWallets") + ResultDTO initWallets(@RequestParam("userOpenId") String userOpenId); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/PushInner.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/PushInner.java new file mode 100644 index 0000000..0bf4903 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/PushInner.java @@ -0,0 +1,31 @@ +package com.blockchain.server.user.inner; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.enums.PushEnums; +import com.blockchain.server.user.inner.api.PushInnerApi; +import com.blockchain.server.user.service.PushService; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@RestController +@RequestMapping("/inner/push") +public class PushInner { + + @Autowired + private PushService pushService; + + @ApiOperation(value = PushInnerApi.PushToSingle.METHOD_NAME, + notes = PushInnerApi.PushToSingle.METHOD_NOTE) + @PostMapping("/pushToSingle") + public ResultDTO pushToSingle(@ApiParam(PushInnerApi.PushToSingle.METHOD_API_USER_ID) @RequestParam("/userId") String userId, + @ApiParam(PushInnerApi.PushToSingle.METHOD_API_PUSH_TYPE) @RequestParam("/pushType") String pushType, + @ApiParam(PushInnerApi.PushToSingle.METHOD_API_PAYLOAD) @RequestBody Map payload) { + pushService.pushToSingle(userId, pushType, payload); + return ResultDTO.requstSuccess(); + } + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/UserInner.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/UserInner.java new file mode 100644 index 0000000..5e2d536 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/UserInner.java @@ -0,0 +1,203 @@ +package com.blockchain.server.user.inner; + +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.SessionUserDTO; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.user.common.constants.sql.UserListConstant; +import com.blockchain.server.user.common.enums.SmsCountEnum; +import com.blockchain.server.user.common.enums.UserEnums; +import com.blockchain.server.user.common.exceprion.UserException; +import com.blockchain.server.user.common.utils.CheckUtils; +import com.blockchain.server.user.common.utils.EmailCodeUtils; +import com.blockchain.server.user.common.utils.SmsCodeUtils; +import com.blockchain.server.user.entity.UserMain; +import com.blockchain.server.user.entity.UserRelation; +import com.blockchain.server.user.inner.api.UserInnerApi; +import com.blockchain.server.user.service.*; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author huangxl + * @create 2019-03-04 10:53 + */ +@RestController +@RequestMapping("/inner") +public class UserInner { + @Autowired + private RedisTemplate redisTemplate; + @Autowired + private UserListService userListService; + @Autowired + private UserMainService userMainService; + @Autowired + private SmsCountService smsCountService; + @Autowired + private SmsCodeUtils smsCodeUtils; + @Autowired + private UserService userService; + @Autowired + private EmailCodeUtils emailCodeUtils; + @Autowired + private UserRelationService userRelationService; + + /** + * 禁止交易 + * + * @param userId + * @param request + * @return + */ + @GetMapping("/hasTransactionPermission") + @ApiOperation(value = UserInnerApi.HasTransactionPermission.METHOD_NAME, + notes = UserInnerApi.HasTransactionPermission.METHOD_NOTE) + public ResultDTO verifyEmailCode(@ApiParam(UserInnerApi.HasTransactionPermission.METHOD_API_USER_ID) @RequestParam("userId") String userId, HttpServletRequest request) { +// String userId = SSOHelper.getUserId(redisTemplate, request); + boolean exit = userListService.checkUserByUserIdAndType(UserListConstant.LIST_TYPE_BLACK, userId, UserListConstant.TYPE_BAN_TRANSACTION); + + //返回true,代表在黑名单中 + if (exit) { + throw new UserException(UserEnums.TRANSACTION_FORBIDDEN); + } + return ResultDTO.requstSuccess(); + } + + /** + * 禁止提现 + * + * @param userId + * @return + */ + @PostMapping("/verifyBanWithdraw") + @ApiOperation(value = UserInnerApi.VerifyBanWithdraw.METHOD_NAME, + notes = UserInnerApi.VerifyBanWithdraw.METHOD_NOTE) + public ResultDTO verifyBanWithdraw(@ApiParam(UserInnerApi.VerifyBanWithdraw.METHOD_API_USER_ID) @RequestParam("userId") String userId) { + boolean exit = userListService.checkUserByUserIdAndType(UserListConstant.LIST_TYPE_BLACK, userId, UserListConstant.TYPE_BAN_OUT); + //返回true,代表在黑名单中 + if (exit) { + throw new UserException(UserEnums.WITHDRAW_FORBIDDEN); + } + return ResultDTO.requstSuccess(); + } + + /** + * 免提现手续费 + * + * @param userId + * @return + */ + @PostMapping("/verifyFreeWithdraw") + @ApiOperation(value = UserInnerApi.VerifyFreeWithdraw.METHOD_NAME, + notes = UserInnerApi.VerifyFreeWithdraw.METHOD_NOTE) + public ResultDTO verifyFreeWithdraw(@ApiParam(UserInnerApi.VerifyFreeWithdraw.METHOD_API_USER_ID) @RequestParam("userId") String userId) { + // 返回true表示用户免手续费 + boolean exit = userListService.checkUserByUserIdAndType(UserListConstant.LIST_TYPE_WHITE, userId, UserListConstant.TYPE_FREE_TX); + return ResultDTO.requstSuccess(exit); + } + + /** + * 免交易手续费 + * + * @param userId + * @return + */ + @PostMapping("/verifyFreeTransaction") + @ApiOperation(value = UserInnerApi.VerifyFreeTransaction.METHOD_NAME, + notes = UserInnerApi.VerifyFreeTransaction.METHOD_NOTE) + public ResultDTO verifyFreeTransaction(@ApiParam(UserInnerApi.VerifyFreeTransaction.METHOD_API_USER_ID) @RequestParam("userId") String userId) { + // 返回true表示用户免手续费 + boolean exit = userListService.checkUserByUserIdAndType(UserListConstant.LIST_TYPE_WHITE, userId, UserListConstant.TYPE_FREE_TRANSACTION); + return ResultDTO.requstSuccess(exit); + } + + /** + * 发布广告 + * + * @param userId + * @return + */ + @PostMapping("/verifyFreePushAd") + @ApiOperation(value = UserInnerApi.VerifyFreePushAd.METHOD_NAME, + notes = UserInnerApi.VerifyFreePushAd.METHOD_NOTE) + public ResultDTO verifyFreePushAd(@ApiParam(UserInnerApi.VerifyFreePushAd.METHOD_API_USER_ID) @RequestParam("userId") String userId) { + // 返回true表示用户允许发布广告 + boolean exit = userListService.checkUserByUserIdAndType(UserListConstant.LIST_TYPE_WHITE, userId, UserListConstant.TYPE_PUSH_AD); + return ResultDTO.requstSuccess(exit); + } + + @PostMapping("/sendWalletPwCode") + @ApiOperation(value = UserInnerApi.SendUpdateWalletPassword.METHOD_NAME, + notes = UserInnerApi.SendUpdateWalletPassword.METHOD_NOTE) + public ResultDTO sendWalletPwCode(HttpServletRequest request) { + String userId = SSOHelper.getUserId(redisTemplate, request); + UserMain userMain = userMainService.selectById(userId); + if (CheckUtils.checkMobilePhone(userMain.getMobilePhone())) { + smsCountService.handleInsertSmsCode(userMain.getMobilePhone(), userMain.getInternationalCode(), SmsCountEnum.SMS_COUNT_UPDATE_WALLET_PASSWORD); + } else if (CheckUtils.checkEmail(userMain.getMobilePhone())) { + emailCodeUtils.sendSmsCodeAndStoreToCache(userMain.getMobilePhone(), HttpRequestUtil.getUserLocale(request), SmsCountEnum.SMS_COUNT_UPDATE_WALLET_PASSWORD); + } else { + throw new UserException(UserEnums.FORMAT_ERROR); + } + + return ResultDTO.requstSuccess(); + } + + @GetMapping("/updateWalletPassword") + @ApiOperation(value = UserInnerApi.UpdateWalletPassword.METHOD_NAME, + notes = UserInnerApi.UpdateWalletPassword.METHOD_NOTE) + public ResultDTO updateWalletPassword(@ApiParam(UserInnerApi.UpdateWalletPassword.METHOD_API_CODE) @RequestParam("code") String code, + HttpServletRequest request) { + SessionUserDTO user = SSOHelper.getUser(redisTemplate, request); + smsCodeUtils.validateVerifyCode(code, user.getTel(), SmsCountEnum.SMS_COUNT_UPDATE_WALLET_PASSWORD); + smsCodeUtils.removeKey(user.getTel(), SmsCountEnum.SMS_COUNT_UPDATE_WALLET_PASSWORD); + return ResultDTO.requstSuccess(); + } + + @GetMapping("/selectUserInfoById") + @ApiOperation(value = UserInnerApi.selectUserInfoById.METHOD_NAME, + notes = UserInnerApi.selectUserInfoById.METHOD_NOTE) + public ResultDTO selectUserInfoById(@ApiParam(UserInnerApi.selectUserInfoById.METHOD_API_USERID) @RequestParam("userId") String userId) { + return ResultDTO.requstSuccess(userMainService.selectUserInfoById(userId)); + } + + /************* 验证码 *************/ + @ApiOperation(value = UserInnerApi.ValidateSmsg.METHOD_TITLE_NAME, notes = UserInnerApi.ValidateSmsg.METHOD_TITLE_NOTE) + @PostMapping("/validateSmsg") + public ResultDTO validateSmsg(HttpServletRequest request, + @ApiParam(UserInnerApi.ValidateSmsg.METHOD_API_VERIFYCODE) @RequestParam("verifyCode") String verifyCode, + @ApiParam(UserInnerApi.ValidateSmsg.METHOD_API_PHONE) @RequestParam("phone") String phone) { + smsCodeUtils.validateVerifyCode(verifyCode, phone, SmsCountEnum.SMS_COUNT_UPDATE_WALLET_PASSWORD); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = UserInnerApi.hasLowAuthAndUserList.METHOD_TITLE_NAME, + notes = UserInnerApi.hasLowAuthAndUserList.METHOD_TITLE_NOTE) + @GetMapping("/hasLowAuthAndUserList") + public ResultDTO hasLowAuthAndUserList(@ApiParam(UserInnerApi.hasLowAuthAndUserList.METHOD_API_USERID) @RequestParam("userId") String userId) { + userService.hasLowAuthAndUserList(userId); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = UserInnerApi.hasHighAuthAndUserList.METHOD_TITLE_NAME, + notes = UserInnerApi.hasHighAuthAndUserList.METHOD_TITLE_NOTE) + @GetMapping("/hasHighAuthAndUserList") + public ResultDTO hasHighAuthAndUserList(@ApiParam(UserInnerApi.hasHighAuthAndUserList.METHOD_API_USERID) @RequestParam("userId") String userId) { + userService.hasHighAuthAndUserList(userId); + return ResultDTO.requstSuccess(); + } + + @ApiOperation(value = UserInnerApi.SelectRelationByUserId.METHOD_TITLE_NAME, + notes = UserInnerApi.SelectRelationByUserId.METHOD_TITLE_NOTE) + @GetMapping("/selectRelationByUserId") + public ResultDTO selectRelationByUserId(@ApiParam(UserInnerApi.SelectRelationByUserId.METHOD_API_USERID) @RequestParam("userId") String userId) { + UserRelation result = userRelationService.findByUserId(userId); + return ResultDTO.requstSuccess(result); + } + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/api/PushInnerApi.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/api/PushInnerApi.java new file mode 100644 index 0000000..9e80539 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/api/PushInnerApi.java @@ -0,0 +1,12 @@ +package com.blockchain.server.user.inner.api; + +public class PushInnerApi { + + public static class PushToSingle { + public static final String METHOD_NAME = "对单个用户推送消息"; + public static final String METHOD_NOTE = "对单个用户推送消息"; + public static final String METHOD_API_USER_ID = "用户id"; + public static final String METHOD_API_PUSH_TYPE = "推送通知信息类型"; + public static final String METHOD_API_PAYLOAD = "透传数据(通知附带数据)"; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/api/UserInnerApi.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/api/UserInnerApi.java new file mode 100644 index 0000000..81e10f2 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/inner/api/UserInnerApi.java @@ -0,0 +1,79 @@ +package com.blockchain.server.user.inner.api; + +/** + * @author huangxl + * @create 2019-03-04 10:54 + */ +public class UserInnerApi { + public static class HasTransactionPermission { + public static final String METHOD_NAME = "查询用户是否有交易权限接口"; + public static final String METHOD_NOTE = "查询用户是否有交易权限接口"; + public static final String METHOD_API_USER_ID = "用户id"; + } + + public static class VerifyBanWithdraw { + public static final String METHOD_NAME = "查询用户是否有提现权限接口"; + public static final String METHOD_NOTE = "查询用户是否有提现权限接口"; + public static final String METHOD_API_USER_ID = "用户id"; + } + + public static class VerifyFreeWithdraw { + public static final String METHOD_NAME = "查询用户是否有免提现手续费权限接口"; + public static final String METHOD_NOTE = "查询用户是否有免提现手续费权限接口"; + public static final String METHOD_API_USER_ID = "用户id"; + } + + public static class VerifyFreeTransaction { + public static final String METHOD_NAME = "查询用户是否有免交易手续费权限接口"; + public static final String METHOD_NOTE = "查询用户是否有免交易手续费权限接口"; + public static final String METHOD_API_USER_ID = "用户id"; + } + + public static class VerifyFreePushAd { + public static final String METHOD_NAME = "查询用户是否有发布广告权限接口"; + public static final String METHOD_NOTE = "查询用户是否有发布广告权限接口"; + public static final String METHOD_API_USER_ID = "用户id"; + } + + public static class SendUpdateWalletPassword { + public static final String METHOD_NAME = "发送重置、修改资金密码验证码接口"; + public static final String METHOD_NOTE = "发送重置、修改资金密码验证码接口"; + } + + public static class UpdateWalletPassword { + public static final String METHOD_NAME = "重置、修改资金密码验证码"; + public static final String METHOD_NOTE = "重置、修改资金密码验证码"; + public static final String METHOD_API_CODE = "验证码"; + } + + public static class selectUserInfoById { + public static final String METHOD_NAME = "查询用户主体信息"; + public static final String METHOD_NOTE = "查询用户主体信息"; + public static final String METHOD_API_USERID = "用户id"; + } + + public static class ValidateSmsg { + public static final String METHOD_TITLE_NAME = "验证修改资金密码短信验证码"; + public static final String METHOD_TITLE_NOTE = "验证短信验证码"; + public static final String METHOD_API_PHONE = "手机号"; + public static final String METHOD_API_VERIFYCODE = "验证码"; + } + + public static class hasLowAuthAndUserList { + public static final String METHOD_TITLE_NAME = "检查用户初级认证和黑名单"; + public static final String METHOD_TITLE_NOTE = "不通过认证或存在黑名单则抛出异常"; + public static final String METHOD_API_USERID = "用户id"; + } + + public static class hasHighAuthAndUserList { + public static final String METHOD_TITLE_NAME = "检查用户高级认证和黑名单"; + public static final String METHOD_TITLE_NOTE = "不通过认证或存在黑名单则抛出异常"; + public static final String METHOD_API_USERID = "用户id"; + } + + public class SelectRelationByUserId { + public static final String METHOD_TITLE_NAME = "根据用户id查询推荐关系"; + public static final String METHOD_TITLE_NOTE = "根据用户id查询推荐关系"; + public static final String METHOD_API_USERID = "根据用户id查询推荐关系"; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/AuthenticationApplyLogMapper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/AuthenticationApplyLogMapper.java new file mode 100644 index 0000000..38493c6 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/AuthenticationApplyLogMapper.java @@ -0,0 +1,17 @@ +package com.blockchain.server.user.mapper; + +import com.blockchain.server.user.entity.AuthenticationApplyLog; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * AppUAuthenticationApplyMapper 数据访问类 + * @date 2019-02-21 13:37:17 + * @version 1.0 + */ +@Repository +public interface AuthenticationApplyLogMapper extends Mapper { + + AuthenticationApplyLog selectRemarkByUserId(@Param("type") String type, @Param("userId") String userId); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/AuthenticationApplyMapper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/AuthenticationApplyMapper.java new file mode 100644 index 0000000..9001548 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/AuthenticationApplyMapper.java @@ -0,0 +1,22 @@ +package com.blockchain.server.user.mapper; + +import com.blockchain.server.user.entity.AuthenticationApply; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * AppUAuthenticationApplyMapper 数据访问类 + * @date 2019-02-21 13:37:17 + * @version 1.0 + */ +@Repository +public interface AuthenticationApplyMapper extends Mapper { + + /** + * 查询用户初级认证状态 + * @param userId + * @return + */ + String judgeAuthentication(@Param("userId") String userId); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/ConfigMapper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/ConfigMapper.java new file mode 100644 index 0000000..6aeb6f6 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/ConfigMapper.java @@ -0,0 +1,13 @@ +package com.blockchain.server.user.mapper; + +import com.blockchain.server.user.entity.Config; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * @author huangxl + * @create 2019-02-25 17:51 + */ +@Repository +public interface ConfigMapper extends Mapper { +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/HighAuthenticationApplyMapper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/HighAuthenticationApplyMapper.java new file mode 100644 index 0000000..ef60990 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/HighAuthenticationApplyMapper.java @@ -0,0 +1,22 @@ +package com.blockchain.server.user.mapper; + +import com.blockchain.server.user.entity.HighAuthenticationApply; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * AppUHighAuthenticationApplyMapper 数据访问类 + * @date 2019-02-21 13:37:18 + * @version 1.0 + */ +@Repository +public interface HighAuthenticationApplyMapper extends Mapper { + + /** + * 查询用户高级审核状态 + * @param userId + * @return + */ + String judgeAuthentication(@Param("userId") String userId); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/PushUserMapper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/PushUserMapper.java new file mode 100644 index 0000000..db37055 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/PushUserMapper.java @@ -0,0 +1,43 @@ +package com.blockchain.server.user.mapper; + +import com.blockchain.server.user.entity.PushUser; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.Date; + +/** + * UserMapper 数据访问类 + * + * @version 1.0 + * @date 2019-06-18 17:27:35 + */ +@Repository +public interface PushUserMapper extends Mapper { + + /*** + * 根据用户id查询 + * @param userId + * @return + */ + PushUser selectByUserId(@Param("userId") String userId); + + /*** + * 根据客户端id查询 + * @param clientId + * @return + */ + PushUser selectByClientId(@Param("clientId") String clientId); + + /*** + * 根据用户id更新 + * @param userId + * @param clientId + * @param language + * @param modifyTime + * @return + */ + int updateUser(@Param("userId") String userId, @Param("clientId") String clientId, + @Param("language") String language, @Param("modifyTime") Date modifyTime); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/SmsCountMapper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/SmsCountMapper.java new file mode 100644 index 0000000..5f8c397 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/SmsCountMapper.java @@ -0,0 +1,34 @@ +package com.blockchain.server.user.mapper; + +import com.blockchain.server.user.entity.SmsCount; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.Date; + +@Repository +public interface SmsCountMapper extends Mapper { + + /** + * 获取某个日期的获取短信记录 + * @param phone 手机号 + * @param smsType 类型 + * @param smsDate 日期 + * @return + */ + SmsCount getSmsRecordByPhoneAndTypeOfDay(@Param("phone") String phone, + @Param("smsType") String smsType, + @Param("smsDate") Date smsDate); + + /** + * 更新手机获取短信记录+1 + * @param phone 手机号 + * @param smsType 类型 + * @param count 原来的数量 + * @return 影响的行数 + */ + int updateIncrCountByPhoneAndTypeInRowLock(@Param("phone") String phone, + @Param("smsType") String smsType, + @Param("count") int count); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserAuthenticationMapper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserAuthenticationMapper.java new file mode 100644 index 0000000..822619b --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserAuthenticationMapper.java @@ -0,0 +1,14 @@ +package com.blockchain.server.user.mapper; + +import com.blockchain.server.user.entity.UserAuthentication; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * AppUUserAuthenticationMapper 数据访问类 + * @date 2019-02-21 13:37:18 + * @version 1.0 + */ +@Repository +public interface UserAuthenticationMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserInfoMapper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserInfoMapper.java new file mode 100644 index 0000000..4e2e2c7 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserInfoMapper.java @@ -0,0 +1,19 @@ +package com.blockchain.server.user.mapper; + +import com.blockchain.server.user.entity.UserInfo; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * AppUUserInfoMapper 数据访问类 + * @date 2019-02-21 13:37:18 + * @version 1.0 + */ +@Repository +public interface UserInfoMapper extends Mapper { + /** + * 根据用户id查找用户信息 + * @param userId 用户id + */ + UserInfo selectByUserId(String userId); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserListMapper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserListMapper.java new file mode 100644 index 0000000..fd52c54 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserListMapper.java @@ -0,0 +1,14 @@ +package com.blockchain.server.user.mapper; + +import com.blockchain.server.user.entity.UserList; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * AppUUserListMapper 数据访问类 + * @date 2019-02-21 13:37:18 + * @version 1.0 + */ +@Repository +public interface UserListMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserLoginLogMapper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserLoginLogMapper.java new file mode 100644 index 0000000..5cdd645 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserLoginLogMapper.java @@ -0,0 +1,23 @@ +package com.blockchain.server.user.mapper; + +import com.blockchain.server.user.dto.UserLoginLogDto; +import com.blockchain.server.user.entity.UserLoginLog; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * @date 2019-02-21 13:37:18 + * @version 1.0 + */ +@Repository +public interface UserLoginLogMapper extends Mapper { + /** + * 查询用户登录日志 + * @param userId + * @return + */ + List listUserLoginLogByUserId(@Param("userId") String userId); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserLoginMapper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserLoginMapper.java new file mode 100644 index 0000000..4d930b2 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserLoginMapper.java @@ -0,0 +1,16 @@ +package com.blockchain.server.user.mapper; + +import com.blockchain.server.user.entity.UserLogin; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * @date 2019-02-21 13:37:18 + * @version 1.0 + */ +@Repository +public interface UserLoginMapper extends Mapper { + + UserLogin selectByUserId(@Param("userId") String userId); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserMainMapper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserMainMapper.java new file mode 100644 index 0000000..a8766b7 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserMainMapper.java @@ -0,0 +1,19 @@ +package com.blockchain.server.user.mapper; + +import com.blockchain.server.user.entity.UserMain; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +/** + * @date 2019-02-21 13:37:18 + * @version 1.0 + */ +@Repository +public interface UserMainMapper extends Mapper { + UserMain selectByMobilePhone(@Param("mobilePhone") String mobilePhone); + + List listByIds(@Param("ids")String[] ids); +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserOptLogMapper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserOptLogMapper.java new file mode 100644 index 0000000..112246b --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserOptLogMapper.java @@ -0,0 +1,14 @@ +package com.blockchain.server.user.mapper; + +import com.blockchain.server.user.entity.UserOptLog; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * AppUUserOptLogMapper 数据访问类 + * @date 2019-02-21 13:37:18 + * @version 1.0 + */ +@Repository +public interface UserOptLogMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserRelationMapper.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserRelationMapper.java new file mode 100644 index 0000000..5cf2ea4 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/mapper/UserRelationMapper.java @@ -0,0 +1,14 @@ +package com.blockchain.server.user.mapper; + +import com.blockchain.server.user.entity.UserRelation; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.common.Mapper; + +/** + * AppUUserRelationMapper 数据访问类 + * @date 2019-02-21 13:37:18 + * @version 1.0 + */ +@Repository +public interface UserRelationMapper extends Mapper { +} \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/AuthenticationApplyLogService.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/AuthenticationApplyLogService.java new file mode 100644 index 0000000..abc10f7 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/AuthenticationApplyLogService.java @@ -0,0 +1,12 @@ +package com.blockchain.server.user.service; + +import com.blockchain.server.user.entity.AuthenticationApplyLog; + +public interface AuthenticationApplyLogService { + + + Integer insert(AuthenticationApplyLog authenticationApplyLog); + + AuthenticationApplyLog selectRemarkByUserId(String type, String userId); + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/AuthenticationApplyService.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/AuthenticationApplyService.java new file mode 100644 index 0000000..1ae1951 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/AuthenticationApplyService.java @@ -0,0 +1,47 @@ +package com.blockchain.server.user.service; + +/** + * 申请认证服务 + * @author huangxl + * @create 2019-02-26 14:20 + */ +public interface AuthenticationApplyService { + + + /** + * 查询高级未认证原因 + * @param userId + * @return + */ + String selectHighRemarkByUserId(String userId); + /** + * 查询初级未认证原因 + * @param userId + * @return + */ + String selectLowRemarkByUserId(String userId); + /** + * 插入初级认证申请记录 + * @param userId 用户id + * @param realName 真实名称 + * @param idNumber 身份证号 + * @param type 证件类型 + * @param imgs 文件地址 + */ + void insertBasicAuth(String userId,String realName, String idNumber, String type,String imgs); + + /** + * 插入高级认证申请记录 + * @param userId 用户id + * @param img 文件地址 + */ + void insertHighAuth(String userId, String img); + + /** + * 判断用户认证状态接口 + * @param userId + * @param authenticationType + * @return + */ + String judgeAuthentication(String userId, String authenticationType); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/ConfigService.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/ConfigService.java new file mode 100644 index 0000000..f81e5b0 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/ConfigService.java @@ -0,0 +1,11 @@ +package com.blockchain.server.user.service; + +import com.blockchain.server.user.entity.Config; + +/** + * @author huangxl + * @create 2019-02-25 17:48 + */ +public interface ConfigService { + String getValidValueByKey(String key); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/PushService.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/PushService.java new file mode 100644 index 0000000..4c8630b --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/PushService.java @@ -0,0 +1,16 @@ +package com.blockchain.server.user.service; + +import com.blockchain.common.base.enums.PushEnums; + +import java.util.Map; + +public interface PushService { + + /*** + * 对单个用户推送消息 + * @param userId + * @param pushType + * @param payLoadMap + */ + void pushToSingle(String userId, String pushType, Map payLoadMap); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/PushUserService.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/PushUserService.java new file mode 100644 index 0000000..8896a33 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/PushUserService.java @@ -0,0 +1,55 @@ +package com.blockchain.server.user.service; + +import com.blockchain.server.user.entity.PushUser; + +public interface PushUserService { + + /*** + * 新建或者更新客户端数据 + * @param userId + * @param clientId + * @param language + * @return + */ + Integer insertOrUpadteUser(String userId, String clientId, String language); + + /*** + * 新建用户客户端数据 + * @param userId + * @param clientId + * @param language + * @return + */ + Integer insertUser(String userId, String clientId, String language); + + /*** + * 更新用户客户端数据 + * @param userId + * @param clientId + * @param language + * @return + */ + Integer updateUser(String userId, String clientId, String language); + + /*** + * 更新用户语种 + * @param userId + * @param language + * @return + */ + Integer updateUserLanguage(String userId, String language); + + /*** + * 根据用户id查询 + * @param userId + * @return + */ + PushUser selectByUserId(String userId); + + /*** + * 根据客户端id查询 + * @param clientId + * @return + */ + PushUser selectByClientId(String clientId); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/SmsCountService.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/SmsCountService.java new file mode 100644 index 0000000..d709462 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/SmsCountService.java @@ -0,0 +1,14 @@ +package com.blockchain.server.user.service; + + +import com.blockchain.server.user.common.enums.SmsCountEnum; + +public interface SmsCountService { + /** + * 是否能发送短信 + * + * @return + */ + void handleInsertSmsCode(String phone, String internationalCode, SmsCountEnum type); + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserInfoService.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserInfoService.java new file mode 100644 index 0000000..0a9a5c1 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserInfoService.java @@ -0,0 +1,64 @@ +package com.blockchain.server.user.service; + +import com.blockchain.server.user.entity.UserInfo; + +/** + * @author huangxl + * @create 2019-02-23 18:21 + */ +public interface UserInfoService{ + + /** + * 保存用户信息 + * @param userId 用户id + * @param email + * @param hasRelation 是否有关系链信息 + */ + void saveUser(String userId, String email, boolean hasRelation); + + /** + * 根据自增长id查询用户信息 + * @param incrCode 自增长id + */ + UserInfo selectByIncrCode(Integer incrCode); + + /** + * 绑定谷歌验证器 + * @param userId 用户id + * @param key 谷歌验证器安全码 + * @param code + */ + void handleBindGoogleAuthenticator(String userId, String key, Long code); + + /** + * 根据用户id查询用户信息 + * @param userId 用户id + */ + UserInfo selectByUserId(String userId); + + /** + * 更新用户信息 + */ + void updateUserInfo(UserInfo userInfo); + + /** + * 绑定邮箱地址 + * @param userId 用户id + * @param email email + * @param code 验证码 + */ + void handleBindEmail(String userId, String email, String code); + + /** + * 更新用户头像信息 + * @param userId 用户id + * @param img 头像地址 + */ + void updateUserHeadImg(String userId, String img); + + /** + * 检查是否有重复邮箱 + * @param email 邮箱地址 + */ + void checkRepeatEmail(String email); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserListService.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserListService.java new file mode 100644 index 0000000..3f19e11 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserListService.java @@ -0,0 +1,11 @@ +package com.blockchain.server.user.service; + +/** + * 用户黑|白名单服务 + * + * @author huangxl + * @create 2019-02-23 15:20 + */ +public interface UserListService { + boolean checkUserByUserIdAndType(String listType, String userId, String type); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserLoginLogService.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserLoginLogService.java new file mode 100644 index 0000000..ff1b10a --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserLoginLogService.java @@ -0,0 +1,20 @@ +package com.blockchain.server.user.service; + +import com.blockchain.server.user.dto.UserLoginLogDto; + +import java.util.List; + +/** + * @author huangxl + * @create 2019-02-23 14:43 + */ +public interface UserLoginLogService { + int saveLoginLog(String userId, String status); + + /** + * 查询用户登录日志 + * @param userId + * @return + */ + List listUserLoginLogByUserId(String userId); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserLoginService.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserLoginService.java new file mode 100644 index 0000000..13be69f --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserLoginService.java @@ -0,0 +1,67 @@ +package com.blockchain.server.user.service; + + +import com.blockchain.server.user.entity.UserMain; + +/** + * @author huangxl + * @data 2019/2/21 15:17 + */ +public interface UserLoginService { + + /** + * 用户账号密码登录 + * + * @param tel 用户手机号 + * @param password 密码 + */ + UserMain handleLoginByPassword(String tel, String password); + + /** + * 插入记录信息 + * + * @param userId 用户id + * @param password 密码 + * @return + */ + int insertEntity(String userId, String password); + + /** + * 用户手机验证码登录 + * + * @param tel + * @return + */ + UserMain handleLoginByPhoneCode(String tel); + + /** + * 初次设置密码 + * + * @param userId 用户id + * @param password 密码 + */ + void insertPassword(String userId, String password); + + /** + * 修改密码 + * + * @param userId 用户id + * @param password 新密码 + * @param oldPassword 旧密码 + */ + void updatePassword(String userId, String password, String oldPassword); + + /** + * 修改密码 + * + * @param userId 用户id + * @param password 新密码 + */ + void updatePassword(String userId, String password); + + /** + * 查询该用户是否设置密码 + * @param userId 用户id + */ + boolean selectUserPasswordIsExist(String userId); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserMainService.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserMainService.java new file mode 100644 index 0000000..d64c4e4 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserMainService.java @@ -0,0 +1,76 @@ +package com.blockchain.server.user.service; + +import com.blockchain.server.user.dto.UserBaseDTO; +import com.blockchain.server.user.entity.UserMain; + +import java.util.List; + +/** + * @author huangxl + * @create 2019-02-23 14:59 + */ +public interface UserMainService { + /** + * 根据手机号查询用户信息 + * + * @param tel 手机号 + */ + UserMain selectByMobilePhone(String tel); + + /** + * 根据用户id查询用户主体信息 + * + * @param id 用户id + */ + UserMain selectById(String id); + + /** + * 根据用户id查询用户主体信息 + * + * @param ids 用户id集合 + */ + List selectInIds(String[] ids); + + /** + * 注册接口 + * + * @param tel 手机号 + * @param invitationCode 邀请码 + * @param internationalCode 国际标识 + * @param password 密码 + * @param nickName 昵称 + * @return + */ + UserMain handleRegister(String tel, String invitationCode, String internationalCode, String password, String nickName); + + /** + * 更新用户昵称 + * + * @param userId 用户id + * @param nickName 昵称 + */ + void updateNickName(String userId, String nickName); + + /** + * 更新手机号信息 + * + * @param userId 用户id + * @param tel 手机号 + * @param internationalCode 国际标识码 + */ + void updateTel(String userId, String tel, String internationalCode); + + /** + * 发送修改手机号验证码之前的业务逻辑处理 + * @param userId 当前登录的用户id + * @param tel 要更换的手机号 + */ + void handleSendChangePhoneCodeBefore(String userId, String tel); + + /** + * 查询用户主体信息 + * @param userId 用户id + * @return + */ + UserBaseDTO selectUserInfoById(String userId); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserOptLogService.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserOptLogService.java new file mode 100644 index 0000000..7583719 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserOptLogService.java @@ -0,0 +1,19 @@ +package com.blockchain.server.user.service; + +import com.blockchain.server.user.entity.UserOptLog; + +/** + * @author huangxl + * @create 2019-02-24 11:17 + */ +public interface UserOptLogService { + /** + * 保存用户操作记录 + */ + int insertUserOpt(UserOptLog userOptLog); + + /** + * 保存用户操作记录 + */ + int saveUserOptLog(String userId, String optType, String content); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserRelationService.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserRelationService.java new file mode 100644 index 0000000..6b71a6d --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserRelationService.java @@ -0,0 +1,27 @@ +package com.blockchain.server.user.service; + + +import com.blockchain.server.user.entity.UserRelation; + +public interface UserRelationService { + /** + * 插入用户的推荐关系 + * @param userId + * @param invitationCode + */ + int insertRelationChain(String userId, String invitationCode); + + /** + * 初始化用户关系链信息(无推荐关系) + * @param userId 用户id + */ + int insertRelationChainByNotInvited(String userId); + + /** + * 根据用户ID,查询用户关系表 + * + * @param userId 用户关系表 + * @return + */ + UserRelation findByUserId(String userId); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserService.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserService.java new file mode 100644 index 0000000..53370b1 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/UserService.java @@ -0,0 +1,22 @@ +package com.blockchain.server.user.service; + +public interface UserService { + + /*** + * 检查用户高级认证和黑名单 + * 不通过认证或存在黑名单则抛出异常 + * + * @param userId + * @return + */ + void hasHighAuthAndUserList(String userId); + + /*** + * 检查用户初级认证和黑名单 + * 不通过认证或存在黑名单则抛出异常 + * + * @param userId + * @return + */ + void hasLowAuthAndUserList(String userId); +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/AuthenticationApplyLogServiceImpl.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/AuthenticationApplyLogServiceImpl.java new file mode 100644 index 0000000..e8c4376 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/AuthenticationApplyLogServiceImpl.java @@ -0,0 +1,26 @@ +package com.blockchain.server.user.service.impl; + +import com.blockchain.server.user.entity.AuthenticationApplyLog; +import com.blockchain.server.user.mapper.AuthenticationApplyLogMapper; +import com.blockchain.server.user.service.AuthenticationApplyLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class AuthenticationApplyLogServiceImpl implements AuthenticationApplyLogService { + + + @Autowired + private AuthenticationApplyLogMapper authenticationApplyLogMapper; + + + @Override + public Integer insert(AuthenticationApplyLog authenticationApplyLog) { + return authenticationApplyLogMapper.insert(authenticationApplyLog); + } + + @Override + public AuthenticationApplyLog selectRemarkByUserId(String type, String userId) { + return authenticationApplyLogMapper.selectRemarkByUserId(type,userId); + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/AuthenticationApplyServiceImpl.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/AuthenticationApplyServiceImpl.java new file mode 100644 index 0000000..08447c2 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/AuthenticationApplyServiceImpl.java @@ -0,0 +1,146 @@ +package com.blockchain.server.user.service.impl; + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.user.common.constants.sql.AuthenticationApplyConstant; +import com.blockchain.server.user.common.constants.sql.UserAuthenticationConstant; +import com.blockchain.server.user.common.constants.sql.UserInfoConstant; +import com.blockchain.server.user.common.enums.UserEnums; +import com.blockchain.server.user.common.exceprion.UserException; +import com.blockchain.server.user.common.utils.FileUploadHelper; +import com.blockchain.server.user.entity.AuthenticationApply; +import com.blockchain.server.user.entity.AuthenticationApplyLog; +import com.blockchain.server.user.entity.HighAuthenticationApply; +import com.blockchain.server.user.entity.UserInfo; +import com.blockchain.server.user.mapper.AuthenticationApplyMapper; +import com.blockchain.server.user.mapper.HighAuthenticationApplyMapper; +import com.blockchain.server.user.service.AuthenticationApplyLogService; +import com.blockchain.server.user.service.AuthenticationApplyService; +import com.blockchain.server.user.service.UserInfoService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.UUID; + +/** + * @author huangxl + * @create 2019-02-26 14:26 + */ +@Service +public class AuthenticationApplyServiceImpl implements AuthenticationApplyService { + @Autowired + private UserInfoService userInfoService; + @Autowired + private AuthenticationApplyMapper authenticationApplyMapper; + @Autowired + private HighAuthenticationApplyMapper highAuthenticationApplyMapper; + @Autowired + private AuthenticationApplyLogService authenticationApplyLogService; + + + @Override + public String selectHighRemarkByUserId(String userId) { + AuthenticationApplyLog authenticationApplyLog = authenticationApplyLogService.selectRemarkByUserId(UserAuthenticationConstant.HIGH,userId); + return authenticationApplyLog!=null?authenticationApplyLog.getRemark():null; + } + + @Override + public String selectLowRemarkByUserId(String userId) { + AuthenticationApplyLog authenticationApplyLog = authenticationApplyLogService.selectRemarkByUserId(UserAuthenticationConstant.LOW,userId); + return authenticationApplyLog!=null?authenticationApplyLog.getRemark():null; + } + + @Override + @Transactional + public void insertBasicAuth(String userId, String realName, String idNumber, String type, String imgs) { + ExceptionPreconditionUtils.notEmpty(userId, realName, idNumber, imgs); + FileUploadHelper.verifyFileNameList(imgs, 2);//初级认证需要身份证正反面,手持身份证照片 + if (StringUtils.isEmpty(type)) { + type = AuthenticationApplyConstant.TYPE_DEFAULT; + } + + UserInfo userInfo = userInfoService.selectByUserId(userId); + if (userInfo.getLowAuth().equals(UserInfoConstant.STATUS_LOW_AUTH_WAIT)) { + //已经是等待审核状态 + throw new UserException(UserEnums.AUTH_WAIT); + } + if (userInfo.getLowAuth().equals(UserInfoConstant.STATUS_LOW_AUTH_YES)) { + //已经是认证状态 + throw new UserException(UserEnums.AUTH_YES); + } + + Date now = new Date(); + String[] filePath = imgs.split(","); + + + AuthenticationApply apply = new AuthenticationApply(); + apply.setId(UUID.randomUUID().toString()); + apply.setUserId(userId); + apply.setCreateTime(now); + apply.setFileUrl1(filePath[0]); + apply.setFileUrl2(filePath[1]); + // apply.setFileUrl3(filePath[2]); + apply.setIdNumber(idNumber); + apply.setRealName(realName); + apply.setType(type); + apply.setModifyTime(now); + apply.setStatus(AuthenticationApplyConstant.STATUS_WAIT); + authenticationApplyMapper.insert(apply); + + userInfo.setLowAuth(UserInfoConstant.STATUS_LOW_AUTH_WAIT); + userInfo.setModifyTime(now); + userInfoService.updateUserInfo(userInfo); + } + + @Override + @Transactional + public void insertHighAuth(String userId, String img) { + ExceptionPreconditionUtils.notEmpty(userId, img); + FileUploadHelper.verifyFileName(img); + UserInfo userInfo = userInfoService.selectByUserId(userId); + if (!userInfo.getLowAuth().equals(UserInfoConstant.STATUS_LOW_AUTH_YES)) { + //提交高级认证前必须经过初级认证 + throw new UserException(UserEnums.AUTH_BASIC_BEFORE); + } + if (userInfo.getHighAuth().equals(UserInfoConstant.STATUS_HIGHT_AUTH_WAIT)) { + //已经是等待审核状态 + throw new UserException(UserEnums.AUTH_WAIT); + } + if (userInfo.getHighAuth().equals(UserInfoConstant.STATUS_HIGHT_AUTH_YES)) { + //已经是认证状态 + throw new UserException(UserEnums.AUTH_YES); + } + Date now = new Date(); + + HighAuthenticationApply apply = new HighAuthenticationApply(); + apply.setId(UUID.randomUUID().toString()); + apply.setCreateTime(now); + apply.setFileUrl(img); + apply.setModifyTime(now); + apply.setStatus(AuthenticationApplyConstant.STATUS_WAIT); + apply.setUserId(userId); + + highAuthenticationApplyMapper.insert(apply); + + userInfo.setHighAuth(UserInfoConstant.STATUS_HIGHT_AUTH_WAIT); + userInfo.setModifyTime(now); + userInfoService.updateUserInfo(userInfo); + } + + /** + * 判断用户认证状态接口 + * @param userId + * @param authenticationType + * @return + */ + @Override + public String judgeAuthentication(String userId, String authenticationType) { + if (UserAuthenticationConstant.LOW.equalsIgnoreCase(authenticationType)) + return authenticationApplyMapper.judgeAuthentication(userId); + else if (UserAuthenticationConstant.HIGH.equalsIgnoreCase(authenticationType)) + return highAuthenticationApplyMapper.judgeAuthentication(userId); + return null; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/ConfigServiceImpl.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/ConfigServiceImpl.java new file mode 100644 index 0000000..6d8b4ce --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/ConfigServiceImpl.java @@ -0,0 +1,33 @@ +package com.blockchain.server.user.service.impl; + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.user.common.constants.sql.ConfigConstant; +import com.blockchain.server.user.entity.Config; +import com.blockchain.server.user.mapper.ConfigMapper; +import com.blockchain.server.user.service.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * @author huangxl + * @create 2019-02-25 17:50 + */ +@Service +public class ConfigServiceImpl implements ConfigService { + + @Autowired + private ConfigMapper configMapper; + + @Override + public String getValidValueByKey(String key) { + ExceptionPreconditionUtils.notEmpty(key); + Config config = new Config(); + config.setDataKey(key); + config.setDataStatus(ConfigConstant.STATUS_YES); + Config exist = configMapper.selectOne(config); + if (exist != null) { + return exist.getDataValue(); + } + return null; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/PushServiceImpl.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/PushServiceImpl.java new file mode 100644 index 0000000..04363f3 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/PushServiceImpl.java @@ -0,0 +1,38 @@ +package com.blockchain.server.user.service.impl; + +import com.blockchain.server.user.dto.PushInfoDTO; +import com.blockchain.common.base.enums.PushEnums; +import com.blockchain.server.user.common.utils.push.PushUtils; +import com.blockchain.server.user.entity.PushUser; +import com.blockchain.server.user.service.PushService; +import com.blockchain.server.user.service.PushUserService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.Map; + +@Service +public class PushServiceImpl implements PushService { + + @Autowired + private PushUtils pushUtils; + @Autowired + private PushUserService pushUserService; + + @Override + public void pushToSingle(String userId, String pushType, Map payLoadMap) { + PushUser pushUser = pushUserService.selectByUserId(userId); + //用户存在并且客户端ID不为空 + if (pushUser != null && StringUtils.isNotBlank(pushUser.getClientId())) { + //根据pushType获取推送信息枚举 + PushEnums pushEnums = PushEnums.getEnumByPushType(pushType); + //获取推送内容对象 + PushInfoDTO pushInfo = new PushInfoDTO(pushEnums, pushUser.getLanguage()); + //推送消息 + pushUtils.pushToSingle(pushUser.getClientId(), pushInfo.getPushTitle(), pushInfo.getPushContent(), payLoadMap); + } + } + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/PushUserServiceImpl.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/PushUserServiceImpl.java new file mode 100644 index 0000000..958abe5 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/PushUserServiceImpl.java @@ -0,0 +1,112 @@ +package com.blockchain.server.user.service.impl; + +import com.blockchain.server.user.entity.PushUser; +import com.blockchain.server.user.mapper.PushUserMapper; +import com.blockchain.server.user.service.PushUserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.UUID; + +@Service +public class PushUserServiceImpl implements PushUserService { + @Autowired + private PushUserMapper userMapper; + + //日志 + private static final Logger LOG = LoggerFactory.getLogger(PushUserServiceImpl.class); + + @Override + public Integer insertOrUpadteUser(String userId, String clientId, String language) { + //根据用户id查询 + PushUser pushUser = userMapper.selectByUserId(userId); + //判断客户端id是否存在 + checkClientIdExist(clientId); + //返回行数 + Integer row; + //不等于空,走更新 + if (pushUser != null) { + //更新 + row = updateUser(userId, clientId, language); + } else { + //等于空,走新增 + row = insertUser(userId, clientId, language); + } + + return row; + } + + @Override + @Transactional + public Integer insertUser(String userId, String clientId, String language) { + //新增通知用户 + PushUser user = new PushUser(); + Date now = new Date(); + user.setId(UUID.randomUUID().toString()); + user.setUserId(userId); + user.setClientId(clientId); + user.setLanguage(language); + user.setCreateTime(now); + user.setModifyTime(now); + int row = userMapper.insertSelective(user); + //判断更新行数 + if (row != 1) { + LOG.error("新增通知用户异常:user:{0},更新行数:{1}", user, row); + } + return row; + } + + @Override + @Transactional + public Integer updateUser(String userId, String clientId, String language) { + int row = userMapper.updateUser(userId, clientId, language, new Date()); + //判断更新行数 + if (row != 1) { + LOG.error("更新通知用户异常:userId:{0},clientId:{1},更新行数:{2}", userId, clientId, row); + } + return row; + } + + @Override + @Transactional + public Integer updateUserLanguage(String userId, String language) { + PushUser pushUser = userMapper.selectByUserId(userId); + if (pushUser != null) { + pushUser.setModifyTime(new Date()); + pushUser.setLanguage(language); + return userMapper.updateByPrimaryKeySelective(pushUser); + } + return 0; + } + + @Override + public PushUser selectByUserId(String userId) { + return userMapper.selectByUserId(userId); + } + + @Override + public PushUser selectByClientId(String clientId) { + return userMapper.selectByClientId(clientId); + } + + /*** + * 判断是否有重复的客户端ID + * 如果有,将之前用户的客户端ID设置为空 + * @param clientId + * @return + */ + private int checkClientIdExist(String clientId) { + //根据客户端id查询用户 + PushUser user = selectByClientId(clientId); + //客户端ID已存在,将其设置为空,让后入的用户使用该客户端ID + if (user != null) { + //更新 + return userMapper.updateUser(user.getUserId(), null, null, new Date()); + } + return 0; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/SmsCountServiceImpl.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/SmsCountServiceImpl.java new file mode 100644 index 0000000..60c718f --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/SmsCountServiceImpl.java @@ -0,0 +1,78 @@ +package com.blockchain.server.user.service.impl; + +import com.blockchain.common.base.enums.BaseResultEnums; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.server.user.common.enums.SmsCountEnum; +import com.blockchain.server.user.common.enums.UserEnums; +import com.blockchain.server.user.common.exceprion.UserException; +import com.blockchain.server.user.common.utils.CheckUtils; +import com.blockchain.server.user.common.utils.SmsCodeUtils; +import com.blockchain.server.user.entity.SmsCount; +import com.blockchain.server.user.mapper.SmsCountMapper; +import com.blockchain.server.user.service.SmsCountService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.UUID; + +@Service +public class SmsCountServiceImpl implements SmsCountService { + + @Autowired + private SmsCountMapper smsCountMapper; + @Autowired + private SmsCodeUtils smsCodeUtils; + + @Override + @Transactional + public void handleInsertSmsCode(String phone, String internationalCode,SmsCountEnum smsCountEnum) { + //校验手机号 + if (!CheckUtils.checkMobilePhone(phone)) { + throw new UserException(UserEnums.PHONE_FORMAT_ERROR); + } + //校验短信类型 + if (smsCountEnum == null) { + throw new UserException(UserEnums.VERIFY_CODE_TYPE_ERROR); + } + SmsCount smsCount = smsCountMapper.getSmsRecordByPhoneAndTypeOfDay(phone, smsCountEnum.getType(), new Date()); + if (smsCount == null) { + insertOneRecord(phone, smsCountEnum.getType());//插入一条新记录 + } else { + incrCountRecord(smsCount, smsCountEnum.getMaxCount());//获取短信次数+1 + } + smsCodeUtils.sendSmsCodeAndStoreToCache(phone, internationalCode,smsCountEnum); + } + + /** + * 插入一条新纪录 + */ + private void insertOneRecord(String phone, String type) { + SmsCount smsCount = new SmsCount(); + smsCount.setId(UUID.randomUUID().toString()); + smsCount.setPhone(phone); + smsCount.setSmsCount(1); + smsCount.setSmsType(type); + smsCount.setSmsDate(new Date()); + smsCountMapper.insert(smsCount); + } + + /** + * 获取短信次数+1 + */ + private void incrCountRecord(SmsCount smsCount, int maxCount) { + int count = smsCount.getSmsCount(); + if (count + 1 > maxCount) {//检查是否大于获取短信数量限制 + throw new UserException(UserEnums.VERIFY_CODE_OVER_COUNT); + } + //数量自增1 + int row = smsCountMapper.updateIncrCountByPhoneAndTypeInRowLock(smsCount.getPhone(), smsCount.getSmsType(), count); + if (row < 1) { + throw new BaseException(BaseResultEnums.BUSY); + } + } + + + +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserInfoServiceImpl.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserInfoServiceImpl.java new file mode 100644 index 0000000..7a2fbe6 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserInfoServiceImpl.java @@ -0,0 +1,163 @@ +package com.blockchain.server.user.service.impl; + +import com.blockchain.common.base.enums.BaseResultEnums; +import com.blockchain.common.base.exception.BaseException; +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.user.common.constants.other.RedisConstant; +import com.blockchain.server.user.common.constants.other.StringFormatConstant; +import com.blockchain.server.user.common.constants.sql.UserOptConstant; +import com.blockchain.server.user.common.enums.SmsCountEnum; +import com.blockchain.server.user.common.enums.UserEnums; +import com.blockchain.server.user.common.exceprion.UserException; +import com.blockchain.server.user.common.utils.CheckUtils; +import com.blockchain.server.user.common.utils.FileUploadHelper; +import com.blockchain.server.user.common.utils.GoogleAuthenticatorUtils; +import com.blockchain.server.user.entity.UserInfo; +import com.blockchain.server.user.mapper.UserInfoMapper; +import com.blockchain.server.user.service.UserInfoService; +import com.blockchain.server.user.service.UserOptLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; +import java.util.Random; +import java.util.UUID; + +/** + * @author huangxl + * @create 2019-02-23 18:23 + */ +@Service +public class UserInfoServiceImpl implements UserInfoService { + + @Autowired + private UserInfoMapper userInfoMapper; + @Autowired + private UserOptLogService userOptLogService; + @Autowired + private RedisTemplate redisTemplate; + + @Override + @Transactional + public void saveUser(String userId, String email, boolean hasRelation) { + ExceptionPreconditionUtils.notEmpty(userId); + UserInfo userInfo = new UserInfo(); + userInfo.setId(UUID.randomUUID().toString()); + userInfo.setUserId(userId); + if (CheckUtils.checkEmail(email)){ + userInfo.setEmail(email); + + } + Date now = new Date(); + userInfo.setCreateTime(now); + userInfo.setModifyTime(now); + if (hasRelation) { + int number = 10 + new Random().nextInt(89);//保证一定生成两位数的数值 + userInfo.setRandomNumber(number); + } + userInfoMapper.insertSelective(userInfo); + } + + @Override + public UserInfo selectByIncrCode(Integer incrCode) { + if (incrCode == null) { + throw new BaseException(BaseResultEnums.PARAMS_ERROR); + } + UserInfo userInfo = new UserInfo(); + userInfo.setIncrCode(incrCode); + UserInfo user = userInfoMapper.selectOne(userInfo); + return user; + } + + @Override + public void handleBindGoogleAuthenticator(String userId, String key, Long code) { + ExceptionPreconditionUtils.notEmpty(userId, key, code); + boolean pass = GoogleAuthenticatorUtils.check_code(key, code, System.currentTimeMillis()); + if (!pass) { + throw new UserException(UserEnums.GOOGLE_AUTH_FAIL);//校验失败 + } + //查询用户信息 + UserInfo userInfo = userInfoMapper.selectByUserId(userId); + if (userInfo.getGoogleAuth() != null) { + throw new UserException(UserEnums.GOOGLE_SECRET_KEY_EXIST);//重复绑定 + } + //将安全码设置到用户信息中 + userInfo.setGoogleAuth(key); + userInfo.setModifyTime(new Date()); + userInfoMapper.updateByPrimaryKey(userInfo); + + //保存用户操作信息 + userOptLogService.saveUserOptLog(userId, UserOptConstant.TYPE_BIND_GOOGLE_AUTHENTICATOR, StringFormatConstant.getUserBindGoogleAuthenticator(userId)); + } + + @Override + public UserInfo selectByUserId(String userId) { + UserInfo userInfo = new UserInfo(); + userInfo.setUserId(userId); + UserInfo user = userInfoMapper.selectOne(userInfo); + return user; + } + + @Override + public void updateUserInfo(UserInfo userInfo) { + userInfoMapper.updateByPrimaryKeySelective(userInfo); + } + + @Override + @Transactional + public void handleBindEmail(String userId, String email, String code) { + ExceptionPreconditionUtils.notEmpty(userId, email, code); + boolean check = CheckUtils.checkEmail(email); + if (!check) { + throw new UserException(UserEnums.EMAIL_FORMAT_ERROR); + } + String key = RedisConstant.getSmsHashKey(SmsCountEnum.SMS_COUNT_BIND.getKey(),email); + Object cache = redisTemplate.opsForValue().get(key); + if (cache != null && cache.toString().equals(code)) { + //检查邮箱是否已被绑定 + checkRepeatEmail(email); + + UserInfo userInfo = selectByUserId(userId); + if (userInfo.getEmail() != null) { + throw new UserException(UserEnums.EMAIL_BIND_REPEAT); + } + userInfo.setEmail(email); + userInfo.setModifyTime(new Date()); + userInfoMapper.updateByPrimaryKey(userInfo);//绑定邮箱 + + //保存用户操作信息 + userOptLogService.saveUserOptLog(userId, UserOptConstant.TYPE_BIND_EMAIL, StringFormatConstant.getUserBindEmail(userId)); + + redisTemplate.delete(key); + } else { + throw new UserException(UserEnums.EMAIL_VERIFY_FAIL); + } + } + + @Override + @Transactional + public void updateUserHeadImg(String userId, String img) { + ExceptionPreconditionUtils.notEmpty(userId, img); + FileUploadHelper.verifyFileName(img); + + UserInfo userInfo = userInfoMapper.selectByUserId(userId); + userInfo.setAvatar(img); + userInfo.setModifyTime(new Date()); + + userInfoMapper.updateByPrimaryKeySelective(userInfo); + } + + @Override + public void checkRepeatEmail(String email) { + //校验邮箱是否已被绑定 + UserInfo exist = new UserInfo(); + exist.setEmail(email); + List existUser = userInfoMapper.select(exist); + if (existUser != null && existUser.size() > 0) { + throw new UserException(UserEnums.EMAIL_EXIST); + } + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserListServiceImpl.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserListServiceImpl.java new file mode 100644 index 0000000..accf851 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserListServiceImpl.java @@ -0,0 +1,28 @@ +package com.blockchain.server.user.service.impl; + +import com.blockchain.server.user.entity.UserList; +import com.blockchain.server.user.mapper.UserListMapper; +import com.blockchain.server.user.service.UserListService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + + +/** + * @author huangxl + * @create 2019-02-23 15:22 + */ +@Service +public class UserListServiceImpl implements UserListService { + @Autowired + private UserListMapper userListMapper; + + @Override + public boolean checkUserByUserIdAndType(String listType, String userId, String type) { + UserList userList = new UserList(); + userList.setListType(listType); + userList.setType(type); + userList.setUserId(userId); + int record = userListMapper.selectCount(userList); + return record > 0; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserLoginLoginServiceImpl.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserLoginLoginServiceImpl.java new file mode 100644 index 0000000..f6b558c --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserLoginLoginServiceImpl.java @@ -0,0 +1,51 @@ +package com.blockchain.server.user.service.impl; + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.user.dto.UserLoginLogDto; +import com.blockchain.server.user.entity.UserLoginLog; +import com.blockchain.server.user.mapper.UserLoginLogMapper; +import com.blockchain.server.user.service.UserLoginLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; +import java.util.UUID; + +/** + * @author huangxl + * @create 2019-02-23 14:43 + */ +@Service +public class UserLoginLoginServiceImpl implements UserLoginLogService { + + @Autowired + private UserLoginLogMapper userLoginLogMapper; + @Override + //使用非事务提交方式,防止登录失败跑出异常事务回滚后不插入登录失败记录 + @Transactional(propagation = Propagation.NOT_SUPPORTED) + public int saveLoginLog(String userId,String status) { + UserLoginLog loinLog = new UserLoginLog(); + String ip = HttpRequestUtil.getIpAddr(); + loinLog.setId(UUID.randomUUID().toString()); + loinLog.setUserId(userId); + loinLog.setStatus(status); + loinLog.setCreateTime(new Date()); + loinLog.setIpAddress(ip); + return userLoginLogMapper.insertSelective(loinLog); + } + + /** + * 查询用户登录日志 + * @param userId + * @return + */ + @Override + public List listUserLoginLogByUserId(String userId) { + ExceptionPreconditionUtils.notEmpty(userId); + return userLoginLogMapper.listUserLoginLogByUserId(userId); + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserLoginServiceImpl.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserLoginServiceImpl.java new file mode 100644 index 0000000..078d364 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserLoginServiceImpl.java @@ -0,0 +1,163 @@ +package com.blockchain.server.user.service.impl; + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.common.base.util.MD5Utils; +import com.blockchain.server.user.common.constants.other.StringFormatConstant; +import com.blockchain.server.user.common.constants.sql.UserListConstant; +import com.blockchain.server.user.common.constants.sql.UserLoginLogConstant; +import com.blockchain.server.user.common.constants.sql.UserOptConstant; +import com.blockchain.server.user.common.enums.UserEnums; +import com.blockchain.server.user.common.exceprion.UserException; +import com.blockchain.server.user.common.utils.CheckUtils; +import com.blockchain.server.user.entity.UserLogin; +import com.blockchain.server.user.entity.UserMain; +import com.blockchain.server.user.entity.UserOptLog; +import com.blockchain.server.user.mapper.UserLoginMapper; +import com.blockchain.server.user.service.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.UUID; + +/** + * @author huangxl + * @data 2019/2/21 15:18 + */ +@Service +public class UserLoginServiceImpl implements UserLoginService { + @Autowired + private UserLoginMapper userLoginMapper; + @Autowired + private UserLoginLogService userLoginLogService; + @Autowired + private UserListService userListService; + @Autowired + private UserOptLogService userOptLogService; + @Autowired + private UserMainService userMainService; + + + @Override + public UserMain handleLoginByPassword(String tel, String password) { + ExceptionPreconditionUtils.notEmpty(tel, password); + //校验用户是否有权限登录 + UserMain userMain = checkUserLoginPermission(tel, password); + return userMain; + } + + @Override + public int insertEntity(String userId, String password) { + UserLogin userLogin = new UserLogin(); + userLogin.setId(UUID.randomUUID().toString()); + userLogin.setUserId(userId); + if (password != null && password.trim().length() > 0) { + password = password.trim(); + if (!CheckUtils.checkPassword(password)) { + throw new UserException(UserEnums.USER_PASSWORD_ERROR_FORMAT); + } + userLogin.setPassword(MD5Utils.MD5(password)); + } + userLogin.setCreateTime(new Date()); + userLogin.setModifyTime(new Date()); + return userLoginMapper.insert(userLogin); + } + + @Override + public UserMain handleLoginByPhoneCode(String tel) { + ExceptionPreconditionUtils.notEmpty(tel); + return checkUserLoginPermission(tel, null);//检查是否有登录权限 + } + + @Override + @Transactional + public void insertPassword(String userId, String password) { + ExceptionPreconditionUtils.notEmpty(userId, password); + UserLogin userLogin = userLoginMapper.selectByUserId(userId); + if (userLogin.getPassword() != null) { + throw new UserException(UserEnums.PASSWORD_EXIST); + } + //验证校验码 + userLogin.setPassword(MD5Utils.MD5(password)); + userLogin.setModifyTime(new Date()); +// userLoginMapper.insert(userLogin); + userLoginMapper.updateByPrimaryKey(userLogin); + + //保存用户操作信息 + userOptLogService.saveUserOptLog(userId, UserOptConstant.TYPE_SET_LOGIN_PASSWORD, StringFormatConstant.getUserBindLoginPassword(userId)); + } + + @Override + public void updatePassword(String userId, String password, String oldPassword) { + ExceptionPreconditionUtils.notEmpty(userId, password, oldPassword); + UserLogin userLogin = userLoginMapper.selectByUserId(userId); + if (userLogin.getPassword() != null && !userLogin.getPassword().equals(MD5Utils.MD5(oldPassword))) { + //如果已设置密码并且旧密码不匹配 + throw new UserException(UserEnums.PASSWORD_NOT_MATCH); + } + Date now = new Date(); + userLogin.setPassword(MD5Utils.MD5(password)); + userLogin.setModifyTime(now); + userLoginMapper.updateByPrimaryKey(userLogin); + + //保存用户操作信息 + userOptLogService.saveUserOptLog(userId, UserOptConstant.TYPE_CHANGE_LOGIN_PASSWORD, StringFormatConstant.getUserChangeLoginPassword(userId)); + + } + + @Override + public void updatePassword(String userId, String password) { + ExceptionPreconditionUtils.notEmpty(userId, password); + UserLogin userLogin = userLoginMapper.selectByUserId(userId); + Date now = new Date(); + userLogin.setPassword(MD5Utils.MD5(password)); + userLogin.setModifyTime(now); + userLoginMapper.updateByPrimaryKey(userLogin); + + //保存用户操作信息 + userOptLogService.saveUserOptLog(userId, UserOptConstant.TYPE_CHANGE_LOGIN_PASSWORD, StringFormatConstant.getUserChangeLoginPassword(userId)); + } + + @Override + public boolean selectUserPasswordIsExist(String userId) { + ExceptionPreconditionUtils.notEmpty(userId); + UserLogin userLogin = userLoginMapper.selectByUserId(userId); + return userLogin != null && userLogin.getPassword() != null; + } + + /** + * 检查用户是否有登录权限 + * 如果设置了登录失败限制,则当天无法登录 + * 如果密码不为空,则会校验密码的一致性 + * + * @param tel 手机号 + * @param password 密码,如果密码不为空,会校验密码的一致性 + * @return 用户信息 + */ + private UserMain checkUserLoginPermission(String tel, String password) { + UserMain userMain = userMainService.selectByMobilePhone(tel); + if (userMain == null || userMain.getId() == null) { + throw new UserException(UserEnums.USER_NOT_EXISTS); + } + String userId = userMain.getId(); + //黑名单校验 + boolean blackRecord = userListService.checkUserByUserIdAndType(UserListConstant.LIST_TYPE_BLACK, userId, UserListConstant.TYPE_BAN_LOGIN); + if (blackRecord) { + throw new UserException(UserEnums.LOGIN_FORBIDDEN); + } + + if (password != null) { + //校验密码的一致性 + UserLogin userLogin = userLoginMapper.selectByUserId(userId); + String pw = userLogin.getPassword(); + if (!MD5Utils.MD5(password).equals(pw)) { + userLoginLogService.saveLoginLog(userId, UserLoginLogConstant.STATUS_FAIL);//保存登录失败信息 + throw new UserException(UserEnums.LOGIN_PASSWORD_ERROR); + } + } + userLoginLogService.saveLoginLog(userId, UserLoginLogConstant.STATUS_SUCCESS);//保存登录信息 + return userMain; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserMainServiceImpl.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserMainServiceImpl.java new file mode 100644 index 0000000..6642caa --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserMainServiceImpl.java @@ -0,0 +1,204 @@ +package com.blockchain.server.user.service.impl; + +import com.blockchain.common.base.constant.BaseConstant; +import com.blockchain.common.base.dto.ResultDTO; +import com.blockchain.common.base.dto.SessionUserDTO; +import com.blockchain.common.base.exception.RPCException; +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.common.base.util.SSOHelper; +import com.blockchain.server.user.common.constants.other.InternationalConstant; +import com.blockchain.server.user.common.constants.other.StringFormatConstant; +import com.blockchain.server.user.common.constants.sql.UserMainConstant; +import com.blockchain.server.user.common.constants.sql.UserOptConstant; +import com.blockchain.server.user.common.enums.UserEnums; +import com.blockchain.server.user.common.exceprion.UserException; +import com.blockchain.server.user.common.utils.CommonUtils; +import com.blockchain.server.user.dto.UserBaseDTO; +import com.blockchain.server.user.entity.UserInfo; +import com.blockchain.server.user.entity.UserMain; +import com.blockchain.server.user.entity.UserOptLog; +import com.blockchain.server.user.feign.BtcFeign; +import com.blockchain.server.user.feign.EosFeign; +import com.blockchain.server.user.feign.EthFeign; +import com.blockchain.server.user.mapper.UserMainMapper; +import com.blockchain.server.user.service.*; +import com.codingapi.tx.annotation.TxTransaction; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; +import java.util.UUID; + +/** + * @author huangxl + * @create 2019-02-23 14:59 + */ +@Service +public class UserMainServiceImpl implements UserMainService { + + @Autowired + private UserMainMapper userMainMapper; + @Autowired + private UserLoginService userLoginService; + @Autowired + private UserRelationService userRelationService; + @Autowired + private UserOptLogService userOptLogService; + @Autowired + private UserInfoService userInfoService; + + @Autowired + private EthFeign ethFeign; + @Autowired + private EosFeign eosFeign; + @Autowired + private BtcFeign btcFeign; + + + @Override + public UserMain selectByMobilePhone(String tel) { + ExceptionPreconditionUtils.notEmpty(tel); + return userMainMapper.selectByMobilePhone(tel); + } + + @Override + public UserMain selectById(String id) { + ExceptionPreconditionUtils.notEmpty(id); + UserMain userMain = userMainMapper.selectByPrimaryKey(id); + return userMain; + } + + @Override + public List selectInIds(String[] ids) { + List users = userMainMapper.listByIds(ids); + return users; + } + + @Override + @Transactional + @TxTransaction(isStart = true) + public UserMain handleRegister(String tel, String invitationCode, String internationalCode, String password, String nickName) { + ExceptionPreconditionUtils.notEmpty(tel, internationalCode);//校验信息 + UserMain userMain = selectByMobilePhone(tel); + if (userMain != null) { + throw new UserException(UserEnums.USER_PHONE_EXISTS);//已存在该手机号信息 + } + String international = InternationalConstant.getInternational(internationalCode); + if (international == null) { + throw new UserException(UserEnums.CANNOT_FOUND_INTERNATIONAL);//通过国际编码找不到国际名称 + } + String userId = UUID.randomUUID().toString(); + UserMain user = insertUserMain(tel, internationalCode, userId, international, nickName);//插入主体数据 + userLoginService.insertEntity(userId, password);//插入密码信息 + int insertRow = userRelationService.insertRelationChain(userId, invitationCode);//插入推荐关系 + userInfoService.saveUser(userId,tel, insertRow > 0);//插入用户其他信息 + + ResultDTO ethResult = ethFeign.initWallets(userId); + if (ethResult.getCode() != BaseConstant.REQUEST_SUCCESS) { + throw new RPCException(ethResult.getCode(), ethResult.getMsg()); + } + ResultDTO eosResult = eosFeign.initEosWallet(userId); + if (eosResult.getCode() != BaseConstant.REQUEST_SUCCESS) { + throw new RPCException(eosResult.getCode(), eosResult.getMsg()); + } + ResultDTO btcResult = btcFeign.createWallet(userId); + if (btcResult.getCode() != BaseConstant.REQUEST_SUCCESS) { + throw new RPCException(btcResult.getCode(), btcResult.getMsg()); + } + return user; + } + + @Override + @Transactional + public void updateNickName(String userId, String nickName) { + ExceptionPreconditionUtils.notEmpty(userId, nickName); + UserMain userMain = userMainMapper.selectByPrimaryKey(userId); + if (nickName.contains("<") || nickName.contains(">")) { + throw new UserException(UserEnums.INVALID_NICK_NAME); + } + userMain.setNickName(nickName); + userMain.setModifyTime(new Date()); + userMainMapper.updateByPrimaryKey(userMain); + } + + @Override + @Transactional + public void updateTel(String userId, String tel, String internationalCode) { + ExceptionPreconditionUtils.notEmpty(userId, tel, internationalCode); + //查询是否已存在该手机号 + UserMain existUser = selectByMobilePhone(tel); + if (existUser != null) { + throw new UserException(UserEnums.USER_PHONE_EXISTS);//已存在该手机号信息 + } + UserMain userMain = userMainMapper.selectByPrimaryKey(userId); + Date now = new Date(); + //插入修改记录 + String content = StringFormatConstant.getUserChangeAccountFormat(userId, + userMain.getMobilePhone(), tel, + userMain.getInternationalCode(), internationalCode); + userOptLogService.saveUserOptLog(userId, UserOptConstant.TYPE_CHANGE_TEL, content); + + //更改手机号 + userMain.setInternationalCode(internationalCode); + + if (userMain.getMobilePhone().equals(userMain.getNickName())) {//如果手机号和昵称一致,同时修改昵称 + userMain.setNickName(tel); + } + userMain.setMobilePhone(tel); + userMain.setModifyTime(now); + userMainMapper.updateByPrimaryKey(userMain); + } + + @Override + public void handleSendChangePhoneCodeBefore(String userId, String tel) { + ExceptionPreconditionUtils.notEmpty(userId, tel); + UserMain me = selectById(userId); + if (me.getMobilePhone().equals(tel.trim())) { + throw new UserException(UserEnums.PHONE_NOT_CHANGE); + } + UserMain userMain = selectByMobilePhone(tel); + if (userMain != null) { + throw new UserException(UserEnums.USER_PHONE_EXISTS); + } + } + + @Override + public UserBaseDTO selectUserInfoById(String userId) { + UserMain userMain = userMainMapper.selectByPrimaryKey(userId); + UserInfo userInfo = userInfoService.selectByUserId(userId); + boolean hasPassword = userLoginService.selectUserPasswordIsExist(userId); + + UserBaseDTO userBaseDTO = new UserBaseDTO(); +// BeanUtils.copyProperties(userMain, userBaseDTO); +// BeanUtils.copyProperties(userInfo, userBaseDTO); + BeanUtils.copyProperties(userInfo, userBaseDTO); + BeanUtils.copyProperties(userMain, userBaseDTO); + + userBaseDTO.setHasPassword(hasPassword); + userBaseDTO.setBindGoogleAuth(userInfo.getGoogleAuth() != null); + userBaseDTO.setInvitationCode(CommonUtils.generateInvitationCode(userInfo.getIncrCode(), userInfo.getRandomNumber())); + return userBaseDTO; + } + + private UserMain insertUserMain(String tel, String internationalCode, String userId, String international, String nickName) { + UserMain user = new UserMain(); + user.setId(userId); + if (StringUtils.isEmpty(internationalCode)) { + internationalCode = UserMainConstant.INTERNATIONAL_CODE_DEFAULT; + } + if (StringUtils.isEmpty(nickName)) nickName = tel; + user.setInternationalCode(internationalCode); + user.setInternational(international); + user.setMobilePhone(tel); + user.setNickName(nickName); + user.setCreateTime(new Date()); + user.setModifyTime(new Date()); + userMainMapper.insert(user); + return user; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserOptLogServiceImpl.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserOptLogServiceImpl.java new file mode 100644 index 0000000..98a4c8c --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserOptLogServiceImpl.java @@ -0,0 +1,40 @@ +package com.blockchain.server.user.service.impl; + +import com.blockchain.common.base.util.HttpRequestUtil; +import com.blockchain.server.user.entity.UserOptLog; +import com.blockchain.server.user.mapper.UserOptLogMapper; +import com.blockchain.server.user.service.UserOptLogService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.UUID; + +/** + * @author huangxl + * @create 2019-02-24 11:18 + */ +@Service +public class UserOptLogServiceImpl implements UserOptLogService { + @Autowired + private UserOptLogMapper userOptLogMapper; + + @Override + @Transactional + public int insertUserOpt(UserOptLog userOptLog) { + return userOptLogMapper.insert(userOptLog); + } + + @Override + public int saveUserOptLog(String userId, String optType, String content) { + UserOptLog userOptLog = new UserOptLog(); + userOptLog.setId(UUID.randomUUID().toString()); + userOptLog.setUserId(userId); + userOptLog.setIpAddress(HttpRequestUtil.getIpAddr()); + userOptLog.setCreateTime(new Date()); + userOptLog.setOptType(optType); + userOptLog.setContent(content); + return userOptLogMapper.insert(userOptLog); + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserRelationServiceImpl.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserRelationServiceImpl.java new file mode 100644 index 0000000..747d0b5 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserRelationServiceImpl.java @@ -0,0 +1,78 @@ +package com.blockchain.server.user.service.impl; + +import com.blockchain.common.base.util.ExceptionPreconditionUtils; +import com.blockchain.server.user.common.enums.UserEnums; +import com.blockchain.server.user.common.exceprion.UserException; +import com.blockchain.server.user.common.utils.CommonUtils; +import com.blockchain.server.user.entity.UserInfo; +import com.blockchain.server.user.entity.UserRelation; +import com.blockchain.server.user.mapper.UserRelationMapper; +import com.blockchain.server.user.service.UserInfoService; +import com.blockchain.server.user.service.UserRelationService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.UUID; + +@Service +public class UserRelationServiceImpl implements UserRelationService { + + @Autowired + private UserRelationMapper userRelationMapper; + @Autowired + private UserInfoService userInfoService; + + @Override + @Transactional + public int insertRelationChain(String userId, String invitationCode) { + ExceptionPreconditionUtils.notEmpty(userId); + UserRelation userRelation = new UserRelation(); + //如果邀请码不为空,插入用户关系链 + if (StringUtils.isEmpty(invitationCode)) { + userRelation.setPid(null); + userRelation.setRelationChain(userId); + userRelation.setTreeDepth(0); + } else { + Integer incrCode = CommonUtils.getUserIncrCodeFromInvitationCode(invitationCode);//解析出用户自增长码 + if (incrCode == null) { + throw new UserException(UserEnums.INVALID_INVITATION_CODE); + } + UserInfo userInfo = userInfoService.selectByIncrCode(incrCode); + if (userInfo == null) { + throw new UserException(UserEnums.INVALID_INVITATION_CODE); + } + //拿到父级关系链 + UserRelation parent = findByUserId(userInfo.getUserId()); + userRelation.setPid(parent.getUserId()); + userRelation.setRelationChain(parent.getRelationChain() + "," + userId); + userRelation.setTreeDepth(parent.getTreeDepth() + 1); + } + + String id = UUID.randomUUID().toString(); + userRelation.setId(id); + userRelation.setUserId(userId); + userRelation.setCreateTime(new Date()); + return userRelationMapper.insertSelective(userRelation); + } + + @Override + public int insertRelationChainByNotInvited(String userId) { + UserRelation userRelation = new UserRelation(); + String id = UUID.randomUUID().toString(); + userRelation.setId(id); + userRelation.setUserId(userId); + userRelation.setRelationChain( userId); + userRelation.setCreateTime(new Date()); + return userRelationMapper.insert(userRelation); + } + + @Override + public UserRelation findByUserId(String userId) { + UserRelation where = new UserRelation(); + where.setUserId(userId); + return userRelationMapper.selectOne(where); + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserServiceImpl.java b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..d0d017b --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/java/com/blockchain/server/user/service/impl/UserServiceImpl.java @@ -0,0 +1,85 @@ +package com.blockchain.server.user.service.impl; + +import com.blockchain.server.user.common.constants.sql.GlobalConstant; +import com.blockchain.server.user.common.constants.sql.UserListConstant; +import com.blockchain.server.user.common.enums.UserEnums; +import com.blockchain.server.user.common.exceprion.UserException; +import com.blockchain.server.user.entity.UserInfo; +import com.blockchain.server.user.service.UserInfoService; +import com.blockchain.server.user.service.UserListService; +import com.blockchain.server.user.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class UserServiceImpl implements UserService { + + @Autowired + private UserInfoService userInfoService; + @Autowired + private UserListService userListService; + + @Override + public void hasHighAuthAndUserList(String userId) { + //查询用户信息 + UserInfo userInfo = selectByUserIdAndCheckNull(userId); + + //高级认证状态 + boolean hasHighAuth = false; + //是否已通过高级认证 + if (userInfo.getHighAuth().equals(GlobalConstant.STATUS_YES)) { + hasHighAuth = true; + } + + //用户是否在禁止交易黑名单中 + boolean banTransaction = userListService.checkUserByUserIdAndType(UserListConstant.LIST_TYPE_BLACK, userId, UserListConstant.TYPE_BAN_TRANSACTION); + + //没通过高级认证 + if (!hasHighAuth) { + throw new UserException(UserEnums.TRANSACTION_NOT_PASS_HIGH_AUTH); + } + //黑名单禁止交易 + if (banTransaction) { + throw new UserException(UserEnums.TRANSACTION_FORBIDDEN); + } + } + + @Override + public void hasLowAuthAndUserList(String userId) { + //查询用户信息 + UserInfo userInfo = selectByUserIdAndCheckNull(userId); + + //高级认证状态 + boolean hasLowAuth = false; + //是否已通过高级认证 + if (userInfo.getLowAuth().equals(GlobalConstant.STATUS_YES)) { + hasLowAuth = true; + } + + //用户是否在禁止交易黑名单中 + boolean banTransaction = userListService.checkUserByUserIdAndType(UserListConstant.LIST_TYPE_BLACK, userId, UserListConstant.TYPE_BAN_TRANSACTION); + + //没通过高级认证 + if (!hasLowAuth) { + throw new UserException(UserEnums.TRANSACTION_NOT_PASS_LOW_AUTH); + } + //黑名单禁止交易 + if (banTransaction) { + throw new UserException(UserEnums.TRANSACTION_FORBIDDEN); + } + } + + /*** + * 查询用户信息并判空 + * @param userId + * @return + */ + private UserInfo selectByUserIdAndCheckNull(String userId) { + //查询用户信息 + UserInfo userInfo = userInfoService.selectByUserId(userId); + if (userInfo == null) { + throw new UserException(UserEnums.USER_NOT_EXISTS); + } + return userInfo; + } +} diff --git a/blockchain-server/blockchain-server-user/src/main/resources/application.yml b/blockchain-server/blockchain-server-user/src/main/resources/application.yml new file mode 100644 index 0000000..de0b943 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/application.yml @@ -0,0 +1,4 @@ +#日志配置路径 +logging: + config: classpath:logback/logback-${spring.cloud.config.profile}.xml + diff --git a/blockchain-server/blockchain-server-user/src/main/resources/bootstrap.yml b/blockchain-server/blockchain-server-user/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..8429233 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/bootstrap.yml @@ -0,0 +1,33 @@ +server: + port: 8101 +#注册中心 +eureka: + client: + service-url: + defaultZone: http://eureka:8001/eureka/ + instance: + prefer-ip-address: true + instance-id: ${spring.cloud.client.ip-address}:${server.port} + hostname: ${spring.cloud.client.ip-address} +spring: + cloud: + #配置中心 + config: + discovery: + service-id: dapp-config-server + enabled: true + profile: dev + name: springconf,springcloudconf,redisconf,dbconf,txconf,xssconf,fileconf,smsconf,emailconf,ipconf,pushconf + application: + name: dapp-user-server + mail: + host: smtp.qq.com + username: 1916467696@qq.com + password: smwjogdszbmlbcbj + properties: + mail: + smtp: + auth: true + starttls: + enable: true + required: true diff --git a/blockchain-server/blockchain-server-user/src/main/resources/logback/logback-dev.xml b/blockchain-server/blockchain-server-user/src/main/resources/logback/logback-dev.xml new file mode 100644 index 0000000..9a0e324 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/logback/logback-dev.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/resources/logback/logback-pro.xml b/blockchain-server/blockchain-server-user/src/main/resources/logback/logback-pro.xml new file mode 100644 index 0000000..5b55059 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/logback/logback-pro.xml @@ -0,0 +1,54 @@ + + + + + + + + + ${log.pattern} + + + + + ${log.path}/sys-info.log + + INFO + ACCEPT + DENY + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + ERROR + ACCEPT + DENY + + ${log.path}/sys-error.log + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + 60 + + + ${log.pattern} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/resources/mapper/AuthenticationApplyLogMapper.xml b/blockchain-server/blockchain-server-user/src/main/resources/mapper/AuthenticationApplyLogMapper.xml new file mode 100644 index 0000000..52d5257 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/mapper/AuthenticationApplyLogMapper.xml @@ -0,0 +1,19 @@ + + + + pc_u_authentication_apply_log + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/resources/mapper/AuthenticationApplyMapper.xml b/blockchain-server/blockchain-server-user/src/main/resources/mapper/AuthenticationApplyMapper.xml new file mode 100644 index 0000000..56596d6 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/mapper/AuthenticationApplyMapper.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + app_u_authentication_apply + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/resources/mapper/ConfigMapper.xml b/blockchain-server/blockchain-server-user/src/main/resources/mapper/ConfigMapper.xml new file mode 100644 index 0000000..a4c1c6a --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/mapper/ConfigMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + dapp_u_config + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/resources/mapper/HighAuthenticationApplyMapper.xml b/blockchain-server/blockchain-server-user/src/main/resources/mapper/HighAuthenticationApplyMapper.xml new file mode 100644 index 0000000..bf7832c --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/mapper/HighAuthenticationApplyMapper.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + dapp_u_high_authentication_apply + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/resources/mapper/PushUserMapper.xml b/blockchain-server/blockchain-server-user/src/main/resources/mapper/PushUserMapper.xml new file mode 100644 index 0000000..3cbced7 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/mapper/PushUserMapper.xml @@ -0,0 +1,43 @@ + + + + + push_user + + + + + + + + + + + + + + + + + UPDATE + + SET + client_id = #{clientId}, + + language = #{language}, + + modify_time = #{modifyTime} + WHERE user_id = #{userId} + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/resources/mapper/SmsCountMapper.xml b/blockchain-server/blockchain-server-user/src/main/resources/mapper/SmsCountMapper.xml new file mode 100644 index 0000000..5eadff7 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/mapper/SmsCountMapper.xml @@ -0,0 +1,28 @@ + + + + + id + + dapp_u_sms_count + + + + + UPDATE + + SET sms_count = sms_count + 1 + WHERE + phone = #{phone} + AND sms_type = #{smsType} + AND sms_count = #{count} + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserAuthenticationMapper.xml b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserAuthenticationMapper.xml new file mode 100644 index 0000000..6e17e5b --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserAuthenticationMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserInfoMapper.xml b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserInfoMapper.xml new file mode 100644 index 0000000..51b2ce7 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserInfoMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + dapp_u_user_info + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserListMapper.xml b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserListMapper.xml new file mode 100644 index 0000000..b5fbf4e --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserListMapper.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserLoginMapper.xml b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserLoginMapper.xml new file mode 100644 index 0000000..c25ae71 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserLoginMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + dapp_u_user_login + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserLoinLogMapper.xml b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserLoinLogMapper.xml new file mode 100644 index 0000000..6e1ace2 --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserLoinLogMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + dapp_u_user_login_log + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserMainMapper.xml b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserMainMapper.xml new file mode 100644 index 0000000..bb5373f --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserMainMapper.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + dapp_u_user_main + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserOptLogMapper.xml b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserOptLogMapper.xml new file mode 100644 index 0000000..7bae34d --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserOptLogMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserRelationMapper.xml b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserRelationMapper.xml new file mode 100644 index 0000000..d2f1e5f --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/main/resources/mapper/UserRelationMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/blockchain-server/blockchain-server-user/src/test/java/MD5Test.java b/blockchain-server/blockchain-server-user/src/test/java/MD5Test.java new file mode 100644 index 0000000..6038e4d --- /dev/null +++ b/blockchain-server/blockchain-server-user/src/test/java/MD5Test.java @@ -0,0 +1,11 @@ +import com.blockchain.common.base.util.MD5Utils; + +/** + * @author xusm + * @data 2019/2/21 20:58 + */ +public class MD5Test { + public static void main(String[] args) { + System.out.println(MD5Utils.MD5("111111")); + } +} diff --git a/blockchain-server/pom.xml b/blockchain-server/pom.xml new file mode 100644 index 0000000..f393496 --- /dev/null +++ b/blockchain-server/pom.xml @@ -0,0 +1,43 @@ + + + + blockchain + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + blockchain-server + pom + 1.0-SNAPSHOT + + blockchain-server-base + blockchain-server-cct + blockchain-server-otc + blockchain-server-databot + blockchain-server-btc + blockchain-server-eth + blockchain-server-eos + blockchain-server-currency + blockchain-server-sysconf + blockchain-server-imJg + blockchain-server-user + + + + + com.blockchain + blockchain-common-base + 1.0-SNAPSHOT + + + + com.blockchain + blockchain-common-pom + 1.0-SNAPSHOT + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..7d16aae --- /dev/null +++ b/pom.xml @@ -0,0 +1,119 @@ + + + 4.0.0 + + com.blockchain + blockchain + pom + 1.0-SNAPSHOT + + spring-cloud + blockchain-common + blockchain-server + tx-lcn + + + + + + 1.8 + UTF-8 + 2.6 + 3.2 + 1.3.5.RELEASE + + + 2.0.3.RELEASE + Finchley.SR2 + + + yanhuo + latest + + + yanhuo-eureka + + + + + org.projectlombok + lombok + true + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud-version} + pom + import + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot-version} + pom + import + + + + + + ${custom-project-name}-${project.artifactId} + + + src/main/resources + true + + + + + + org.apache.maven.plugins + maven-resources-plugin + ${mvn.resources.version} + + ${encoding} + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvn.compiler.version} + + ${jdk.version} + ${jdk.version} + ${encoding} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvn.compiler.version} + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.mvn.version} + + + + repackage + + + + + + + + \ No newline at end of file diff --git a/spring-cloud/pom.xml b/spring-cloud/pom.xml new file mode 100644 index 0000000..2b502ea --- /dev/null +++ b/spring-cloud/pom.xml @@ -0,0 +1,22 @@ + + + + blockchain + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + spring-cloud + pom + 1.0-SNAPSHOT + + spring-cloud-eureka + spring-cloud-hystrix-dashboard + spring-cloud-config + + + + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/pom.xml b/spring-cloud/spring-cloud-config/pom.xml new file mode 100644 index 0000000..ff65269 --- /dev/null +++ b/spring-cloud/spring-cloud-config/pom.xml @@ -0,0 +1,37 @@ + + + + spring-cloud + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + spring-cloud-config + 1.0-SNAPSHOT + + + org.springframework.cloud + spring-cloud-config-server + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.boot + spring-boot-starter-actuator + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/src/main/java/com/blockchain/spring/cloud/config/ConfigApplication.java b/spring-cloud/spring-cloud-config/src/main/java/com/blockchain/spring/cloud/config/ConfigApplication.java new file mode 100644 index 0000000..51508a2 --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/java/com/blockchain/spring/cloud/config/ConfigApplication.java @@ -0,0 +1,19 @@ +package com.blockchain.spring.cloud.config; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.config.server.EnableConfigServer; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; + +/** + * @author huangxl + * Created on 2018/11/5 + */ +@SpringBootApplication +@EnableConfigServer +@EnableEurekaClient +public class ConfigApplication { + public static void main(String[] args) { + SpringApplication.run(ConfigApplication.class, args); + } +} \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/src/main/resources/application.yml b/spring-cloud/spring-cloud-config/src/main/resources/application.yml new file mode 100644 index 0000000..1c1ccac --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/application.yml @@ -0,0 +1,20 @@ +server: + port: 8002 +spring: + application: + name: dapp-config-server + profiles: + active: native + cloud: + config: + server: + native: + search-locations: classpath:/properties/ + git: + uri: http://120.79.243.120:8099/spring-cloud/config-repo.git +eureka: + client: + service-url: + defaultZone: http://eureka:8001/eureka/ + instance: + prefer-ip-address: true \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/authconfig-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/authconfig-dev.yml new file mode 100644 index 0000000..239488d --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/authconfig-dev.yml @@ -0,0 +1,3 @@ +AUTH: + ISPASS_LOW_PREFIX: 0 + ISPASS_HIGN_PREFIX: 0 diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/btcconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/btcconf-dev.yml new file mode 100644 index 0000000..63f7dca --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/btcconf-dev.yml @@ -0,0 +1,15 @@ +btc: + #测试网 + rpc: + user: admin + password: admin123 + url: http://120.79.243.120:8332 + #测试节点USDT代币id + usdt_property_id: 1 + #正式网 +# rpc: +# user: bijiaosuo +# password: bi@123jiao!678suo +# url: http://47.52.194.217:8332 +# #正式节点USDT代币id +# usdt_property_id: 31 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/dbconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/dbconf-dev.yml new file mode 100644 index 0000000..aedda40 --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/dbconf-dev.yml @@ -0,0 +1,41 @@ +spring: + datasource: +# url: jdbc:mysql://47.74.158.47:3306/yanhuo?useUnicode=true&characterEncoding=UTF-8&useSSL=false +# username: yanhuo +# password: Skjbi8Fd6t7BdFa8 + url: jdbc:mysql://127.0.0.1:3306/yanhuo?useUnicode=true&characterEncoding=UTF-8&useSSL=false + username: root + password: +# url: jdbc:mysql://161.117.192.37:3306/yanhuo_test?useUnicode=true&characterEncoding=UTF-8&useSSL=false +# username: yanhuo_test +# password: yanhuo_test + driver-class-name: com.mysql.jdbc.Driver + druid: + initial-size: 1 + max-active: 2 + min-idle: 0 + max-wait: -1 + # 配置 StatFilter + filter: + stat: + log-slow-sql: true + slow-sql-millis: 2000 + enabled: true + # druid 监控 访问/${context-path}/druid + stat-view-servlet: + enabled: true + login-username: admin + login-password: admin123 + +mybatis: + #mapper 文件路径 + mapper-locations: classpath*:mapper/*.xml + configuration: + #驼峰转换 + map-underscore-to-camel-case: true + +pagehelper: + auto-dialect: mysql + #设置为true时,如果 输入值<0,则查询第0条;输入值>最大页数,则查询最后一页 + reasonable: false + support-methods-arguments: true diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/emailconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/emailconf-dev.yml new file mode 100644 index 0000000..3e11b22 --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/emailconf-dev.yml @@ -0,0 +1,13 @@ +#邮件发送方的email +email: + fromAccount: flamex333@gmail.com + #主机名 126邮箱为smtp.126.com,163邮箱为163.smtp.com,QQ为smtp.qq.com + hostName: smtp.gmail.com + #邮件发送方的授权码 + authCode: yanhuo888@99 + #邮件发送方的用户名 + userName: FLAMEX + #过期时间,单位:分钟,默认30分钟 + timeout: 30 + #是否关闭邮箱 关闭(true) | 开启(false) + closed: false \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/eosconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/eosconf-dev.yml new file mode 100644 index 0000000..02139e2 --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/eosconf-dev.yml @@ -0,0 +1,8 @@ +# 测试网点 +rpc_url: http://api-kylin.eosasia.one/ + +# eos正式网点 +#rpc_url: http://api.eosnewyork.io/ +get_block_url: v1/chain/get_block +get_transaction: v1/history/get_transaction +get_actions: v1/history/get_actions \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/ethconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/ethconf-dev.yml new file mode 100644 index 0000000..1956e7d --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/ethconf-dev.yml @@ -0,0 +1,16 @@ +#以太坊连接配置 +ethconfig: + emptyAddress: "0x0000000000000000000000000000000000000000" + os: "windows" # ['unix','windows'] + urlType: "rpc" #['rpc','ipc'] +# url: "https://ropsten.infura.io/mew" #测试网络 + url: "https://mainnet.infura.io/mew" + +#以太坊钱包参数配置 +wallert: + eth: # ETH燃烧费用设置 + gasPrice: 10000000000 + gasLimit: 21000 + token: # ETH代币燃烧费用设置 + gasPrice: 10000000000 + gasLimit: 80000 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/fileconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/fileconf-dev.yml new file mode 100644 index 0000000..c5b1782 --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/fileconf-dev.yml @@ -0,0 +1,2 @@ +FILES_DIR: + ROOT: /app/files_root \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/imjgconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/imjgconf-dev.yml new file mode 100644 index 0000000..66a3f12 --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/imjgconf-dev.yml @@ -0,0 +1,10 @@ +#极光配置信息 +JiGuang: + p_type: yanhuo + appkey: d8496dd94124ee33209f3fc4 + secret: 7f728916dfb2a81e734c59c4 + #随机数,随便填 + random_str: d8496dd94124ee33209f3fc42019 + api: + address: https://api.im.jpush.cn + address_v2: https://report.im.jpush.cn/v2 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/ipconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/ipconf-dev.yml new file mode 100644 index 0000000..f555820 --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/ipconf-dev.yml @@ -0,0 +1,2 @@ +ip: + inner: 127.0.0.1,192.168.105.10 #inner内部接口不拦截的ip diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/marketconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/marketconf-dev.yml new file mode 100644 index 0000000..66ea175 --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/marketconf-dev.yml @@ -0,0 +1,9 @@ +market: + coinbase: + USD: https://api.coinbase.com/v2/exchange-rates?currency=USD + kraken: + url: https://api.kraken.com/0/public/Trades?pair= + currencys: "{\"USDT\":\"USDTZUSD\"}" + huobi: + url: https://api.huobi.pro/market/history/trade?size=5&symbol= + currencys: "[[\"btcusdt\",\"BTC\",\"USDT\"],[\"ethusdt\",\"ETH\",\"USDT\"],[\"eosusdt\",\"EOS\",\"USDT\"],[\"ethbtc\",\"ETH\",\"BTC\"],[\"eosbtc\",\"EOS\",\"BTC\"],[\"eoseth\",\"EOS\",\"ETH\"]]" \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/pushconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/pushconf-dev.yml new file mode 100644 index 0000000..e5e7a98 --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/pushconf-dev.yml @@ -0,0 +1,6 @@ +getuipush: + appId: FdfEiZmSEt69ZqGSsmurT1 + appKey: mjbrfDYpMw7ikH7tb1b4H3 + masterSecret: 7UbovKkqTU9locZt0FzwC + host: http://sdk.open.api.igexin.com/apiex.htm + open: true \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/redisconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/redisconf-dev.yml new file mode 100644 index 0000000..3d9bb6b --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/redisconf-dev.yml @@ -0,0 +1,19 @@ +spring: + redis: + database: 2 + host: 172.20.0.1 + port: 6379 + password: + timeout: 10000ms + jedis: + pool: + min-idle: 0 + max-idle: 8 + max-active: 8 + max-wait: -1ms + cache: + #自定义属性,redis缓存失效时间 + timeout-seconds: 1800 + expired: + #失效缓存key监听 + listener: __keyevent@${spring.redis.database}__:expired \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/smsconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/smsconf-dev.yml new file mode 100644 index 0000000..dfe93cc --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/smsconf-dev.yml @@ -0,0 +1,20 @@ +#aliyunsms: +# closed: false +# timeout: 5 +# SginName: 焰火网 +# TemplateCode: SMS_10465107 +# ACCESS_KEY_ID: LTAIR0MJ0WtQJbLj +# ACCESS_SECRET: nhVRtuw5JaRBRXrk96DkB7Las4NjCb +# ENGLISH_MSG: "【FLAME】Your verification code is: ${code}, which is valid within 5 minutes. Please do not disclose it to others." +# BIG5_MSG: 【焰火網】您的驗證碼為:${code},該驗證碼5分鐘內有效,請勿泄漏於他人。 + + +aliyunsms: + closed: true + timeout: 5 + SginName: 焰火网 + TemplateCode: SMS_10505006 + ACCESS_KEY_ID: LTAI46NrbdbBtAdS + ACCESS_SECRET: oG4lZDX0dyH98CQNjSQ9gfTYdc5tj5 + ENGLISH_MSG: "【FLAME】Your verification code is: ${code}, which is valid within 5 minutes. Please do not disclose it to others." + BIG5_MSG: 【焰火網】您的驗證碼為:${code},該驗證碼5分鐘內有效,請勿泄漏於他人。 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/springcloudconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/springcloudconf-dev.yml new file mode 100644 index 0000000..ce70cb1 --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/springcloudconf-dev.yml @@ -0,0 +1,26 @@ +feign: + hystrix: + #开启熔断器 + enabled: false +management: + endpoints: + web: + exposure: + #暴露接口,用于熔断监控 + include: hystrix.stream +hystrix: + shareSecurityContext: true + command: + default: + circuitBreaker: + sleepWindowInMilliseconds: 100000 + forceClosed: true + execution: + isolation: + thread: + timeoutInMilliseconds: 600000 + +ribbon: + ReadTimeout: 60000 + ConnectTimeout: 60000 + diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/springconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/springconf-dev.yml new file mode 100644 index 0000000..6ba7cdc --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/springconf-dev.yml @@ -0,0 +1,13 @@ +#spring 配置信息 +spring: + jackson: + #格式化后台返回时间 + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + serialization: + fail-on-empty-beans: false + mvc: + date-format: yyyy-MM-dd HH:mm:ss + http: + encoding: + charset: UTF-8 \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/txconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/txconf-dev.yml new file mode 100644 index 0000000..b07a8bd --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/txconf-dev.yml @@ -0,0 +1,4 @@ +#分布式事务配置 +tm: + manager: + url: http://tx:7000/tx/manager/ \ No newline at end of file diff --git a/spring-cloud/spring-cloud-config/src/main/resources/properties/xssconf-dev.yml b/spring-cloud/spring-cloud-config/src/main/resources/properties/xssconf-dev.yml new file mode 100644 index 0000000..e7b7c4b --- /dev/null +++ b/spring-cloud/spring-cloud-config/src/main/resources/properties/xssconf-dev.yml @@ -0,0 +1,7 @@ +#放置xss攻击配置,如果需要自定义,则不需要引入该配置文件 +xss: + enabled: false + # 排除链接(多个用逗号分隔)/system/notice/* + excludes: + # 匹配链接 /user/* + urlPatterns: /* \ No newline at end of file diff --git a/spring-cloud/spring-cloud-eureka/pom.xml b/spring-cloud/spring-cloud-eureka/pom.xml new file mode 100644 index 0000000..c053202 --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/pom.xml @@ -0,0 +1,29 @@ + + + + spring-cloud + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + spring-cloud-eureka + 1.0-SNAPSHOT + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-server + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-eureka/src/main/java/com/blockchain/spring/cloud/eureka/EurekaApplication.java b/spring-cloud/spring-cloud-eureka/src/main/java/com/blockchain/spring/cloud/eureka/EurekaApplication.java new file mode 100644 index 0000000..50054e8 --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/src/main/java/com/blockchain/spring/cloud/eureka/EurekaApplication.java @@ -0,0 +1,17 @@ +package com.blockchain.spring.cloud.eureka; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; + +/** + * @author huangxl + * Created on 2018/11/5 + */ +@SpringBootApplication +@EnableEurekaServer +public class EurekaApplication { + public static void main(String[] args) { + SpringApplication.run(EurekaApplication.class,args); + } +} diff --git a/spring-cloud/spring-cloud-eureka/src/main/resources/application.yml b/spring-cloud/spring-cloud-eureka/src/main/resources/application.yml new file mode 100644 index 0000000..22ff785 --- /dev/null +++ b/spring-cloud/spring-cloud-eureka/src/main/resources/application.yml @@ -0,0 +1,15 @@ +spring: + application: + name: dapp-eureka-server +server: + port: 8001 +eureka: + instance: + hostname: localhost + client: + registerWithEureka: false + fetchRegistry: false + service-url: + defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ + server: + enable-self-preservation: false diff --git a/spring-cloud/spring-cloud-hystrix-dashboard/pom.xml b/spring-cloud/spring-cloud-hystrix-dashboard/pom.xml new file mode 100644 index 0000000..2836e7d --- /dev/null +++ b/spring-cloud/spring-cloud-hystrix-dashboard/pom.xml @@ -0,0 +1,33 @@ + + + + spring-cloud + com.blockchain + 1.0-SNAPSHOT + + 4.0.0 + + spring-cloud-hystrix-dashboard + + + + + org.springframework.cloud + spring-cloud-starter-netflix-hystrix + + + + org.springframework.cloud + spring-cloud-starter-netflix-hystrix-dashboard + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + \ No newline at end of file diff --git a/spring-cloud/spring-cloud-hystrix-dashboard/src/main/java/com/blockchain/springcloud/dashboard/DashboardApplication.java b/spring-cloud/spring-cloud-hystrix-dashboard/src/main/java/com/blockchain/springcloud/dashboard/DashboardApplication.java new file mode 100644 index 0000000..1fcedd8 --- /dev/null +++ b/spring-cloud/spring-cloud-hystrix-dashboard/src/main/java/com/blockchain/springcloud/dashboard/DashboardApplication.java @@ -0,0 +1,17 @@ +package com.blockchain.springcloud.dashboard; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; + +/** + * @author huangxl + * @create 2018-11-14 15:39 + */ +@SpringBootApplication +@EnableHystrixDashboard +public class DashboardApplication { + public static void main(String[] args) { + SpringApplication.run(DashboardApplication.class,args); + } +} diff --git a/spring-cloud/spring-cloud-hystrix-dashboard/src/main/resources/application.yml b/spring-cloud/spring-cloud-hystrix-dashboard/src/main/resources/application.yml new file mode 100644 index 0000000..40f7571 --- /dev/null +++ b/spring-cloud/spring-cloud-hystrix-dashboard/src/main/resources/application.yml @@ -0,0 +1,5 @@ +server: + port: 9000 +spring: + application: + name: hystrix-dashboard \ No newline at end of file diff --git a/tx-lcn/LICENSE b/tx-lcn/LICENSE new file mode 100644 index 0000000..3d05a1d --- /dev/null +++ b/tx-lcn/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +   Copyright 2017 CodingApi + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/tx-lcn/README.md b/tx-lcn/README.md new file mode 100644 index 0000000..8962f14 --- /dev/null +++ b/tx-lcn/README.md @@ -0,0 +1,166 @@ +# LCN分布式事务框架v4.0 + + "LCN并不生产事务,LCN只是本地事务的协调者" + +## 框架介绍 + + LCN分布式事务框架的核心功能是对本地事务的协调控制,框架本身并不创建事务,只是对本地事务做协调控制。因此该框架与其他第三方的框架兼容性强,支持所有的关系型数据库事务,支持多数据源,支持与第三方数据库框架一块使用(例如 sharding-jdbc),在使用框架的时候只需要添加分布式事务的注解即可,对业务的侵入性低。LCN框架主要是为微服务框架提供分布式事务的支持,在微服务框架上做了进一步的事务机制优化,在一些负载场景上LCN事务机制要比本地事务机制的性能更好,4.0以后框架开方了插件机制可以让更多的第三方框架支持进来。 + + +## 官方网址 + +[https://www.txlcn.org](https://www.txlcn.org) + + +## 框架特点 + +1. 支持各种基于spring的db框架 +2. 兼容SpringCloud、Dubbo、motan +3. 使用简单,低依赖,代码完全开源 +4. 基于切面的强一致性事务框架 +5. 高可用,模块可以依赖RPC模块做集群化,TxManager也可以做集群化 +6. 支持本地事务和分布式事务共存 +7. 支持事务补偿机制,增加事务补偿决策提醒 +8. 添加插件拓展机制 + + +## 原理介绍 + +[原理介绍](https://github.com/codingapi/tx-lcn/wiki) [视频讲解](https://www.txlcn.org/v4/index.html) + +## 目录说明 + +transaction-dubbo LCN dubbo rpc框架扩展支持 + +transaction-springcloud LCN springcloud rpc框架扩展支持 + +transaction-motan LCN motan rpc框架扩展支持 + +tx-client 是LCN核心tx模块端控制框架 + +tx-manager 是LCN 分布式事务协调器 + +tx-plugins-db 是LCN 对关系型数据库的插件支持 + + +## 使用说明 + +分布式事务发起方: + +``` + + @Override + @TxTransaction(isStart=true) + @Transactional + public boolean hello() { + //本地调用 + testDao.save(); + //远程调用方 + boolean res = test2Service.test(); + //模拟异常 + int v = 100/0; + return true; + } + + +``` + +分布式事务被调用方(test2Service的业务实现类) +``` + + @Override + @Transactional + @TxTransaction + public boolean test() { + //本地调用 + testDao.save(); + return true; + } + +``` + +如上代码执行完成以后两个模块都将回滚事务。 + +说明:在使用LCN分布式事务时,只需要将事务的开始方法添加`@TxTransaction(isStart=true)`注解即可,在参与方添加`@TxTransaction`或者实现`ITxTransaction`接口即可。详细见demo教程 + +## 关于@TxTransaction 使用说明 + + @TxTransaction注解是分布式事务的标示。 + + 若存在业务方法:a->b b->c b->d,那么开启分布式事务注解的话,需要在各个模块方法上添加@TxTransaction即可。 + +``` + @TxTransaction(isStart=true) + @Transactional + public void a(){ + b(); + } + + @TxTransaction + public void b(){ + c(); + d(); + } + + @TxTransaction + public void c(){} + + @TxTransaction + public void d(){} +``` + +## maven 中心库地址 + + +``` + + com.codingapi + tx-client + ${lcn.last.version} + + + + + com.codingapi + tx-plugins-db + ${lcn.last.version} + + + + + com.codingapi + transaction-dubbo + ${lcn.last.version} + + + + com.codingapi + transaction-motan + ${lcn.last.version} + + + + + com.codingapi + transaction-springcloud + ${lcn.last.version} + + +``` + +依赖gradle等形式,见中心库 + +[http://mvnrepository.com/search?q=codingapi](http://mvnrepository.com/search?q=codingapi) + + +## demo演示教程 + +每个demo下有区分为 jdbc/hibernate/mybatis不同框架的版本demo + +[springcloud版本](https://github.com/codingapi/springcloud-lcn-demo) + +[dubbo版本](https://github.com/codingapi/dubbo-lcn-demo) + +[motan版本](https://gitee.com/zfvipCase/motan-lcn-demo) + +技术交流群:554855843 diff --git a/tx-lcn/pom.xml b/tx-lcn/pom.xml new file mode 100644 index 0000000..eabf946 --- /dev/null +++ b/tx-lcn/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + blockchain + com.blockchain + 1.0-SNAPSHOT + + com.codingapi + tx-lcn + 4.2.0-SNAPSHOT + pom + + tx-lcn + https://github.com/codingapi/tx-lcn + tx-lcn project for LCN + + + tx-client + tx-manager + transaction-springcloud + tx-plugins-db + + + + 4.2.0-SNAPSHOT + + diff --git a/tx-lcn/transaction-springcloud/README.md b/tx-lcn/transaction-springcloud/README.md new file mode 100644 index 0000000..e81188c --- /dev/null +++ b/tx-lcn/transaction-springcloud/README.md @@ -0,0 +1,16 @@ +# transaction-springcloud + +是LCN基于springcloud的分布式事务框架 + +修改com.codingapi.tx.springcloud.listener.ServerListener 第28行 + +引入: + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + org.springframework.cloud + spring-cloud-starter-netflix-ribbon + \ No newline at end of file diff --git a/tx-lcn/transaction-springcloud/pom.xml b/tx-lcn/transaction-springcloud/pom.xml new file mode 100644 index 0000000..8edf7bc --- /dev/null +++ b/tx-lcn/transaction-springcloud/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + + com.codingapi + tx-lcn + 4.2.0-SNAPSHOT + + + com.codingapi + transaction-springcloud + ${lcn.last.version} + + + transaction-springcloud + https://github.com/codingapi/tx-lcn + + transaction-springcloud project for Spring Boot + + + 1.3.2.RELEASE + 4.3.7.RELEASE + + + + + + + com.codingapi + tx-client + ${lcn.last.version} + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + org.springframework.cloud + spring-cloud-starter-netflix-ribbon + + + + org.springframework + spring-context + ${org.springframework-version} + + + + + + diff --git a/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnLoadBalancerRule.java b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnLoadBalancerRule.java new file mode 100644 index 0000000..aa79642 --- /dev/null +++ b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnLoadBalancerRule.java @@ -0,0 +1,94 @@ +package com.codingapi.ribbon.loadbalancer; + + +import com.codingapi.tx.aop.bean.TxTransactionLocal; +import com.lorne.core.framework.utils.encode.MD5Util; +import com.netflix.loadbalancer.Server; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * created by foxdd 2017-12-05 + */ +public class LcnLoadBalancerRule { + + private Logger logger = LoggerFactory.getLogger(LcnLoadBalancerRule.class); + + public Server proxy(List servers,Server server){ + + TxTransactionLocal txTransactionLocal = TxTransactionLocal.current(); + if(txTransactionLocal==null){ + return server; + } + + try{ + logger.debug("LCNBalanceProxy - > start"); + + String groupId = txTransactionLocal.getGroupId(); + + //取出组件的appName + String appName = server.getMetaInfo().getAppName(); + + + String key = MD5Util.md5((groupId + "_" + appName).getBytes()); + + //如果只有一个可调用模块,则用当前的,且需要将数据记录到redis中 + if(servers.size() == 1){ + putServer(key, txTransactionLocal, server); + logger.debug("LCNBalanceProxy -> only one server available"); + return server; + } + + Server oldServer =getServer(txTransactionLocal,servers,key); + if(oldServer != null){ + logger.debug("LCNBalanceProxy - > load old server "); + return server; + } + + putServer(key, txTransactionLocal, server); + logger.debug("LCNBalanceProxy - > load new server "); + + return server; + }finally { + logger.debug("LCNBalanceProxy - > end"); + } + } + + + + private void putServer(String key,TxTransactionLocal txTransactionLocal,Server server){ + String serviceName = server.getMetaInfo().getAppName(); + String address = server.getHostPort(); + + String md5 = MD5Util.md5((address+serviceName).getBytes()); + + logger.debug("putServer->address->"+address+",md5-->"+md5); + + txTransactionLocal.putLoadBalance(key,md5); + } + + + private Server getServer(TxTransactionLocal txTransactionLocal, List servers, String key){ + String val = txTransactionLocal.getLoadBalance(key); + if(StringUtils.isEmpty(val)){ + return null; + } + for(Server server:servers){ + String serviceName = server.getMetaInfo().getAppName(); + String address = server.getHostPort(); + + String md5 = MD5Util.md5((address+serviceName).getBytes()); + + logger.debug("getServer->address->"+address+",md5-->"+md5); + + if(val.equals(md5)){ + return server; + } + } + return null; + } + +} diff --git a/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnNoOpLoadBalancerProxy.java b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnNoOpLoadBalancerProxy.java new file mode 100644 index 0000000..1814260 --- /dev/null +++ b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnNoOpLoadBalancerProxy.java @@ -0,0 +1,34 @@ +package com.codingapi.ribbon.loadbalancer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.loadbalancer.NoOpLoadBalancer; +import com.netflix.loadbalancer.Server; + +import java.util.ArrayList; +import java.util.List; + +/** + * created by foxdd 2017-12-05 + */ +public class LcnNoOpLoadBalancerProxy extends NoOpLoadBalancer { + + private Logger logger = LoggerFactory.getLogger(LcnNoOpLoadBalancerProxy.class); + + LcnLoadBalancerRule lcnLoadBalancerRule = new LcnLoadBalancerRule(); + + public LcnNoOpLoadBalancerProxy(){ + super(); + } + + @Override + public Server chooseServer(Object key){ + logger.debug("enter chooseServer method, key:" + key); + + List serverList = new ArrayList(); + return lcnLoadBalancerRule.proxy(serverList, super.chooseServer(key)); + + } + +} diff --git a/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnRibbonConfiguration.java b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnRibbonConfiguration.java new file mode 100644 index 0000000..e56700f --- /dev/null +++ b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnRibbonConfiguration.java @@ -0,0 +1,37 @@ +package com.codingapi.ribbon.loadbalancer; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.IPing; +import com.netflix.loadbalancer.IRule; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; +import com.netflix.loadbalancer.ServerListFilter; +import com.netflix.loadbalancer.ServerListUpdater; + +@Configuration +public class LcnRibbonConfiguration { + + /** + * 为ribbon的loadbalancer做代理,相比于重写IRULE,重写loadbalancer更有利于用户自选LB算法,而且有默认LB算法可用 + * @param config + * @param serverList + * @param serverListFilter + * @param rule + * @param ping + * @param serverListUpdater + * @return + */ + @Bean + public ILoadBalancer ribbonLoadBalancer(IClientConfig config, + ServerList serverList, ServerListFilter serverListFilter, + IRule rule, IPing ping, ServerListUpdater serverListUpdater) { + return new LcnZoneAwareLoadBalancerProxy(config, rule, ping, serverList, + serverListFilter, serverListUpdater); + } + + +} diff --git a/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnZoneAwareLoadBalancerProxy.java b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnZoneAwareLoadBalancerProxy.java new file mode 100644 index 0000000..87ea502 --- /dev/null +++ b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/ribbon/loadbalancer/LcnZoneAwareLoadBalancerProxy.java @@ -0,0 +1,42 @@ +package com.codingapi.ribbon.loadbalancer; + +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * created by foxdd 2017-12-05 + */ +public class LcnZoneAwareLoadBalancerProxy extends ZoneAwareLoadBalancer { + + private Logger logger = LoggerFactory.getLogger(LcnZoneAwareLoadBalancerProxy.class); + + LcnLoadBalancerRule lcnLoadBalancerRule = new LcnLoadBalancerRule(); + + public LcnZoneAwareLoadBalancerProxy(IClientConfig clientConfig, IRule rule, + IPing ping, ServerList serverList, ServerListFilter filter, + ServerListUpdater serverListUpdater) { + super(clientConfig, rule, ping, serverList, filter, serverListUpdater); + } + + @Override + public Server chooseServer(Object key){ + logger.info("enter chooseServer method, key:" + key); + //利用Dynamic负载类中的方法,可以在运行时更新服务实例清单 + List serverList = new ArrayList(); + //获取处理之后的serverlist + serverList = super.getServerListImpl().getUpdatedListOfServers(); + //获取过滤之后的serverlist + serverList = super.getFilter().getFilteredListOfServers(serverList); + if(null == serverList || serverList.isEmpty()){ + return super.chooseServer(key); + } + return lcnLoadBalancerRule.proxy(serverList, super.chooseServer(key)); + + } + +} diff --git a/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/RequestInterceptorConfiguration.java b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/RequestInterceptorConfiguration.java new file mode 100644 index 0000000..1b7c36f --- /dev/null +++ b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/RequestInterceptorConfiguration.java @@ -0,0 +1,18 @@ +package com.codingapi.tx; + +import com.codingapi.tx.springcloud.feign.TransactionRestTemplateInterceptor; +import feign.RequestInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * create by lorne on 2018/1/18 + */ +@Configuration +public class RequestInterceptorConfiguration { + + @Bean + public RequestInterceptor requestInterceptor(){ + return new TransactionRestTemplateInterceptor(); + } +} diff --git a/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/TransactionConfiguration.java b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/TransactionConfiguration.java new file mode 100644 index 0000000..fff9869 --- /dev/null +++ b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/TransactionConfiguration.java @@ -0,0 +1,20 @@ +package com.codingapi.tx; + +import org.springframework.cloud.netflix.ribbon.RibbonClients; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +import com.codingapi.ribbon.loadbalancer.LcnRibbonConfiguration; + +/** + * Created by lorne on 2017/6/26. + */ + +@Configuration +@ComponentScan +@RibbonClients(defaultConfiguration=LcnRibbonConfiguration.class) +public class TransactionConfiguration { + + + +} diff --git a/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/feign/TransactionRestTemplateInterceptor.java b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/feign/TransactionRestTemplateInterceptor.java new file mode 100644 index 0000000..6431b0d --- /dev/null +++ b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/feign/TransactionRestTemplateInterceptor.java @@ -0,0 +1,30 @@ +package com.codingapi.tx.springcloud.feign; + +import com.codingapi.tx.aop.bean.TxTransactionLocal; +import feign.RequestInterceptor; +import feign.RequestTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Created by lorne on 2017/6/26. + */ +public class TransactionRestTemplateInterceptor implements RequestInterceptor { + + + private Logger logger = LoggerFactory.getLogger(TransactionRestTemplateInterceptor.class); + + @Override + public void apply(RequestTemplate requestTemplate) { + + TxTransactionLocal txTransactionLocal = TxTransactionLocal.current(); + String groupId = txTransactionLocal == null ? null : txTransactionLocal.getGroupId(); + + logger.info("LCN-SpringCloud TxGroup info -> groupId:"+groupId); + + if (txTransactionLocal != null) { + requestTemplate.header("tx-group", groupId); + } + } + +} diff --git a/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/http/TransactionHttpRequestInterceptor.java b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/http/TransactionHttpRequestInterceptor.java new file mode 100644 index 0000000..0c9ee29 --- /dev/null +++ b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/http/TransactionHttpRequestInterceptor.java @@ -0,0 +1,34 @@ +package com.codingapi.tx.springcloud.http; + +import com.codingapi.tx.aop.bean.TxTransactionLocal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; + +import java.io.IOException; + +/** + * Created by lorne on 2017/7/3. + */ +public class TransactionHttpRequestInterceptor implements ClientHttpRequestInterceptor { + + + private Logger logger = LoggerFactory.getLogger(TransactionHttpRequestInterceptor.class); + + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { + + TxTransactionLocal txTransactionLocal = TxTransactionLocal.current(); + String groupId = txTransactionLocal == null ? null : txTransactionLocal.getGroupId(); + + logger.info("LCN-SpringCloud TxGroup info -> groupId:"+groupId); + + if(txTransactionLocal!=null) { + request.getHeaders().add("tx-group", groupId); + } + return execution.execute(request,body); + } +} diff --git a/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TransactionAspect.java b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TransactionAspect.java new file mode 100644 index 0000000..1b33e9c --- /dev/null +++ b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TransactionAspect.java @@ -0,0 +1,51 @@ +package com.codingapi.tx.springcloud.interceptor; + +import com.codingapi.tx.Constants; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; + +/** + * LCN 事务拦截器 + * create by lorne on 2018/1/5 + */ + +@Aspect +@Component +public class TransactionAspect implements Ordered { + + private Logger logger = LoggerFactory.getLogger(TransactionAspect.class); + + @Autowired + private TxManagerInterceptor txManagerInterceptor; + + + @Around("@annotation(com.codingapi.tx.annotation.TxTransaction)") + public Object transactionRunning(ProceedingJoinPoint point)throws Throwable{ + logger.debug("annotation-TransactionRunning-start---->"); + Object obj = txManagerInterceptor.around(point); + logger.debug("annotation-TransactionRunning-end---->"); + return obj; + } + + @Around("this(com.codingapi.tx.annotation.ITxTransaction) && execution( * *(..))") + public Object around(ProceedingJoinPoint point)throws Throwable{ + logger.debug("interface-ITransactionRunning-start---->"); + Object obj = txManagerInterceptor.around(point); + logger.debug("interface-ITransactionRunning-end---->"); + return obj; + } + + + @Override + public int getOrder() { + return Constants.ASPECT_ORDER; + } + + +} diff --git a/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TxManagerInterceptor.java b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TxManagerInterceptor.java new file mode 100644 index 0000000..1898d74 --- /dev/null +++ b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/interceptor/TxManagerInterceptor.java @@ -0,0 +1,34 @@ +package com.codingapi.tx.springcloud.interceptor; + +import com.codingapi.tx.aop.service.AspectBeforeService; +import org.aspectj.lang.ProceedingJoinPoint; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +/** + * Created by lorne on 2017/6/7. + */ + +@Component +public class TxManagerInterceptor { + + @Autowired + private AspectBeforeService aspectBeforeService; + + public Object around(ProceedingJoinPoint point) throws Throwable { + String groupId = null; + String mode = null; + try { + RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); + HttpServletRequest request = requestAttributes == null ? null : ((ServletRequestAttributes) requestAttributes).getRequest(); + groupId = request == null ? null : request.getHeader("tx-group"); + mode = request == null ? null : request.getHeader("tx-mode"); + }catch (Exception e){} + return aspectBeforeService.around(groupId, point, mode); + } +} diff --git a/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/listener/ServerListener.java b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/listener/ServerListener.java new file mode 100644 index 0000000..ee82e9d --- /dev/null +++ b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/listener/ServerListener.java @@ -0,0 +1,48 @@ +package com.codingapi.tx.springcloud.listener; + +import com.codingapi.tx.listener.service.InitService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + + +@Component +public class ServerListener implements ApplicationListener { + + private Logger logger = LoggerFactory.getLogger(ServerListener.class); + + private int serverPort; + @Value("${server.port}") + private String port; + + @Autowired + private InitService initService; + + @Override + public void onApplicationEvent(ApplicationEvent event) { + logger.info("onApplicationEvent -> onApplicationEvent. "); + this.serverPort = Integer.parseInt(port);//event.getEmbeddedServletContainer().getPort(); + + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + // 若连接不上txmanager start()方法将阻塞 + initService.start(); + } + }); + thread.setName("TxInit-thread"); + thread.start(); + } + + public int getPort() { + return this.serverPort; + } + + public void setServerPort(int serverPort) { + this.serverPort = serverPort; + } +} diff --git a/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/service/impl/ModelNameServiceImpl.java b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/service/impl/ModelNameServiceImpl.java new file mode 100644 index 0000000..a9b6f9d --- /dev/null +++ b/tx-lcn/transaction-springcloud/src/main/java/com/codingapi/tx/springcloud/service/impl/ModelNameServiceImpl.java @@ -0,0 +1,78 @@ +package com.codingapi.tx.springcloud.service.impl; + +import com.codingapi.tx.listener.service.ModelNameService; +import com.codingapi.tx.springcloud.listener.ServerListener; +import com.lorne.core.framework.utils.encode.MD5Util; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Service; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * Created by lorne on 2017/7/12. + */ +@Service +@Configuration +public class ModelNameServiceImpl implements ModelNameService { + + @Value("${spring.application.name}") + private String modelName; + + @Autowired + private ServerListener serverListener; + + + private String host = null; + + @Override + public String getModelName() { + return modelName; + } + + private String getIp() { + if (host == null) { + try { + host = InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + e.printStackTrace(); + } + } + return host; + } + + private int getPort() { + int port = serverListener.getPort(); + int count = 0; + while (port == 0) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + port = serverListener.getPort(); + count++; + + if(count==2000){ + throw new RuntimeException("get server port error."); + } + } + + return port; + } + + @Override + public String getUniqueKey() { + String address = getIp() + getPort(); + return MD5Util.md5(address.getBytes()); + } + + + @Override + public String getIpAddress() { + String address = getIp() + ":" + getPort(); + return address; + } +} diff --git a/tx-lcn/transaction-springcloud/src/main/resources/META-INF/spring.factories b/tx-lcn/transaction-springcloud/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..a827f99 --- /dev/null +++ b/tx-lcn/transaction-springcloud/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.codingapi.tx.TransactionConfiguration \ No newline at end of file diff --git a/tx-lcn/transaction-springcloud/src/main/resources/banner.txt b/tx-lcn/transaction-springcloud/src/main/resources/banner.txt new file mode 100644 index 0000000..51d6834 --- /dev/null +++ b/tx-lcn/transaction-springcloud/src/main/resources/banner.txt @@ -0,0 +1,13 @@ + + >=> >=> >==> >=> + >=> >=> >=> >> >=> >=> + >=> >=> >=> >=> >=> + >=> >=> >=> >=>>=> + >=> >=> >=> > >=> + >=> >=> >=> >=> >>=> + >=======> >===> >=> >=> + + LCN-Client version:4.1.0 + + + diff --git a/tx-lcn/tx-client/README.md b/tx-lcn/tx-client/README.md new file mode 100644 index 0000000..9411501 --- /dev/null +++ b/tx-lcn/tx-client/README.md @@ -0,0 +1,3 @@ +# tx-client + +是LCN基于tx模块端的核心封装库 diff --git a/tx-lcn/tx-client/pom.xml b/tx-lcn/tx-client/pom.xml new file mode 100644 index 0000000..7f206d2 --- /dev/null +++ b/tx-lcn/tx-client/pom.xml @@ -0,0 +1,126 @@ + + + 4.0.0 + + + com.codingapi + tx-lcn + 4.2.0-SNAPSHOT + + + com.codingapi + tx-client + ${lcn.last.version} + + tx-client + https://github.com/codingapi/tx-lcn + + tx-client project for Spring Boot + + + + 4.3.7.RELEASE + 19.0 + 4.0.38 + 1.1.3 + 4.0.0 + 1.7.7 + + + + + + + com.github.1991wangliang + lorne_core + 1.0.0 + + + + io.netty + netty-all + 4.1.12.Final + + + + + org.aspectj + aspectjweaver + 1.8.4 + + + org.aspectj + aspectjrt + 1.8.4 + + + + + org.slf4j + slf4j-api + ${org.slf4j-version} + + + org.slf4j + slf4j-log4j12 + ${org.slf4j-version} + + + org.slf4j + slf4j-simple + ${org.slf4j-version} + + + org.slf4j + slf4j-jdk14 + ${org.slf4j-version} + + + + + + javax.servlet + javax.servlet-api + 3.1.0 + + + + + org.springframework + spring-context + ${org.springframework-version} + + + + + com.caucho + hessian + ${hessian.version} + + + com.dyuproject.protostuff + protostuff-core + ${protostuff.version} + + + com.dyuproject.protostuff + protostuff-runtime + ${protostuff.version} + + + com.esotericsoftware + kryo-shaded + ${kryo.version} + + + com.google.guava + guava + ${guava.version} + + + + + + + diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/Constants.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/Constants.java new file mode 100644 index 0000000..b4650b9 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/Constants.java @@ -0,0 +1,26 @@ +package com.codingapi.tx; + +import com.codingapi.tx.model.TxServer; + +/** + * Created by lorne on 2017/6/8. + */ +public class Constants { + + + public static volatile boolean hasExit = false; + + /** + * tm 服务对象 + */ + public static TxServer txServer; + + /** + * 主切面的 order值 + * 主切面一定要在 @Transaction 切面的外层(ASPECT_ORDER 小于 标签中的order ) + * 主切面需要能接受到异常。接收到异常才会触发回滚 + * 这意味着自定义的切面若catch了异常且不向外传递,那么这个切面需要在主切面的外层(自定义切面order 小于 ASPECT_ORDER) + */ + public static final int ASPECT_ORDER = 1000; + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/annotation/ITxTransaction.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/annotation/ITxTransaction.java new file mode 100644 index 0000000..45a1ad2 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/annotation/ITxTransaction.java @@ -0,0 +1,7 @@ +package com.codingapi.tx.annotation; + +/** + * create by lorne on 2018/1/25 + */ +public interface ITxTransaction { +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/annotation/TxTransaction.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/annotation/TxTransaction.java new file mode 100644 index 0000000..4584b13 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/annotation/TxTransaction.java @@ -0,0 +1,48 @@ +package com.codingapi.tx.annotation; + +import java.lang.annotation.*; + +/** + * Created by lorne on 2017/6/26. + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface TxTransaction { + + + /** + * 是否LCN事务发起方 + * @return true 是:是发起方 false 否:是参与方 + */ + boolean isStart() default false; + + + /** + * 回滚异常 + * @return + */ + Class[] rollbackFor() default {}; + + + /** + * 不回滚异常 + * @return + */ + Class[] noRollbackFor() default {}; + + /** + * 事务模式 仅在事务发起方配置有效 + * @return + */ + TxTransactionMode mode() default TxTransactionMode.TX_MODE_LCN; + + /** + * 标示本服务是否是只读 + * 若为true : 不会加入事务组; Connection 不会被 Wrap; 事务信息能正常传递 + * 在本服务无DB操作或仅有查询时请配置 true 将提高性能 + * 若应用都没有DB配置,此配置无意义不用设值 + */ + boolean readOnly() default false; +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/annotation/TxTransactionMode.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/annotation/TxTransactionMode.java new file mode 100644 index 0000000..1a549ed --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/annotation/TxTransactionMode.java @@ -0,0 +1,22 @@ +package com.codingapi.tx.annotation; + +/** + * + * + * @author caican + * 2018/7/24 + */ +public enum TxTransactionMode { + /** LCN 模式 */ + TX_MODE_LCN("LCN 模式,2阶段提交 读提交"), + + /** TXC 模式 */ + TX_MODE_TXC("TXC 模式,未提交读(READ UNCOMMITTED)"); + + + private String description; + + TxTransactionMode(String description) { + this.description = description; + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxCompensateLocal.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxCompensateLocal.java new file mode 100644 index 0000000..c405346 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxCompensateLocal.java @@ -0,0 +1,50 @@ +package com.codingapi.tx.aop.bean; + +/** + * 分布式事务远程调用控制对象 + * Created by lorne on 2017/6/5. + */ +public class TxCompensateLocal { + + private final static ThreadLocal currentLocal = new InheritableThreadLocal(); + + private String groupId; + + private String type; + + private int startState; + + + public int getStartState() { + return startState; + } + + public void setStartState(int startState) { + this.startState = startState; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public static TxCompensateLocal current() { + return currentLocal.get(); + } + + public static void setCurrent(TxCompensateLocal current) { + currentLocal.set(current); + } + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionInfo.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionInfo.java new file mode 100644 index 0000000..6d229b2 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionInfo.java @@ -0,0 +1,61 @@ +package com.codingapi.tx.aop.bean; + +import com.codingapi.tx.annotation.TxTransaction; +import com.codingapi.tx.annotation.TxTransactionMode; +import com.codingapi.tx.model.TransactionInvocation; + + +/** + * 切面控制对象 + * Created by lorne on 2017/6/8. + */ +public class TxTransactionInfo { + + + private TxTransaction transaction; + + + private TxTransactionLocal txTransactionLocal; + + /** + * 事务组Id + */ + private String txGroupId; + + + private TransactionInvocation invocation; + + private TxTransactionMode mode; + + public TxTransactionInfo(TxTransaction transaction, TxTransactionLocal txTransactionLocal, TransactionInvocation invocation, String txGroupId) { + this.transaction = transaction; + this.txTransactionLocal = txTransactionLocal; + this.txGroupId = txGroupId; + this.invocation = invocation; + } + + public TxTransactionMode getMode() { + return mode; + } + + public void setMode(TxTransactionMode mode) { + this.mode = mode; + } + + public TxTransaction getTransaction() { + return transaction; + } + + public TxTransactionLocal getTxTransactionLocal() { + return txTransactionLocal; + } + + public String getTxGroupId() { + return txGroupId; + } + + public TransactionInvocation getInvocation() { + return invocation; + } + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionLocal.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionLocal.java new file mode 100644 index 0000000..e134b5e --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/bean/TxTransactionLocal.java @@ -0,0 +1,180 @@ +package com.codingapi.tx.aop.bean; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tx.annotation.TxTransactionMode; +import com.codingapi.tx.framework.utils.SocketManager; +import com.codingapi.tx.model.Request; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 分布式事务远程调用控制对象 + * Created by lorne on 2017/6/5. + */ +public class TxTransactionLocal { + + private Logger logger = LoggerFactory.getLogger(TxTransactionLocal.class); + + private final static ThreadLocal currentLocal = new InheritableThreadLocal(); + + private String groupId; + + private int maxTimeOut; + + private Map cacheModelInfo = new ConcurrentHashMap<>(); + + /** + * 是否同一个模块被多次请求 + */ + private boolean hasIsGroup = false; + + /** + * 是否是发起方模块 + */ + private boolean hasStart = false; + + /** + * 时候已经获取到连接对象 + */ + private boolean hasConnection = false; + + + private String kid; + + private String type; + + private boolean readOnly = false; + + private TxTransactionMode mode; + + public boolean isHasIsGroup() { + return hasIsGroup; + } + + public void setHasIsGroup(boolean hasIsGroup) { + this.hasIsGroup = hasIsGroup; + } + + public String getKid() { + return kid; + } + + public void setKid(String kid) { + this.kid = kid; + } + + public boolean isHasStart() { + return hasStart; + } + + public void setHasStart(boolean hasStart) { + this.hasStart = hasStart; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public boolean isHasConnection() { + return hasConnection; + } + + public void setHasConnection(boolean hasConnection) { + this.hasConnection = hasConnection; + } + + + public int getMaxTimeOut() { + return maxTimeOut; + } + + public void setMaxTimeOut(int maxTimeOut) { + this.maxTimeOut = maxTimeOut; + } + + + public static TxTransactionLocal current() { + return currentLocal.get(); + } + + public static void setCurrent(TxTransactionLocal current) { + currentLocal.set(current); + } + + public TxTransactionMode getMode() { + return mode; + } + + public void setMode(TxTransactionMode mode) { + this.mode = mode; + } + + public void putLoadBalance(String key, String data){ + cacheModelInfo.put(key,data); + //与TxManager通讯 + JSONObject jsonObject = new JSONObject(); + jsonObject.put("g", getGroupId()); + jsonObject.put("k", key); + jsonObject.put("d", data); + logger.debug("putLoadBalance--> start "); + Request request = new Request("plb", jsonObject.toString()); + SocketManager.getInstance().sendMsg(request); + logger.debug("putLoadBalance--> end"); + } + + + public String getLoadBalance(String key){ + String old = cacheModelInfo.get(key); + logger.debug("cacheModelInfo->"+old); + if(old==null){ + //与TxManager通讯 + logger.debug("getLoadBalance--> start"); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("g", getGroupId()); + jsonObject.put("k", key); + Request request = new Request("glb", jsonObject.toString()); + String json = SocketManager.getInstance().sendMsg(request); + logger.debug("getLoadBalance--> end ,res - >" + json); + if(StringUtils.isNotEmpty(json)){ + return json; + } + } + return old; + } + + + public void setType(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public boolean isReadOnly() { + return readOnly; + } + + public void setReadOnly(boolean readOnly) { + this.readOnly = readOnly; + } + + public static boolean isInTxcTransaction() { + TxTransactionLocal local = current(); + if (local != null + && local.mode != null + && local.mode == TxTransactionMode.TX_MODE_TXC + && !local.isReadOnly()) { + return true; + } + return false; + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/AspectBeforeService.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/AspectBeforeService.java new file mode 100644 index 0000000..f8dc69d --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/AspectBeforeService.java @@ -0,0 +1,11 @@ +package com.codingapi.tx.aop.service; + +import org.aspectj.lang.ProceedingJoinPoint; + +/** + * Created by lorne on 2017/7/1. + */ +public interface AspectBeforeService { + + Object around(String groupId, ProceedingJoinPoint point, String mode) throws Throwable; +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/TransactionServer.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/TransactionServer.java new file mode 100644 index 0000000..6350342 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/TransactionServer.java @@ -0,0 +1,17 @@ +package com.codingapi.tx.aop.service; + +import com.codingapi.tx.aop.bean.TxTransactionInfo; +import org.aspectj.lang.ProceedingJoinPoint; + + +/** + * Created by lorne on 2017/6/8. + */ +public interface TransactionServer { + + + // void execute(); + + Object execute(ProceedingJoinPoint point, TxTransactionInfo info) throws Throwable; + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/TransactionServerFactoryService.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/TransactionServerFactoryService.java new file mode 100644 index 0000000..3dbe121 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/TransactionServerFactoryService.java @@ -0,0 +1,11 @@ +package com.codingapi.tx.aop.service; + +import com.codingapi.tx.aop.bean.TxTransactionInfo; + +/** + * Created by lorne on 2017/6/8. + */ +public interface TransactionServerFactoryService { + + TransactionServer createTransactionServer(TxTransactionInfo info) throws Throwable; +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/AspectBeforeServiceImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/AspectBeforeServiceImpl.java new file mode 100644 index 0000000..7bc860b --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/AspectBeforeServiceImpl.java @@ -0,0 +1,60 @@ +package com.codingapi.tx.aop.service.impl; + +import com.codingapi.tx.annotation.TxTransaction; +import com.codingapi.tx.annotation.TxTransactionMode; +import com.codingapi.tx.aop.bean.TxTransactionInfo; +import com.codingapi.tx.aop.bean.TxTransactionLocal; +import com.codingapi.tx.aop.service.AspectBeforeService; +import com.codingapi.tx.aop.service.TransactionServer; +import com.codingapi.tx.aop.service.TransactionServerFactoryService; +import com.codingapi.tx.model.TransactionInvocation; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.lang.reflect.Method; + +/** + * Created by lorne on 2017/7/1. + */ +@Service +public class AspectBeforeServiceImpl implements AspectBeforeService { + + @Autowired + private TransactionServerFactoryService transactionServerFactoryService; + + + private Logger logger = LoggerFactory.getLogger(AspectBeforeServiceImpl.class); + + @Override + public Object around(String groupId, ProceedingJoinPoint point, String mode) throws Throwable { + + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + Class clazz = point.getTarget().getClass(); + Object[] args = point.getArgs(); + Method thisMethod = clazz.getMethod(method.getName(), method.getParameterTypes()); + + TxTransaction transaction = thisMethod.getAnnotation(TxTransaction.class); + + TxTransactionLocal txTransactionLocal = TxTransactionLocal.current(); + + logger.debug("around--> groupId-> " +groupId+",txTransactionLocal->"+txTransactionLocal); + + TransactionInvocation invocation = new TransactionInvocation(clazz, thisMethod.getName(), thisMethod.toString(), args, method.getParameterTypes()); + + TxTransactionInfo info = new TxTransactionInfo(transaction,txTransactionLocal,invocation,groupId); + try { + info.setMode(TxTransactionMode.valueOf(mode)); + } catch (Exception e) { + info.setMode(TxTransactionMode.TX_MODE_LCN); + } + + TransactionServer server = transactionServerFactoryService.createTransactionServer(info); + + return server.execute(point, info); + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TransactionServerFactoryServiceImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TransactionServerFactoryServiceImpl.java new file mode 100644 index 0000000..3f6fb01 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TransactionServerFactoryServiceImpl.java @@ -0,0 +1,95 @@ +package com.codingapi.tx.aop.service.impl; + + +import com.codingapi.tx.aop.bean.TxTransactionInfo; +import com.codingapi.tx.aop.service.TransactionServer; +import com.codingapi.tx.aop.service.TransactionServerFactoryService; +import com.codingapi.tx.datasource.ILCNTransactionControl; +import com.codingapi.tx.framework.utils.SocketManager; +import com.codingapi.tx.netty.service.NettyService; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + + + +/** + * Created by lorne on 2017/6/8. + */ +@Service +public class TransactionServerFactoryServiceImpl implements TransactionServerFactoryService { + + private Logger logger = LoggerFactory.getLogger(TransactionServerFactoryServiceImpl.class); + + @Autowired + private TransactionServer txStartTransactionServer; + + @Autowired + private TransactionServer txRunningTransactionServer; + + @Autowired + private TransactionServer txDefaultTransactionServer; + + @Autowired + private TransactionServer txRunningNoTransactionServer; + + @Autowired + private NettyService nettyService; + + @Autowired + private ILCNTransactionControl transactionControl; + + + public TransactionServer createTransactionServer(TxTransactionInfo info) throws Throwable { + + if (!SocketManager.getInstance().isNetState()) { + logger.warn("tx-manager not connected."); + return txDefaultTransactionServer; + } + + /*********分布式事务处理逻辑*开始***********/ + + /** 尽当Transaction注解不为空,其他都为空时。表示分布式事务开始启动 **/ + if (info.getTransaction() != null && info.getTransaction().isStart() && info.getTxTransactionLocal() == null && StringUtils.isEmpty(info.getTxGroupId())) { + //检查socket通讯是否正常 (当启动事务的主业务方法执行完以后,再执行其他业务方法时将进入txInServiceTransactionServer业务处理) + if (SocketManager.getInstance().isNetState()) { + return txStartTransactionServer; + } else { + logger.warn("tx-manager not connected."); + return txDefaultTransactionServer; + } + } + + + /** 分布式事务已经开启,业务进行中 **/ + if (info.getTxTransactionLocal() != null || StringUtils.isNotEmpty(info.getTxGroupId())) { + //检查socket通讯是否正常 (第一次执行时启动txRunningTransactionServer的业务处理控制,然后嵌套调用其他事务的业务方法时都并到txInServiceTransactionServer业务处理下) + if (SocketManager.getInstance().isNetState()) { + if (info.getTxTransactionLocal() != null) { + return txDefaultTransactionServer; + } else { + /*if(transactionControl.isNoTransactionOperation() // 表示整个应用没有获取过DB连接 + || info.getTransaction().readOnly()) { //无事务业务的操作 + return txRunningNoTransactionServer; + }else { + return txRunningTransactionServer; + }*/ + //TODO 有事务业务的操作 + if(!transactionControl.isNoTransactionOperation()) { + return txRunningTransactionServer; + }else { + return txRunningNoTransactionServer; + } + } + } else { + logger.warn("tx-manager not connected."); + return txDefaultTransactionServer; + } + } + /*********分布式事务处理逻辑*结束***********/ + + return txDefaultTransactionServer; + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxDefaultTransactionServerImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxDefaultTransactionServerImpl.java new file mode 100644 index 0000000..7dca225 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxDefaultTransactionServerImpl.java @@ -0,0 +1,20 @@ +package com.codingapi.tx.aop.service.impl; + +import com.codingapi.tx.aop.bean.TxTransactionInfo; +import com.codingapi.tx.aop.service.TransactionServer; +import org.aspectj.lang.ProceedingJoinPoint; +import org.springframework.stereotype.Service; + +/** + * Created by lorne on 2017/6/8. + */ +@Service(value = "txDefaultTransactionServer") +public class TxDefaultTransactionServerImpl implements TransactionServer { + + + + @Override + public Object execute(ProceedingJoinPoint point, TxTransactionInfo info) throws Throwable { + return point.proceed(); + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningNoTransactionServerImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningNoTransactionServerImpl.java new file mode 100644 index 0000000..b38579a --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningNoTransactionServerImpl.java @@ -0,0 +1,52 @@ +package com.codingapi.tx.aop.service.impl; + +import com.codingapi.tx.Constants; +import com.codingapi.tx.aop.bean.TxTransactionInfo; +import com.codingapi.tx.aop.bean.TxTransactionLocal; +import com.codingapi.tx.aop.service.TransactionServer; +import com.lorne.core.framework.utils.KidUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +/** + * 分布式事务启动参与事务中的业务处理(无事务模块) + * Created by lorne on 2017/6/8. + */ +@Service(value = "txRunningNoTransactionServer") +public class TxRunningNoTransactionServerImpl implements TransactionServer { + + + private Logger logger = LoggerFactory.getLogger(TxRunningNoTransactionServerImpl.class); + + @Override + public Object execute(final ProceedingJoinPoint point, final TxTransactionInfo info) throws Throwable { + + String kid = KidUtils.generateShortUuid(); + String txGroupId = info.getTxGroupId(); + logger.debug("--->begin readonly transaction, groupId: " + txGroupId); + long t1 = System.currentTimeMillis(); + + + TxTransactionLocal txTransactionLocal = new TxTransactionLocal(); + txTransactionLocal.setGroupId(txGroupId); + txTransactionLocal.setHasStart(false); + txTransactionLocal.setKid(kid); + txTransactionLocal.setMaxTimeOut(Constants.txServer.getCompensateMaxWaitTime()); + txTransactionLocal.setMode(info.getMode()); + txTransactionLocal.setReadOnly(true); + TxTransactionLocal.setCurrent(txTransactionLocal); + + try { + return point.proceed(); + } catch (Throwable e) { + throw e; + } finally { + TxTransactionLocal.setCurrent(null); + long t2 = System.currentTimeMillis(); + logger.debug("<---end readonly transaction,groupId:" + txGroupId+",execute time:"+(t2-t1)); + } + } + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningTransactionServerImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningTransactionServerImpl.java new file mode 100644 index 0000000..24e81e4 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxRunningTransactionServerImpl.java @@ -0,0 +1,120 @@ +package com.codingapi.tx.aop.service.impl; + +import com.codingapi.tx.Constants; +import com.codingapi.tx.aop.bean.TxTransactionInfo; +import com.codingapi.tx.model.TxGroup; +import com.lorne.core.framework.exception.ServiceException; +import com.lorne.core.framework.utils.KidUtils; +import com.codingapi.tx.aop.bean.TxTransactionLocal; +import com.codingapi.tx.datasource.ILCNTransactionControl; +import com.codingapi.tx.framework.task.TaskGroupManager; +import com.codingapi.tx.framework.task.TxTask; +import com.codingapi.tx.netty.service.MQTxManagerService; +import com.codingapi.tx.aop.service.TransactionServer; +import org.aspectj.lang.ProceedingJoinPoint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.concurrent.TimeUnit; + +/** + * 分布式事务启动参与事务中的业务处理 + * Created by lorne on 2017/6/8. + */ +@Service(value = "txRunningTransactionServer") +public class TxRunningTransactionServerImpl implements TransactionServer { + + + @Autowired + private MQTxManagerService txManagerService; + + + @Autowired + private ILCNTransactionControl transactionControl; + + + private Logger logger = LoggerFactory.getLogger(TxRunningTransactionServerImpl.class); + + @Override + public Object execute(final ProceedingJoinPoint point, final TxTransactionInfo info) throws Throwable { + + String kid = KidUtils.generateShortUuid(); + String txGroupId = info.getTxGroupId(); + logger.debug("--->begin running transaction,groupId:" + txGroupId); + long t1 = System.currentTimeMillis(); + + boolean isHasIsGroup = transactionControl.hasGroup(txGroupId); + + + TxTransactionLocal txTransactionLocal = new TxTransactionLocal(); + txTransactionLocal.setGroupId(txGroupId); + txTransactionLocal.setHasStart(false); + txTransactionLocal.setKid(kid); + txTransactionLocal.setHasIsGroup(isHasIsGroup); + txTransactionLocal.setMaxTimeOut(Constants.txServer.getCompensateMaxWaitTime()); + txTransactionLocal.setMode(info.getMode()); + TxTransactionLocal.setCurrent(txTransactionLocal); + + + try { + + Object res = point.proceed(); + + //写操作 处理 + if(!txTransactionLocal.isReadOnly()) { + + String methodStr = info.getInvocation().getMethodStr(); + + TxGroup resTxGroup = txManagerService.addTransactionGroup(txGroupId, kid, isHasIsGroup, methodStr); + + //已经进入过该模块的,不再执行此方法 + if(!isHasIsGroup) { + String type = txTransactionLocal.getType(); + + TxTask waitTask = TaskGroupManager.getInstance().getTask(kid, type); + + //lcn 连接已经开始等待时. + while (waitTask != null && !waitTask.isAwait()) { + TimeUnit.MILLISECONDS.sleep(1); + } + + if (resTxGroup == null) { + + //通知业务回滚事务 + if (waitTask != null) { + //修改事务组状态异常 + waitTask.setState(-1); + waitTask.signalTask(); + throw new ServiceException("update TxGroup error, groupId:" + txGroupId); + } + } + } + } + + return res; + } catch (Throwable e) { + // 这里处理以下情况:当 point.proceed() 业务代码中 db事务正常提交,开始等待,后续处理发生异常。 + // 由于没有加入事务组,不会收到通知。这里唤醒并回滚 + if(!isHasIsGroup) { + String type = txTransactionLocal.getType(); + TxTask waitTask = TaskGroupManager.getInstance().getTask(kid, type); + // 有一定几率不能唤醒: wait的代码是在另一个线程,有可能线程还没执行到wait,先执行到了这里 + // TODO 要不要 sleep 1毫秒 + logger.warn("wake the waitTask: {}", (waitTask != null && waitTask.isAwait())); + if (waitTask != null && waitTask.isAwait()) { + waitTask.setState(-1); + waitTask.signalTask(); + } + } + throw e; + } finally { + TxTransactionLocal.setCurrent(null); + long t2 = System.currentTimeMillis(); + logger.debug("<---end running transaction,groupId:" + txGroupId+",execute time:"+(t2-t1)); + + } + } + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxStartTransactionServerImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxStartTransactionServerImpl.java new file mode 100644 index 0000000..69d90bf --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/aop/service/impl/TxStartTransactionServerImpl.java @@ -0,0 +1,154 @@ +package com.codingapi.tx.aop.service.impl; + +import com.codingapi.tx.Constants; +import com.codingapi.tx.aop.bean.TxCompensateLocal; +import com.codingapi.tx.aop.bean.TxTransactionInfo; +import com.codingapi.tx.aop.bean.TxTransactionLocal; +import com.codingapi.tx.aop.service.TransactionServer; +import com.codingapi.tx.framework.task.TaskGroupManager; +import com.codingapi.tx.framework.task.TaskState; +import com.codingapi.tx.framework.task.TxTask; +import com.codingapi.tx.netty.service.MQTxManagerService; +import com.lorne.core.framework.utils.KidUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 分布式事务启动开始时的业务处理 + * Created by lorne on 2017/6/8. + */ +@Service(value = "txStartTransactionServer") +public class TxStartTransactionServerImpl implements TransactionServer { + + + private Logger logger = LoggerFactory.getLogger(TxStartTransactionServerImpl.class); + + + @Autowired + protected MQTxManagerService txManagerService; + + + @Override + public Object execute(ProceedingJoinPoint point,final TxTransactionInfo info) throws Throwable { + //分布式事务开始执行 + + logger.debug("--->begin start transaction"); + + final long start = System.currentTimeMillis(); + + int state = 0; + + final String groupId = TxCompensateLocal.current()==null?KidUtils.generateShortUuid():TxCompensateLocal.current().getGroupId(); + + //创建事务组 + txManagerService.createTransactionGroup(groupId); + + + TxTransactionLocal txTransactionLocal = new TxTransactionLocal(); + txTransactionLocal.setGroupId(groupId); + txTransactionLocal.setHasStart(true); + txTransactionLocal.setMaxTimeOut(Constants.txServer.getCompensateMaxWaitTime()); + txTransactionLocal.setMode(info.getTransaction().mode()); + txTransactionLocal.setReadOnly(info.getTransaction().readOnly()); + TxTransactionLocal.setCurrent(txTransactionLocal); + + + try { + Object obj = point.proceed(); + state = 1; + return obj; + } catch (Throwable e) { + state = rollbackException(info,e); + throw e; + } finally { + + final String type = txTransactionLocal.getType(); + + int rs = txManagerService.closeTransactionGroup(groupId, state); + + int lastState = rs==-1?0:state; + + int executeConnectionError = 0; + + //控制本地事务的数据提交 + final TxTask waitTask = TaskGroupManager.getInstance().getTask(groupId, type); + if(waitTask!=null){ + waitTask.setState(lastState); + waitTask.signalTask(); + + while (!waitTask.isRemove()){ + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + if(waitTask.getState()== TaskState.connectionError.getCode()){ + //本地执行失败. + executeConnectionError = 1; + + lastState = 0; + } + } + + final TxCompensateLocal compensateLocal = TxCompensateLocal.current(); + + if (compensateLocal == null) { + long end = System.currentTimeMillis(); + long time = end - start; + if ((executeConnectionError == 1&&rs == 1)||(lastState == 1 && rs == 0)) { + //记录补偿日志 + txManagerService.sendCompensateMsg(groupId, time, info,executeConnectionError); + } + }else{ + if(rs==1){ + lastState = 1; + }else{ + lastState = 0; + } + } + + TxTransactionLocal.setCurrent(null); + logger.debug("<---end start transaction"); + logger.debug("start transaction over, res -> groupId:" + groupId + ", now state:" + (lastState == 1 ? "commit" : "rollback")); + + } + } + + + private int rollbackException(TxTransactionInfo info,Throwable throwable){ + + //spring 事务机制默认回滚异常. + if(RuntimeException.class.isAssignableFrom(throwable.getClass())){ + return 0; + } + + if(Error.class.isAssignableFrom(throwable.getClass())){ + return 0; + } + + //回滚异常检测. + for(Class rollbackFor:info.getTransaction().rollbackFor()){ + + //存在关系 + if(rollbackFor.isAssignableFrom(throwable.getClass())){ + return 0; + } + + } + + //不回滚异常检测. + for(Class rollbackFor:info.getTransaction().noRollbackFor()){ + + //存在关系 + if(rollbackFor.isAssignableFrom(throwable.getClass())){ + return 1; + } + } + return 1; + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/model/CompensateInfo.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/model/CompensateInfo.java new file mode 100644 index 0000000..9fafc32 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/model/CompensateInfo.java @@ -0,0 +1,147 @@ +package com.codingapi.tx.compensate.model; + +/** + * create by lorne on 2017/11/13 + */ +public class CompensateInfo { + + + private long currentTime; + private String modelName; + private String uniqueKey; + private String data; + private String methodStr; + private String className; + private String groupId; + private String address; + private long time; + private String resJson; + private int startError; + + private int state; + + + public String toParamsString() { + String postParam = "model=" + modelName + "&uniqueKey=" + uniqueKey + "" + + "&address=" + address + "¤tTime=" + currentTime + + "&data=" + data + "&time=" + time + "&groupId=" + groupId + "" + + "&className=" + className + "&methodStr=" + methodStr+"&startError="+startError; + return postParam; + } + + public CompensateInfo() { + } + + public CompensateInfo(long currentTime, String modelName, String uniqueKey, String data, + String methodStr, String className, String groupId, String address, + long time,int startError) { + this.currentTime = currentTime; + this.modelName = modelName; + this.uniqueKey = uniqueKey; + this.data = data; + this.methodStr = methodStr; + this.className = className; + this.groupId = groupId; + this.address = address; + this.time = time; + this.state = 0; + this.startError =startError; + } + + + public int getStartError() { + return startError; + } + + public void setStartError(int startError) { + this.startError = startError; + } + + public int getState() { + return state; + } + + public void setState(int state) { + this.state = state; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public long getTime() { + return time; + } + + public void setTime(long time) { + this.time = time; + } + + public long getCurrentTime() { + return currentTime; + } + + public void setCurrentTime(long currentTime) { + this.currentTime = currentTime; + } + + public String getModelName() { + return modelName; + } + + public void setModelName(String modelName) { + this.modelName = modelName; + } + + public String getUniqueKey() { + return uniqueKey; + } + + public void setUniqueKey(String uniqueKey) { + this.uniqueKey = uniqueKey; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getMethodStr() { + return methodStr; + } + + public void setMethodStr(String methodStr) { + this.methodStr = methodStr; + } + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String getResJson() { + return resJson; + } + + public void setResJson(String resJson) { + this.resJson = resJson; + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/package-info.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/package-info.java new file mode 100644 index 0000000..be617b2 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/package-info.java @@ -0,0 +1,6 @@ +/** + * 补偿模块业务逻辑 + * + * create by lorne on 2017/11/11 + */ +package com.codingapi.tx.compensate; diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/service/CompensateService.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/service/CompensateService.java new file mode 100644 index 0000000..6ebb505 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/service/CompensateService.java @@ -0,0 +1,17 @@ +package com.codingapi.tx.compensate.service; + + +import com.codingapi.tx.compensate.model.CompensateInfo; +import com.codingapi.tx.model.TransactionInvocation; + +/** + * create by lorne on 2017/11/11 + */ +public interface CompensateService { + + + void saveLocal(CompensateInfo compensateInfo); + + boolean invoke(TransactionInvocation invocation, String groupId, int startState); + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/service/impl/CompensateServiceImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/service/impl/CompensateServiceImpl.java new file mode 100644 index 0000000..3da00d0 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/compensate/service/impl/CompensateServiceImpl.java @@ -0,0 +1,49 @@ +package com.codingapi.tx.compensate.service.impl; + +import com.alibaba.fastjson.JSON; +import com.codingapi.tx.aop.bean.TxCompensateLocal; +import com.codingapi.tx.compensate.model.CompensateInfo; +import com.codingapi.tx.compensate.service.CompensateService; +import com.codingapi.tx.framework.utils.MethodUtils; +import com.codingapi.tx.model.TransactionInvocation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; + +/** + * create by lorne on 2017/11/11 + */ +@Service +public class CompensateServiceImpl implements CompensateService { + + + @Autowired + private ApplicationContext spring; + + + private Logger logger = LoggerFactory.getLogger(CompensateServiceImpl.class); + + @Override + public void saveLocal(CompensateInfo compensateInfo) { + String json = JSON.toJSONString(compensateInfo); + logger.info("local-compensate-logs->" + json); + } + + @Override + public boolean invoke(TransactionInvocation invocation, String groupId, int startState) { + + TxCompensateLocal compensateLocal = new TxCompensateLocal(); + compensateLocal.setGroupId(groupId); + compensateLocal.setStartState(startState); + + TxCompensateLocal.setCurrent(compensateLocal); + + boolean res = MethodUtils.invoke(spring, invocation); + + TxCompensateLocal.setCurrent(null); + + return res; + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/config/ConfigReader.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/config/ConfigReader.java new file mode 100644 index 0000000..2c91f22 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/config/ConfigReader.java @@ -0,0 +1,32 @@ +package com.codingapi.tx.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +/** + * create by lorne on 2017/11/13 + */ +@Component +public class ConfigReader { + + + private Logger logger = LoggerFactory.getLogger(ConfigReader.class); + + @Autowired + private ApplicationContext spring; + + @Value("${tm.manager.url}") + private String managerUrl; + + + public String getTxUrl() { + logger.debug("load tx-manager:" + managerUrl); + return managerUrl; + } + + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/LCNTransactionAspectSupport.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/LCNTransactionAspectSupport.java new file mode 100644 index 0000000..915c4e8 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/LCNTransactionAspectSupport.java @@ -0,0 +1,53 @@ +package com.codingapi.tx.control; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tx.aop.bean.TxTransactionLocal; +import com.codingapi.tx.framework.utils.SocketManager; +import com.codingapi.tx.model.Request; +import org.apache.commons.lang.StringUtils; + +/** + * create by lorne on 2017/12/5 + */ +public class LCNTransactionAspectSupport { + + + + private static LCNTransactionAspectSupport instance = null; + + private LCNTransactionAspectSupport(){} + + public static LCNTransactionAspectSupport currentTransactionStatus() { + if (instance == null) { + synchronized (LCNTransactionAspectSupport.class) { + if(instance==null){ + instance = new LCNTransactionAspectSupport(); + } + } + } + return instance; + } + + + public boolean setRollbackOnly(){ + TxTransactionLocal txTransactionLocal = TxTransactionLocal.current(); + if(txTransactionLocal==null){ + return false; + } + + if(StringUtils.isEmpty(txTransactionLocal.getGroupId())){ + return false; + } + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("g", txTransactionLocal.getGroupId()); + Request request = new Request("rg", jsonObject.toString()); + String json = SocketManager.getInstance().sendMsg(request); + try { + return Integer.parseInt(json)==1; + }catch (Exception e){ + return false; + } + } + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/IActionService.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/IActionService.java new file mode 100644 index 0000000..32c91fd --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/IActionService.java @@ -0,0 +1,12 @@ +package com.codingapi.tx.control.service; + +import com.alibaba.fastjson.JSONObject; + +/** + * create by lorne on 2017/11/13 + */ +public interface IActionService { + + String execute(JSONObject resObj, String json); + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/TransactionControlService.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/TransactionControlService.java new file mode 100644 index 0000000..0ccc096 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/TransactionControlService.java @@ -0,0 +1,13 @@ +package com.codingapi.tx.control.service; + +import com.alibaba.fastjson.JSONObject; +import io.netty.channel.ChannelHandlerContext; + +/** + * create by lorne on 2017/11/11 + */ +public interface TransactionControlService { + + + void notifyTransactionMsg(ChannelHandlerContext ctx, JSONObject resObj, String json); +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/impl/ActionCServiceImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/impl/ActionCServiceImpl.java new file mode 100644 index 0000000..e3ff8d2 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/impl/ActionCServiceImpl.java @@ -0,0 +1,64 @@ +package com.codingapi.tx.control.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tx.compensate.service.CompensateService; +import com.codingapi.tx.control.service.IActionService; +import com.codingapi.tx.framework.utils.SerializerUtils; +import com.codingapi.tx.model.TransactionInvocation; +import com.lorne.core.framework.utils.encode.Base64Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 通知补偿 + * create by lorne on 2017/11/13 + */ +@Service(value = "c") +public class ActionCServiceImpl implements IActionService { + + + private Logger logger = LoggerFactory.getLogger(ActionCServiceImpl.class); + + + @Autowired + private CompensateService compensateService; + + @Override + public String execute(JSONObject resObj, String json) { + + String cmd = resObj.toJSONString(); + + logger.debug("accept compensate data ->" + cmd); + + + String data = resObj.getString("d"); + + String groupId = resObj.getString("g"); + + int startState = resObj.getInteger("ss"); + + byte[] bytes = Base64Utils.decode(data); + + TransactionInvocation invocation = SerializerUtils.parserTransactionInvocation(bytes); + + if (invocation != null) { + logger.info("compensate method ->" + invocation.getMethodStr()); + + boolean res = compensateService.invoke(invocation, groupId,startState); + + logger.info("compensate res ->" + res); + + if (res) { + return "1"; + } else { + return "0"; + } + } + + return "-1"; + } + + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/impl/ActionTServiceImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/impl/ActionTServiceImpl.java new file mode 100644 index 0000000..98d74ae --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/impl/ActionTServiceImpl.java @@ -0,0 +1,104 @@ +package com.codingapi.tx.control.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tx.control.service.IActionService; +import com.codingapi.tx.datasource.ILCNTransactionControl; +import com.codingapi.tx.framework.task.TaskGroup; +import com.codingapi.tx.framework.task.TaskGroupManager; +import com.codingapi.tx.framework.task.TaskState; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 通知提交 + * create by lorne on 2017/11/13 + */ +@Service(value = "t") +public class ActionTServiceImpl implements IActionService { + + + private Logger logger = LoggerFactory.getLogger(ActionTServiceImpl.class); + + @Autowired + private ILCNTransactionControl transactionControl; + + @Override + public String execute(JSONObject resObj, String json) { + String res; + //通知提醒 + final int state = resObj.getInteger("c"); + String taskId = resObj.getString("t"); + if(transactionControl.executeTransactionOperation()) { + TaskGroup task = TaskGroupManager.getInstance().getTaskGroup(taskId); + logger.info("accept notify data ->" + json); + if (task != null) { + if (task.isAwait()) { //已经等待 + res = notifyWaitTask(task, state); + } else { + int index = 0; + while (true) { + if (index > 500) { + res = "0"; + break; + } + if (task.isAwait()) { //已经等待 + res = notifyWaitTask(task, state); + break; + } + index++; + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } else { + res = "0"; + } + }else{ + //非事务操作 + res = "1"; + transactionControl.autoNoTransactionOperation(); + } + logger.info("accept notify response res ->" + res); + return res; + } + + private String notifyWaitTask(TaskGroup task, int state) { + String res; + task.setState(state); + task.signalTask(); + int count = 0; + + while (true) { + if (task.isRemove()) { + + if (task.getState() == TaskState.rollback.getCode() + || task.getState() == TaskState.commit.getCode()) { + + res = "1"; + } else { + res = "0"; + } + break; + } + if (count > 1000) { + //已经通知了,有可能失败. + res = "2"; + break; + } + + count++; + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + return res; + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/impl/TransactionControlServiceImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/impl/TransactionControlServiceImpl.java new file mode 100644 index 0000000..4702163 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/control/service/impl/TransactionControlServiceImpl.java @@ -0,0 +1,52 @@ +package com.codingapi.tx.control.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tx.control.service.IActionService; +import com.codingapi.tx.control.service.TransactionControlService; +import com.codingapi.tx.framework.utils.SocketUtils; +import io.netty.channel.ChannelHandlerContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; + +/** + * create by lorne on 2017/11/11 + */ +@Service +public class TransactionControlServiceImpl implements TransactionControlService{ + + private Logger logger = LoggerFactory.getLogger(TransactionControlServiceImpl.class); + + + @Autowired + private ApplicationContext spring; + + + + @Override + public void notifyTransactionMsg(ChannelHandlerContext ctx,JSONObject resObj, String json) { + + + String action = resObj.getString("a"); + String key = resObj.getString("k"); + + IActionService actionService = spring.getBean(action, IActionService.class); + + String res = actionService.execute(resObj, json); + + + JSONObject data = new JSONObject(); + data.put("k", key); + data.put("a", action); + + JSONObject params = new JSONObject(); + params.put("d", res); + data.put("p", params); + + SocketUtils.sendMsg(ctx, data.toString()); + + logger.info("send notify data ->" + data.toString()); + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/AbstractResourceProxy.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/AbstractResourceProxy.java new file mode 100644 index 0000000..69d21f1 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/AbstractResourceProxy.java @@ -0,0 +1,191 @@ +package com.codingapi.tx.datasource; + + +import com.codingapi.tx.annotation.TxTransactionMode; +import com.codingapi.tx.aop.bean.TxTransactionLocal; +import com.codingapi.tx.datasource.service.DataSourceService; +import com.lorne.core.framework.utils.task.Task; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * create by lorne on 2017/8/22 + */ + +public abstract class AbstractResourceProxy implements ILCNTransactionControl { + + + protected Map pools = new ConcurrentHashMap<>(); + + + private Logger logger = LoggerFactory.getLogger(AbstractResourceProxy.class); + + + @Autowired + protected DataSourceService dataSourceService; + + + //default size + protected volatile int maxCount = 5; + + //default time (seconds) + protected int maxWaitTime = 30; + + protected volatile int nowCount = 0; + + + protected volatile boolean hasTransaction = false; + + private volatile boolean isNoTransaction = false; + + + + // not thread + protected ICallClose subNowCount = new ICallClose() { + + @Override + public void close(ILCNResource connection) { + Task waitTask = connection.getWaitTask(); + if (waitTask != null) { + if (!waitTask.isRemove()) { + waitTask.remove(); + } + } + + pools.remove(connection.getGroupId()); + nowCount--; + } + }; + + + protected abstract C createLcnConnection(C connection, TxTransactionLocal txTransactionLocal); + + protected abstract C createTxcConnection(C connection, TxTransactionLocal txTransactionLocal); + + protected abstract void initDbType(); + + + + protected ILCNResource loadConnection(){ + + TxTransactionLocal txTransactionLocal = TxTransactionLocal.current(); + + if(txTransactionLocal==null){ + logger.debug("loadConnection -> null !"); + return null; + } + if (txTransactionLocal.isReadOnly()) { + logger.debug("readonly tx don't reuse connection."); + return null; + } + + //是否获取旧连接的条件:同一个模块下被多次调用时第一次的事务操作 + ILCNResource old = pools.get(txTransactionLocal.getGroupId()); + if (old != null) { + + if(txTransactionLocal.isHasConnection()){ + logger.debug("connection is had , transaction get a new connection ."); + return null; + } + + logger.debug("loadConnection -> old !"); + txTransactionLocal.setHasConnection(true); + return old; + } + return null; + } + + + private C createConnection(TxTransactionLocal txTransactionLocal, C connection){ + if (txTransactionLocal.getMode() != null + && txTransactionLocal.getMode() == TxTransactionMode.TX_MODE_TXC) { + // txc 模式下没有maxCount的限制 直接创建 + return createTxcConnection(connection, txTransactionLocal); + } + if (nowCount == maxCount) { + for (int i = 0; i < maxWaitTime; i++) { + for(int j=0;j<100;j++){ + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (nowCount < maxCount) { + return createLcnConnection(connection, txTransactionLocal); + } + } + } + } else if (nowCount < maxCount) { + return createLcnConnection(connection, txTransactionLocal); + } else { + logger.info("connection was overload"); + return null; + } + return connection; + } + + + + protected C initLCNConnection(C connection) { + logger.debug("initLCNConnection"); + C lcnConnection = connection; + TxTransactionLocal txTransactionLocal = TxTransactionLocal.current(); + + if (txTransactionLocal != null&&!txTransactionLocal.isHasConnection() + && !txTransactionLocal.isReadOnly()) { + + logger.debug("lcn datasource transaction control "); + + //补偿的情况的 +// if (TxCompensateLocal.current() != null) { +// logger.info("rollback transaction "); +// return getCompensateConnection(connection,TxCompensateLocal.current()); +// } + + if(StringUtils.isNotEmpty(txTransactionLocal.getGroupId())){ + + logger.debug("lcn transaction "); + return createConnection(txTransactionLocal, connection); + } + } + logger.debug("load default connection !"); + return lcnConnection; + } + + + @Override + public boolean hasGroup(String group){ + return pools.containsKey(group); + } + + + @Override + public boolean executeTransactionOperation() { + return hasTransaction; + } + + + @Override + public boolean isNoTransactionOperation() { + return isNoTransaction; + } + + @Override + public void autoNoTransactionOperation() { + isNoTransaction = true; + } + + public void setMaxWaitTime(int maxWaitTime) { + this.maxWaitTime = maxWaitTime; + } + + public void setMaxCount(int maxCount) { + this.maxCount = maxCount; + } + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ICallClose.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ICallClose.java new file mode 100644 index 0000000..cec97a3 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ICallClose.java @@ -0,0 +1,10 @@ +package com.codingapi.tx.datasource; + +/** + * create by lorne on 2017/8/22 + */ +public interface ICallClose { + + void close(T resource); + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ILCNConnection.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ILCNConnection.java new file mode 100644 index 0000000..5488b7f --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ILCNConnection.java @@ -0,0 +1,15 @@ +package com.codingapi.tx.datasource; + +import org.aspectj.lang.ProceedingJoinPoint; + +import java.sql.Connection; + +/** + * create by lorne on 2018/1/5 + */ +public interface ILCNConnection { + + Connection getConnection(ProceedingJoinPoint point) throws Throwable; + + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ILCNResource.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ILCNResource.java new file mode 100644 index 0000000..c4034e5 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ILCNResource.java @@ -0,0 +1,29 @@ +package com.codingapi.tx.datasource; + +import com.codingapi.tx.framework.task.TxTask; + +/** + * + * LCN 资源连接对象 + * + * + * create by lorne on 2017/8/22 + */ +public interface ILCNResource { + + + /** + * 用于关闭时检查是否未删除 + * @return TxTask任务对象 + */ + TxTask getWaitTask(); + + /** + * 事务组id + * @return 事务组Id + */ + String getGroupId(); + + + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ILCNTransactionControl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ILCNTransactionControl.java new file mode 100644 index 0000000..05b301b --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/ILCNTransactionControl.java @@ -0,0 +1,34 @@ +package com.codingapi.tx.datasource; + + +/** + * LCN 代理事务协调控制 + * create by lorne on 2017/9/6 + */ +public interface ILCNTransactionControl { + + /** + * 是否是同一个事务下 + * @param group 事务组id + * @return true是,false否 + */ + boolean hasGroup(String group); + + /** + * 有无执行过事务操作 + * @return true 有,false 否 + */ + boolean executeTransactionOperation(); + + + /** + * 是否没有事务操作 default false + * @return true 是 false 否 + */ + boolean isNoTransactionOperation(); + + /** + * 设置开启没有事务操作 + */ + void autoNoTransactionOperation(); +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/aspect/DataSourceAspect.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/aspect/DataSourceAspect.java new file mode 100644 index 0000000..b24db92 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/aspect/DataSourceAspect.java @@ -0,0 +1,42 @@ +package com.codingapi.tx.datasource.aspect; + +import com.codingapi.tx.datasource.ILCNConnection; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.sql.Connection; + +/** + * create by lorne on 2018/1/5 + */ + + +@Aspect +@Component +public class DataSourceAspect { + + private Logger logger = LoggerFactory.getLogger(DataSourceAspect.class); + + @Autowired + private ILCNConnection lcnConnection; + + + @Around("execution(* javax.sql.DataSource.getConnection(..))") + public Connection around(ProceedingJoinPoint point)throws Throwable{ + + logger.debug("getConnection-start---->"); + + Connection connection = lcnConnection.getConnection(point); + logger.debug("connection-->"+connection); + + logger.debug("getConnection-end---->"); + + return connection; + } + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/package-info.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/package-info.java new file mode 100644 index 0000000..ec4f845 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/package-info.java @@ -0,0 +1,6 @@ +/** + * 连接池 + * + * create by lorne on 2017/11/11 + */ +package com.codingapi.tx.datasource; diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/service/DataSourceService.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/service/DataSourceService.java new file mode 100644 index 0000000..78701f2 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/service/DataSourceService.java @@ -0,0 +1,15 @@ +package com.codingapi.tx.datasource.service; + +import com.lorne.core.framework.utils.task.Task; + + +/** + * create by lorne on 2017/7/29 + */ +public interface DataSourceService { + + + void schedule(String groupId, Task waitTask); + + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/service/impl/DataSourceServiceImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/service/impl/DataSourceServiceImpl.java new file mode 100644 index 0000000..93beced --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/datasource/service/impl/DataSourceServiceImpl.java @@ -0,0 +1,45 @@ +package com.codingapi.tx.datasource.service.impl; + +import com.codingapi.tx.datasource.service.DataSourceService; +import com.codingapi.tx.netty.service.MQTxManagerService; +import com.lorne.core.framework.utils.task.Task; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * create by lorne on 2017/7/29 + */ +@Service +public class DataSourceServiceImpl implements DataSourceService { + + + @Autowired + private MQTxManagerService txManagerService; + + + @Override + public void schedule(String groupId, Task waitTask) { + + + String waitTaskId = waitTask.getKey(); + int rs = txManagerService.cleanNotifyTransaction(groupId, waitTaskId); + if (rs == 1 || rs == 0) { + waitTask.setState(rs); + waitTask.signalTask(); + + return; + } + rs = txManagerService.cleanNotifyTransactionHttp(groupId, waitTaskId); + if (rs == 1 || rs == 0) { + waitTask.setState(rs); + waitTask.signalTask(); + + return; + } + + //添加到补偿队列 + waitTask.setState(-100); + waitTask.signalTask(); + + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TaskGroup.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TaskGroup.java new file mode 100644 index 0000000..b868f79 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TaskGroup.java @@ -0,0 +1,86 @@ +package com.codingapi.tx.framework.task; + + +import java.util.ArrayList; +import java.util.List; + +/** + * create by lorne on 2017/8/22 + */ +public class TaskGroup { + + private String key; + + private List tasks; + + private TxTask current; + + private int state; + + + public TxTask getCurrent() { + return current; + } + + public void setCurrent(TxTask current) { + this.current = current; + } + + public String getKey() { + return key; + } + + + public TaskGroup() { + tasks = new ArrayList<>(); + } + + public void setKey(String key) { + this.key = key; + } + + public List getTasks() { + return tasks; + } + + public void addTask(TxTask task) { + tasks.add(task); + } + + + public boolean isAwait(){ + for(TxTask task: getTasks()){ + if(!task.isAwait()){ + return false; + } + } + return true; + } + + public boolean isRemove(){ + for(TxTask task: getTasks()){ + if(!task.isRemove()){ + return false; + } + } + return true; + } + + + + public void signalTask(){ + for(TxTask task: getTasks()){ + task.setState(state); + task.signalTask(); + } + } + + + public int getState() { + return state; + } + + public void setState(int state) { + this.state = state; + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TaskGroupManager.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TaskGroupManager.java new file mode 100644 index 0000000..f8cac61 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TaskGroupManager.java @@ -0,0 +1,74 @@ +package com.codingapi.tx.framework.task; + +import com.lorne.core.framework.utils.task.ConditionUtils; +import org.apache.commons.lang.StringUtils; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * create by lorne on 2017/8/22 + */ +public class TaskGroupManager { + + private Map taskMap = new ConcurrentHashMap(); + + private static TaskGroupManager instance = null; + + private TaskGroupManager(){} + + public static TaskGroupManager getInstance() { + if (instance == null) { + synchronized (TaskGroupManager.class) { + if(instance==null){ + instance = new TaskGroupManager(); + } + } + } + return instance; + } + + public TaskGroup createTask(String key,String type) { + TaskGroup taskGroup = getTaskGroup(key); + if(taskGroup==null){ + taskGroup = new TaskGroup(); + } + taskGroup.setKey(key); + + String taskKey = type+"_"+key; + + TxTask task = new TxTask(ConditionUtils.getInstance().createTask(taskKey)); + taskGroup.setCurrent(task); + taskGroup.addTask(task); + taskMap.put(key, taskGroup); + return taskGroup; + } + + public TaskGroup getTaskGroup(String key) { + return taskMap.get(key); + } + + + public TxTask getTask(String key,String type) { + String taskKey = type+"_"+key; + TaskGroup txGroup = taskMap.get(key); + if(txGroup!=null){ + for(TxTask task:txGroup.getTasks()){ + if(taskKey.equals(task.getKey())){ + return task; + } + } + } + return null; + } + + + public void removeKey(String key) { + if (StringUtils.isNotEmpty(key)) { + taskMap.remove(key); + } + } + + + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TaskState.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TaskState.java new file mode 100644 index 0000000..0d05754 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TaskState.java @@ -0,0 +1,31 @@ +package com.codingapi.tx.framework.task; + +/** + * create by lorne on 2017/12/21 + */ +public enum TaskState { + + rollback(0),commit(1),networkError(-1),networkTimeOut(-2),connectionError(-3); + + + /** + * 数据状态: + * 1:commit + * 0:rollback + * -1:network error + * -2:network time out + * -3:execute Connection error + * @return state + */ + + private int code; + + TaskState(int code) { + this.code = code; + } + + + public int getCode() { + return code; + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TxTask.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TxTask.java new file mode 100644 index 0000000..6740e6b --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/task/TxTask.java @@ -0,0 +1,109 @@ +package com.codingapi.tx.framework.task; + +import com.lorne.core.framework.utils.task.IBack; +import com.lorne.core.framework.utils.task.Task; + +/** + * create by lorne on 2017/8/22 + */ +public class TxTask extends Task{ + + private Task task; + + + public TxTask(Task task) { + this.task = task; + } + + @Override + public boolean isNotify() { + return task.isNotify(); + } + + @Override + public boolean isRemove() { + return task.isRemove(); + } + + @Override + public boolean isAwait() { + return task.isAwait(); + } + + @Override + public int getState() { + return task.getState(); + } + + @Override + public void setState(int state) { + task.setState(state); + } + + @Override + public String getKey() { + return task.getKey(); + } + + @Override + public void setKey(String key) { + task.setKey(key); + } + + @Override + public IBack getBack() { + return task.getBack(); + } + + @Override + public void setBack(IBack back) { + task.setBack(back); + } + + @Override + public Object execute(IBack back) { + return task.execute(back); + } + + @Override + public void remove() { + task.remove(); + + boolean hasData = true;//true没有,false有 + + String groupKey = getKey().split("_")[1]; + TaskGroup taskGroup = TaskGroupManager.getInstance().getTaskGroup(groupKey); + for(TxTask task: taskGroup.getTasks()){ + if(!task.isRemove()){ + hasData = false; + } + } + + if(hasData){ + TaskGroupManager.getInstance().removeKey(groupKey); + } + + + + } + + @Override + public void signalTask() { + task.signalTask(); + } + + @Override + public void signalTask(IBack back) { + task.signalTask(back); + } + + @Override + public void awaitTask() { + task.awaitTask(); + } + + @Override + public void awaitTask(IBack back) { + task.awaitTask(back); + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/thread/HookRunnable.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/thread/HookRunnable.java new file mode 100644 index 0000000..1f27e44 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/thread/HookRunnable.java @@ -0,0 +1,49 @@ +package com.codingapi.tx.framework.thread; + +import com.codingapi.tx.Constants; + +import java.util.concurrent.TimeUnit; + +/** + * create by lorne on 2017/8/9 + */ +public abstract class HookRunnable implements Runnable { + + private volatile boolean hasOver; + + @Override + public void run() { + Thread thread = new Thread() { + @Override + public void run() { + Constants.hasExit = true; + while (!hasOver) { + try { + TimeUnit.MILLISECONDS.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + }; + if (!Constants.hasExit) { + Runtime.getRuntime().addShutdownHook(thread); + } else { + System.out.println("jvm has exit.."); + return; + } + try { + run0(); + } finally { + + hasOver = true; + + if (!thread.isAlive()) { + Runtime.getRuntime().removeShutdownHook(thread); + } + } + } + + public abstract void run0(); + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/thread/NamedThreadFactory.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/thread/NamedThreadFactory.java new file mode 100644 index 0000000..13bc4ab --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/thread/NamedThreadFactory.java @@ -0,0 +1,55 @@ +package com.codingapi.tx.framework.thread; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * [类描述] + * + * @author caican + * 17/9/12 + */ +public class NamedThreadFactory implements ThreadFactory +{ + private static final AtomicInteger POOL_SEQ = new AtomicInteger(1); + + private final AtomicInteger mThreadNum = new AtomicInteger(1); + + private final String mPrefix; + + private final boolean mDaemo; + + private final ThreadGroup mGroup; + + public NamedThreadFactory() + { + this("pool-" + POOL_SEQ.getAndIncrement(),false); + } + + public NamedThreadFactory(String prefix) + { + this(prefix,false); + } + + public NamedThreadFactory(String prefix, boolean daemo) + { + mPrefix = prefix + "-thread-"; + mDaemo = daemo; + SecurityManager s = System.getSecurityManager(); + mGroup = ( s == null ) ? Thread.currentThread().getThreadGroup() : s.getThreadGroup(); + } + + @Override + public Thread newThread(Runnable runnable) + { + String name = mPrefix + mThreadNum.getAndIncrement(); + Thread ret = new Thread(mGroup,runnable,name,0); + ret.setDaemon(mDaemo); + return ret; + } + + public ThreadGroup getThreadGroup() + { + return mGroup; + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/MethodUtils.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/MethodUtils.java new file mode 100644 index 0000000..b475ac8 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/MethodUtils.java @@ -0,0 +1,27 @@ +package com.codingapi.tx.framework.utils; + + +import com.codingapi.tx.model.TransactionInvocation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationContext; + +/** + * create by lorne on 2017/11/13 + */ +public class MethodUtils { + + private static final Logger logger = LoggerFactory.getLogger(MethodUtils.class); + + public static boolean invoke(ApplicationContext spring, TransactionInvocation invocation) { + try { + Object bean = spring.getBean(invocation.getTargetClazz()); + Object res = org.apache.commons.lang.reflect.MethodUtils.invokeMethod(bean, invocation.getMethod(), invocation.getArgumentValues(), invocation.getParameterTypes()); + logger.info("事务补偿执行---> className:" + invocation.getTargetClazz() + ",methodName::" + invocation.getMethod() + ",args:" + invocation.getArgumentValues() + ",res:" + res); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/SerializerUtils.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/SerializerUtils.java new file mode 100644 index 0000000..3d06004 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/SerializerUtils.java @@ -0,0 +1,38 @@ +package com.codingapi.tx.framework.utils; + +import com.lorne.core.framework.exception.SerializerException; + + +import com.codingapi.tx.model.TransactionInvocation; +import com.codingapi.tx.framework.utils.serializer.ISerializer; +import com.codingapi.tx.framework.utils.serializer.ProtostuffSerializer; + +/** + * create by lorne on 2017/8/3 + */ +public class SerializerUtils { + + + private static ISerializer serializer = new ProtostuffSerializer(); + + + public static byte[] serializeTransactionInvocation(TransactionInvocation invocation) { + try { + return serializer.serialize(invocation); + } catch (SerializerException e) { + e.printStackTrace(); + return null; + } + } + + + public static TransactionInvocation parserTransactionInvocation(byte[] value) { + try { + return serializer.deSerialize(value, TransactionInvocation.class); + } catch (SerializerException e) { + e.printStackTrace(); + return null; + } + } + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/SocketManager.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/SocketManager.java new file mode 100644 index 0000000..fa870bf --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/SocketManager.java @@ -0,0 +1,152 @@ +package com.codingapi.tx.framework.utils; + +import com.codingapi.tx.framework.thread.NamedThreadFactory; +import com.codingapi.tx.model.Request; +import com.lorne.core.framework.utils.task.ConditionUtils; +import com.lorne.core.framework.utils.task.IBack; +import com.lorne.core.framework.utils.task.Task; +import io.netty.channel.ChannelHandlerContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.*; + +/** + * create by lorne on 2017/11/11 + */ +public class SocketManager { + + private final static Logger logger = LoggerFactory.getLogger(SocketManager.class); + + private ChannelHandlerContext ctx; + + /** + * 自动返回数据时间,必须要小于事务模块最大相应时间.(通过心跳获取) + */ + + private volatile int delay = 1; + + /** + * false 未链接 + * true 连接中 + */ + private volatile boolean netState = false; + + + private static SocketManager manager = null; + + private ExecutorService threadPool = Executors.newFixedThreadPool(max_size, new NamedThreadFactory("sender")); + + private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(max_size); + + private final static int max_size = 50; + + + public static SocketManager getInstance() { + if (manager == null){ + synchronized (SocketManager.class){ + if(manager==null){ + manager = new SocketManager(); + } + } + } + return manager; + } + + private SocketManager() { + } + + + public void setCtx(ChannelHandlerContext ctx) { + this.ctx = ctx; + } + + public boolean isNetState() { + return netState; + } + + public void setNetState(boolean netState) { + this.netState = netState; + } + + public void setDelay(int delay) { + this.delay = delay; + } + + private void sleepSend(Task task, Request request) { + while (!task.isAwait() && !Thread.currentThread().interrupted()) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + SocketUtils.sendMsg(ctx, request.toMsg()); + logger.info("send-msg->" + request.toMsg()); + } + + +// public void onlySendMsg(final Request request) { +// threadPool.execute(new Runnable() { +// @Override +// public void run() { +// SocketUtils.sendMsg(ctx, request.toMsg()); +// } +// }); +// } + + + public String sendMsg(final Request request) { + final String key = request.getKey(); + if (ctx != null && ctx.channel() != null && ctx.channel().isActive()) { + final Task task = ConditionUtils.getInstance().createTask(key); + ScheduledFuture future = executorService.schedule(new Runnable() { + @Override + public void run() { + Task task = ConditionUtils.getInstance().getTask(key); + if (task != null && !task.isNotify()) { + task.setBack(new IBack() { + @Override + public Object doing(Object... objs) throws Throwable { + return null; + } + }); + task.signalTask(); + } + } + }, delay, TimeUnit.SECONDS); + + threadPool.execute(new Runnable() { + @Override + public void run() { + sleepSend(task, request); + } + }); + + task.awaitTask(); + + if (!future.isDone()) { + future.cancel(false); + } + + try { + Object msg = task.getBack().doing(); + return (String) msg; + } catch (Throwable e) { + } finally { + task.remove(); + } + } + return null; + + } + + public void close() { + if(threadPool!=null){ + threadPool.shutdown(); + } + if(executorService!=null){ + executorService.shutdown(); + } + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/SocketUtils.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/SocketUtils.java new file mode 100644 index 0000000..703f49c --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/SocketUtils.java @@ -0,0 +1,37 @@ +package com.codingapi.tx.framework.utils; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.util.ReferenceCountUtil; + +/** + * Created by lorne on 2017/7/6. + */ +public class SocketUtils { + + public static String getJson(Object msg) { + String json; + try { + ByteBuf buf = (ByteBuf) msg; + byte[] bytes = new byte[buf.readableBytes()]; + buf.readBytes(bytes); + json = new String(bytes); + } finally { + ReferenceCountUtil.release(msg); + } + return json; + + } + + public static void sendMsg(final ChannelHandlerContext ctx, final String msg) { + ctx.writeAndFlush(Unpooled.buffer().writeBytes(msg.getBytes())); + + } + + + public static void sendMsg(final Channel ctx, final String msg) { + ctx.writeAndFlush(Unpooled.buffer().writeBytes(msg.getBytes())); + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/serializer/ISerializer.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/serializer/ISerializer.java new file mode 100644 index 0000000..6f8503e --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/serializer/ISerializer.java @@ -0,0 +1,32 @@ + +package com.codingapi.tx.framework.utils.serializer; + + +import com.lorne.core.framework.exception.SerializerException; + +/** + * @author lorne 2017/11/11 + */ +public interface ISerializer { + /** + * 序列化对象 + * + * @param obj 需要序更列化的对象 + * @return byte [] 序列号结果 + * @throws SerializerException 序列化异常 + */ + byte[] serialize(Object obj) throws SerializerException; + + + /** + * 反序列化对象 + * + * @param param 需要反序列化的byte [] + * @param clazz 反序列化成为的bean对象Class + * @param 反序列化成为的bean对象 + * @return 对象 + * @throws SerializerException 序列化异常 + */ + + T deSerialize(byte[] param, Class clazz) throws SerializerException; +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/serializer/ProtostuffSerializer.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/serializer/ProtostuffSerializer.java new file mode 100644 index 0000000..54c8446 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/serializer/ProtostuffSerializer.java @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2016 Newland Group Holding Limited + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.codingapi.tx.framework.utils.serializer; + +import com.dyuproject.protostuff.LinkedBuffer; +import com.dyuproject.protostuff.ProtostuffIOUtil; +import com.dyuproject.protostuff.Schema; + + +import com.lorne.core.framework.exception.SerializerException; +import org.objenesis.Objenesis; +import org.objenesis.ObjenesisStd; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + +/** + * @author lorne 2017/11/11 + */ +public class ProtostuffSerializer implements ISerializer { + private static final SchemaCache cachedSchema = SchemaCache.getInstance(); + private static final Objenesis objenesis = new ObjenesisStd(true); + + private static Schema getSchema(Class cls) { + return (Schema) cachedSchema.get(cls); + } + + + + @Override + public byte[] serialize(Object obj) throws SerializerException { + Class cls = obj.getClass(); + LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try { + Schema schema = getSchema(cls); + ProtostuffIOUtil.writeTo(outputStream, obj, schema, buffer); + } catch (Exception e) { + throw new SerializerException(e.getMessage(), e); + } finally { + buffer.clear(); + } + return outputStream.toByteArray(); + } + + @Override + public T deSerialize(byte[] param, Class clazz) throws SerializerException { + T object; + try { + ByteArrayInputStream inputStream = new ByteArrayInputStream(param); + Class cls = clazz; + object = objenesis.newInstance((Class) cls); + Schema schema = getSchema(cls); + ProtostuffIOUtil.mergeFrom(inputStream, object, schema); + return object; + } catch (Exception e) { + throw new SerializerException(e.getMessage(), e); + } + } +} + diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/serializer/SchemaCache.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/serializer/SchemaCache.java new file mode 100644 index 0000000..c473cb5 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/framework/utils/serializer/SchemaCache.java @@ -0,0 +1,60 @@ +/** + * Copyright (C) 2016 Newland Group Holding Limited + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.codingapi.tx.framework.utils.serializer; + +import com.dyuproject.protostuff.Schema; +import com.dyuproject.protostuff.runtime.RuntimeSchema; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * @author lorne 2017/11/11 + */ +public class SchemaCache { + private static class SchemaCacheHolder { + private static SchemaCache cache = new SchemaCache(); + } + + public static SchemaCache getInstance() { + return SchemaCacheHolder.cache; + } + + private Cache, Schema> cache = CacheBuilder.newBuilder() + .maximumSize(1024).expireAfterWrite(1, TimeUnit.HOURS) + .build(); + + private Schema get(final Class cls, Cache, Schema> cache) { + try { + return cache.get(cls, new Callable() { + @Override + public Object call() throws Exception { + return RuntimeSchema.createFrom(cls); + } + }); + } catch (ExecutionException e) { + return null; + } + } + + public Schema get(final Class cls) { + return get(cls, cache); + } +} + diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/listener/service/InitService.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/listener/service/InitService.java new file mode 100644 index 0000000..def2b50 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/listener/service/InitService.java @@ -0,0 +1,9 @@ +package com.codingapi.tx.listener.service; + +/** + * Created by yuliang on 2017/7/11. + */ +public interface InitService { + + void start(); +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/listener/service/ModelNameService.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/listener/service/ModelNameService.java new file mode 100644 index 0000000..80933d2 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/listener/service/ModelNameService.java @@ -0,0 +1,13 @@ +package com.codingapi.tx.listener.service; + +/** + * Created by lorne on 2017/7/12. + */ +public interface ModelNameService { + + String getModelName(); + + String getUniqueKey(); + + String getIpAddress(); +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/listener/service/impl/InitServiceImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/listener/service/impl/InitServiceImpl.java new file mode 100644 index 0000000..a27b049 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/listener/service/impl/InitServiceImpl.java @@ -0,0 +1,27 @@ +package com.codingapi.tx.listener.service.impl; + +import com.codingapi.tx.listener.service.InitService; +import com.codingapi.tx.netty.service.NettyService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * Created by yuliang on 2017/7/11. + */ +@Service +public class InitServiceImpl implements InitService { + + private final static Logger logger = LoggerFactory.getLogger(InitServiceImpl.class); + + @Autowired + private NettyService nettyService; + + @Override + public void start() { + nettyService.start(); + logger.info("socket-start.."); + } + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/Request.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/Request.java new file mode 100644 index 0000000..d1f0303 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/Request.java @@ -0,0 +1,62 @@ +package com.codingapi.tx.model; + +import com.lorne.core.framework.utils.KidUtils; + +/** + * Created by lorne on 2017/6/30. + */ +public class Request { + + /** + * key + */ + private String key; + /** + * action + */ + private String action; + /** + * params + */ + private String params; + + + public Request(String action, String params) { + this.action = action; + this.params = params; + this.key = KidUtils.generateShortUuid(); + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public String getParams() { + return params; + } + + public void setParams(String params) { + this.params = params; + } + + public String toMsg() { +// JSONObject jsonObject = new JSONObject(); +// jsonObject.put("a", getAction()); +// jsonObject.put("k", getKey()); +// jsonObject.put("p", getParams()); + String json = "{\"a\":\"" + getAction() + "\",\"k\":\"" + getKey() + "\",\"p\":" + getParams() + "}"; + return json; + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/TransactionInvocation.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/TransactionInvocation.java new file mode 100644 index 0000000..821405e --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/TransactionInvocation.java @@ -0,0 +1,83 @@ +package com.codingapi.tx.model; + +import java.io.Serializable; + +/** + * create by lorne on 2017/11/11 + */ +public class TransactionInvocation implements Serializable{ + + /** + * 事务执行器 + */ + private Class targetClazz; + /** + * 方法 + */ + private String method; + /** + * 参数值 + */ + private Object[] argumentValues; + + /** + * 参数类型 + */ + private Class[] parameterTypes; + + /** + * 方法字符串 + */ + private String methodStr; + + public TransactionInvocation() { + } + + public TransactionInvocation(Class targetClazz, String method, String methodStr, Object[] argumentValues, Class[] parameterTypes) { + this.targetClazz = targetClazz; + this.method = method; + this.methodStr = methodStr; + this.argumentValues = argumentValues; + this.parameterTypes = parameterTypes; + } + + public String getMethodStr() { + return methodStr; + } + + public void setMethodStr(String methodStr) { + this.methodStr = methodStr; + } + + public Class getTargetClazz() { + return targetClazz; + } + + public void setTargetClazz(Class targetClazz) { + this.targetClazz = targetClazz; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public Object[] getArgumentValues() { + return argumentValues; + } + + public void setArgumentValues(Object[] argumentValues) { + this.argumentValues = argumentValues; + } + + public Class[] getParameterTypes() { + return parameterTypes; + } + + public void setParameterTypes(Class[] parameterTypes) { + this.parameterTypes = parameterTypes; + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/TxGroup.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/TxGroup.java new file mode 100644 index 0000000..79b2421 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/TxGroup.java @@ -0,0 +1,97 @@ +package com.codingapi.tx.model; + + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.apache.commons.lang.StringUtils; + +/** + * Created by lorne on 2017/6/7. + */ +public class TxGroup { + + private String groupId; + + private long startTime; + + private long nowTime; + + private int hasOver; + + private int isCompensate; + + public int getIsCompensate() { + return isCompensate; + } + + public void setIsCompensate(int isCompensate) { + this.isCompensate = isCompensate; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + + public long getStartTime() { + return startTime; + } + + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + + public long getNowTime() { + return nowTime; + } + + public void setNowTime(long nowTime) { + this.nowTime = nowTime; + } + + + public int getHasOver() { + return hasOver; + } + + public void setHasOver(int hasOver) { + this.hasOver = hasOver; + } + + public static TxGroup parser(String json) { + try { + if (StringUtils.isEmpty(json)) { + return null; + } + JSONObject jsonObject = JSONObject.parseObject(json); + TxGroup txGroup = new TxGroup(); + txGroup.setGroupId(jsonObject.getString("g")); + txGroup.setStartTime(jsonObject.getLong("st")); + txGroup.setHasOver(jsonObject.getInteger("o")); + txGroup.setNowTime(jsonObject.getLong("nt")); + txGroup.setIsCompensate(jsonObject.getInteger("i")); + return txGroup; + + } catch (Exception e) { + return null; + } + + } + + public String toJsonString() { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("g", getGroupId()); + jsonObject.put("st", getStartTime()); + jsonObject.put("o",getHasOver()); + jsonObject.put("nt", getNowTime()); + jsonObject.put("i", getIsCompensate()); + JSONArray jsonArray = new JSONArray(); + jsonObject.put("l", jsonArray); + return jsonObject.toString(); + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/TxServer.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/TxServer.java new file mode 100644 index 0000000..89547e2 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/model/TxServer.java @@ -0,0 +1,80 @@ +package com.codingapi.tx.model; + + +import com.alibaba.fastjson.JSONObject; + +/** + * Created by lorne on 2017/6/30. + */ +public class TxServer { + + private int port; + private String host; + private int heart; + private int delay; + private int compensateMaxWaitTime; + + + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + + public int getHeart() { + return heart; + } + + public void setHeart(int heart) { + this.heart = heart; + } + + public int getDelay() { + return delay; + } + + public void setDelay(int delay) { + this.delay = delay; + } + + public int getCompensateMaxWaitTime() { + return compensateMaxWaitTime; + } + + public void setCompensateMaxWaitTime(int compensateMaxWaitTime) { + this.compensateMaxWaitTime = compensateMaxWaitTime; + } + + @Override + public String toString() { + return "host:" + host + ",port:" + port + ",heart:" + heart + ",delay:" + delay + ",compensateMaxWaitTime:" + compensateMaxWaitTime; + } + + public static TxServer parser(String json) { + try { + JSONObject jsonObject = JSONObject.parseObject(json); + TxServer txServer = new TxServer(); + txServer.setPort(jsonObject.getInteger("port")); + txServer.setHost(jsonObject.getString("ip")); + txServer.setHeart(jsonObject.getInteger("heart")); + txServer.setDelay(jsonObject.getInteger("delay")); + txServer.setCompensateMaxWaitTime(jsonObject.getInteger("compensateMaxWaitTime")); + return txServer; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/handler/TransactionHandler.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/handler/TransactionHandler.java new file mode 100644 index 0000000..cffbc42 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/handler/TransactionHandler.java @@ -0,0 +1,122 @@ +package com.codingapi.tx.netty.handler; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tx.framework.utils.SocketManager; +import com.codingapi.tx.framework.utils.SocketUtils; +import com.codingapi.tx.netty.service.NettyControlService; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.timeout.IdleState; +import io.netty.handler.timeout.IdleStateEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.Executor; + +/** + * Created by lorne on 2017/6/30. + */ +@ChannelHandler.Sharable +public class TransactionHandler extends ChannelInboundHandlerAdapter { + + + private Logger logger = LoggerFactory.getLogger(TransactionHandler.class); + + + private NettyControlService nettyControlService; + + private String heartJson; + + private Executor threadPool; + + + public TransactionHandler(Executor threadPool,NettyControlService nettyControlService, int delay) { + this.threadPool = threadPool; + this.nettyControlService = nettyControlService; + + SocketManager.getInstance().setDelay(delay); + + //心跳包 + JSONObject heartJo = new JSONObject(); + heartJo.put("a", "h"); + heartJo.put("k", "h"); + heartJo.put("p", "{}"); + heartJson = heartJo.toString(); + + } + + + @Override + public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { + + final String json = SocketUtils.getJson(msg); + + logger.debug("TxManager-response->" + json); + + threadPool.execute(new Runnable() { + @Override + public void run() { + nettyControlService.executeService(ctx, json); + } + }); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + cause.printStackTrace(); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + super.channelInactive(ctx); + + logger.info("disconnection -->" + ctx); + + SocketManager.getInstance().setNetState(false); + //链接断开,重新连接 + nettyControlService.restart(); + } + + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + super.channelActive(ctx); + SocketManager.getInstance().setCtx(ctx); + + logger.info("connection -->" + ctx); + + //通道激活后进行心跳检查 + SocketUtils.sendMsg(ctx, heartJson); + + nettyControlService.uploadModelInfo(); + } + + + /** + * 当客户端的所有ChannelHandler中4s内没有write事件,则会触发userEventTriggered方法 + * + * @param ctx 管道 + * @param evt 状态 + * @throws Exception 异常数据 + */ + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + //心跳配置 + if (IdleStateEvent.class.isAssignableFrom(evt.getClass())) { + IdleStateEvent event = (IdleStateEvent) evt; + if (event.state() == IdleState.READER_IDLE) { + //表示已经多久没有收到数据了 + //ctx.close(); + } else if (event.state() == IdleState.WRITER_IDLE) { + //表示已经多久没有发送数据了 + SocketUtils.sendMsg(ctx, heartJson); + logger.debug("hart data --->" + heartJson); + } else if (event.state() == IdleState.ALL_IDLE) { + //表示已经多久既没有收到也没有发送数据了 + } + } + } + + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/MQTxManagerService.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/MQTxManagerService.java new file mode 100644 index 0000000..01244a5 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/MQTxManagerService.java @@ -0,0 +1,80 @@ +package com.codingapi.tx.netty.service; + +import com.codingapi.tx.aop.bean.TxTransactionInfo; +import com.codingapi.tx.model.TxGroup; + + +/** + * Created by lorne on 2017/6/7. + */ +public interface MQTxManagerService { + + + /** + * 上传模块信息 + */ + void uploadModelInfo(); + + /** + * 创建事务组 + * @param groupId 事务组id 当为补偿模式时将会自动获取补偿的groupId + */ + void createTransactionGroup(String groupId); + + + /** + * 添加事务组子对象 + * @param groupId 事务组id + * @param taskId 任务Id + * @param isGroup 是否合并到事务组 true合并 false不合并 + * @param methodStr 方法参数列表 + * @return 事务组TxGroup + */ + TxGroup addTransactionGroup(String groupId, String taskId, boolean isGroup, String methodStr); + + + /** + * 关闭事务组-进入事务提交第一阶段 + * + * @param groupId 事务组id + * @param state 提交或者回滚 1提交0回滚 + * @return 1 成功 0失败 -1 事务强制回滚 + */ + int closeTransactionGroup(String groupId, int state); + + + /** + * 检查事务状态 通过netty管道 + * @param groupId 事务组id + * @param taskId 任务id + * @return 事务状态 + */ + int cleanNotifyTransaction(String groupId, String taskId); + + + + /** + * 检查并清理事务数据 + * @param groupId 事务组id + * @param waitTaskId 任务id + * @return 事务状态 + */ + int cleanNotifyTransactionHttp(String groupId, String waitTaskId); + + /** + * 记录补偿事务数据到tm + * @param groupId 事务组Id + * @param time 执行时间 + * @param info 事务信息 + * @param startError 启动模块db执行异常 + */ + void sendCompensateMsg(String groupId, long time, TxTransactionInfo info,int startError); + + + /** + * 获取TM服务地址 + * @return txServer + */ + String httpGetServer(); + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/NettyControlService.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/NettyControlService.java new file mode 100644 index 0000000..71066cb --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/NettyControlService.java @@ -0,0 +1,15 @@ +package com.codingapi.tx.netty.service; + +import io.netty.channel.ChannelHandlerContext; /** + * create by lorne on 2017/11/11 + */ +public interface NettyControlService { + void restart(); + + + void executeService(ChannelHandlerContext ctx, String json); + + + void uploadModelInfo(); + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/NettyDistributeService.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/NettyDistributeService.java new file mode 100644 index 0000000..a8c91d4 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/NettyDistributeService.java @@ -0,0 +1,8 @@ +package com.codingapi.tx.netty.service; + +/** + * Created by lorne on 2017/6/30. + */ +public interface NettyDistributeService { + void loadTxServer(); +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/NettyService.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/NettyService.java new file mode 100644 index 0000000..1aadf87 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/NettyService.java @@ -0,0 +1,14 @@ +package com.codingapi.tx.netty.service; + +/** + * Created by lorne on 2017/6/30. + */ +public interface NettyService { + + void start(); + + void close(); + + boolean checkState(); + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/TxManagerHttpRequestHelper.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/TxManagerHttpRequestHelper.java new file mode 100644 index 0000000..96438c5 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/TxManagerHttpRequestHelper.java @@ -0,0 +1,30 @@ +package com.codingapi.tx.netty.service; + + +import com.lorne.core.framework.utils.http.HttpUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +/** + * create by lorne on 2017/11/17 + */ +@Component +public class TxManagerHttpRequestHelper { + + private Logger logger = LoggerFactory.getLogger(TxManagerHttpRequestHelper.class); + + public String httpGet(String url) { + logger.info("load HttpRequestService:" + url); + return HttpUtils.get(url); + } + + public String httpPost(String url, String params) { + logger.info("load HttpRequestService:" + url + "?" + params); + return HttpUtils.post(url,params); + } + + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/MQTxManagerServiceImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/MQTxManagerServiceImpl.java new file mode 100644 index 0000000..5362b63 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/MQTxManagerServiceImpl.java @@ -0,0 +1,144 @@ +package com.codingapi.tx.netty.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tx.aop.bean.TxCompensateLocal; +import com.codingapi.tx.aop.bean.TxTransactionInfo; +import com.codingapi.tx.compensate.model.CompensateInfo; +import com.codingapi.tx.compensate.service.CompensateService; +import com.codingapi.tx.config.ConfigReader; +import com.codingapi.tx.framework.utils.SerializerUtils; +import com.codingapi.tx.framework.utils.SocketManager; +import com.codingapi.tx.listener.service.ModelNameService; +import com.codingapi.tx.model.Request; +import com.codingapi.tx.model.TxGroup; +import com.codingapi.tx.netty.service.MQTxManagerService; +import com.codingapi.tx.netty.service.TxManagerHttpRequestHelper; +import com.lorne.core.framework.utils.encode.Base64Utils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * Created by lorne on 2017/6/30. + */ +@Service +public class MQTxManagerServiceImpl implements MQTxManagerService { + + + @Autowired + private ModelNameService modelNameService; + + @Autowired + private ConfigReader configReader; + + @Autowired + private CompensateService compensateService; + + @Autowired + private TxManagerHttpRequestHelper managerHelper; + + + @Override + public void createTransactionGroup(String groupId) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("g", groupId); + Request request = new Request("cg", jsonObject.toString()); + SocketManager.getInstance().sendMsg(request); + } + + @Override + public TxGroup addTransactionGroup(String groupId, String taskId, boolean isGroup, String methodStr) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("g", groupId); + jsonObject.put("t", taskId); + jsonObject.put("ms", methodStr); + jsonObject.put("s", isGroup ? 1 : 0); + Request request = new Request("atg", jsonObject.toString()); + String json = SocketManager.getInstance().sendMsg(request); + return TxGroup.parser(json); + } + + + @Override + public int closeTransactionGroup(final String groupId, final int state) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("g", groupId); + jsonObject.put("s", state); + Request request = new Request("ctg", jsonObject.toString()); + String json = SocketManager.getInstance().sendMsg(request); + try { + return Integer.parseInt(json); + }catch (Exception e){ + return 0; + } + } + + + @Override + public void uploadModelInfo() { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("m", modelNameService.getModelName()); + jsonObject.put("i", modelNameService.getIpAddress()); + jsonObject.put("u", modelNameService.getUniqueKey()); + Request request = new Request("umi", jsonObject.toString()); + String json = SocketManager.getInstance().sendMsg(request); + } + + @Override + public int cleanNotifyTransaction(String groupId, String taskId) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("g", groupId); + jsonObject.put("t", taskId); + Request request = new Request("ckg", jsonObject.toString()); + String json = SocketManager.getInstance().sendMsg(request); + try { + return Integer.parseInt(json); + }catch (Exception e){ + return -2; + } + } + + + @Override + public int cleanNotifyTransactionHttp(String groupId, String waitTaskId) { + String url = configReader.getTxUrl() + "cleanNotifyTransactionHttp?groupId=" + groupId + "&taskId=" + waitTaskId; + String clearRes = managerHelper.httpGet(url); + if(clearRes==null){ + return -1; + } + return clearRes.contains("true") ? 1 : 0; + } + + + @Override + public String httpGetServer() { + String url = configReader.getTxUrl() + "getServer"; + return managerHelper.httpGet(url); + } + + @Override + public void sendCompensateMsg(String groupId, long time, TxTransactionInfo info,int startError) { + + String modelName = modelNameService.getModelName(); + String uniqueKey = modelNameService.getUniqueKey(); + String address = modelNameService.getIpAddress(); + + + byte[] serializers = SerializerUtils.serializeTransactionInvocation(info.getInvocation()); + String data = Base64Utils.encode(serializers); + + String className = info.getInvocation().getTargetClazz().getName(); + String methodStr = info.getInvocation().getMethodStr(); + long currentTime = System.currentTimeMillis(); + + + CompensateInfo compensateInfo = new CompensateInfo(currentTime, modelName, uniqueKey, data, methodStr, className, groupId, address, time,startError); + + String json = managerHelper.httpPost(configReader.getTxUrl() + "sendCompensateMsg", compensateInfo.toParamsString()); + + compensateInfo.setResJson(json); + + //记录本地日志 + compensateService.saveLocal(compensateInfo); + + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyControlServiceImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyControlServiceImpl.java new file mode 100644 index 0000000..77b963f --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyControlServiceImpl.java @@ -0,0 +1,123 @@ +package com.codingapi.tx.netty.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tx.control.service.TransactionControlService; +import com.codingapi.tx.framework.utils.SocketManager; +import com.codingapi.tx.listener.service.ModelNameService; +import com.codingapi.tx.netty.service.MQTxManagerService; +import com.codingapi.tx.netty.service.NettyControlService; +import com.codingapi.tx.netty.service.NettyService; +import com.codingapi.tx.netty.utils.IpAddressUtils; +import com.lorne.core.framework.utils.task.ConditionUtils; +import com.lorne.core.framework.utils.task.IBack; +import com.lorne.core.framework.utils.task.Task; +import io.netty.channel.ChannelHandlerContext; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * create by lorne on 2017/11/11 + */ +@Service +public class NettyControlServiceImpl implements NettyControlService { + + private Logger logger = LoggerFactory.getLogger(NettyControlServiceImpl.class); + + @Autowired + private NettyService nettyService; + + @Autowired + private TransactionControlService transactionControlService; + + @Autowired + private MQTxManagerService mqTxManagerService; + + @Autowired + private ModelNameService modelNameService; + + + @Override + public void restart() { + nettyService.close(); + try { + Thread.sleep(1000 * 3); + } catch (InterruptedException e) { + e.printStackTrace(); + } + nettyService.start(); + } + + @Override + public void uploadModelInfo() { + new Thread(new Runnable() { + @Override + public void run() { + while (!SocketManager.getInstance().isNetState()|| !IpAddressUtils.isIpAddress(modelNameService.getIpAddress())) { + try { + Thread.sleep(1000 * 5); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + mqTxManagerService.uploadModelInfo(); + } + }).start(); + } + + @Override + public void executeService(final ChannelHandlerContext ctx,final String json) { + + if (StringUtils.isNotEmpty(json)) { + JSONObject resObj = JSONObject.parseObject(json); + if (resObj.containsKey("a")) { + // tm发送数据给tx模块的处理指令 + logger.info("receive cmd -> {}", json); + transactionControlService.notifyTransactionMsg(ctx,resObj,json); + }else{ + //tx发送数据给tm的响应返回数据 + + String key = resObj.getString("k"); + if (!"h".equals(key)) { + logger.info("receive response -> {}", json); + } + responseMsg(key,resObj); + } + } + } + + + private void responseMsg(String key, JSONObject resObj) { + if (!"h".equals(key)) { + final String data = resObj.getString("d"); + Task task = ConditionUtils.getInstance().getTask(key); + + if (task != null) { + + if (task.isAwait()) { + task.setBack(new IBack() { + @Override + public Object doing(Object... objs) throws Throwable { + return data; + } + }); + task.signalTask(); + } + + } + } else { + //心跳数据 + final String data = resObj.getString("d"); + SocketManager.getInstance().setNetState(true); + if (StringUtils.isNotEmpty(data)) { + try { + SocketManager.getInstance().setDelay(Integer.parseInt(data)); + } catch (Exception e) { + SocketManager.getInstance().setDelay(1); + } + } + } + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyDistributeServiceImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyDistributeServiceImpl.java new file mode 100644 index 0000000..bf9d022 --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyDistributeServiceImpl.java @@ -0,0 +1,65 @@ +package com.codingapi.tx.netty.service.impl; + +import com.codingapi.tx.Constants; +import com.codingapi.tx.model.TxServer; +import com.codingapi.tx.netty.service.MQTxManagerService; +import com.codingapi.tx.netty.service.NettyDistributeService; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * Created by lorne on 2017/6/30. + */ +@Service +public class NettyDistributeServiceImpl implements NettyDistributeService { + + private int connectCont = 0; + + private Logger logger = LoggerFactory.getLogger(NettyDistributeServiceImpl.class); + + @Autowired + private MQTxManagerService txManagerService; + + @Override + public synchronized void loadTxServer() { + if (Constants.txServer == null) { + getTxServer(); + return; + } + connectCont++; + if (connectCont == 3) { + getTxServer(); + } + } + + private void getTxServer() { + //获取负载均衡服务地址 + String json = null; + while (StringUtils.isEmpty(json)) { + json = txManagerService.httpGetServer(); + logger.info("get txManager ->" + json); + if (StringUtils.isEmpty(json)) { + logger.error("TxManager服务器无法访问."); + try { + Thread.sleep(1000 * 2); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + TxServer txServer = TxServer.parser(json); + if (txServer != null) { + logger.debug("txServer -> " + txServer); + logger.info(txServer.toString()); + Constants.txServer = txServer; + logger.info(Constants.txServer.toString()); + connectCont = 0; + } + + } + +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyServiceImpl.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyServiceImpl.java new file mode 100644 index 0000000..d633fed --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/service/impl/NettyServiceImpl.java @@ -0,0 +1,146 @@ +package com.codingapi.tx.netty.service.impl; + +import com.codingapi.tx.Constants; +import com.codingapi.tx.framework.thread.NamedThreadFactory; +import com.codingapi.tx.framework.utils.SocketManager; +import com.codingapi.tx.netty.handler.TransactionHandler; +import com.codingapi.tx.netty.service.NettyControlService; +import com.codingapi.tx.netty.service.NettyDistributeService; +import com.codingapi.tx.netty.service.NettyService; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import io.netty.handler.codec.LengthFieldPrepender; +import io.netty.handler.timeout.IdleStateHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * Created by lorne on 2017/6/30. + */ +@Service +public class NettyServiceImpl implements NettyService ,DisposableBean { + + + @Autowired + private NettyDistributeService nettyDistributeService; + + + @Autowired + private NettyControlService nettyControlService; + + + private EventLoopGroup workerGroup; + + + private static volatile boolean isStarting = false; + + + private Logger logger = LoggerFactory.getLogger(NettyServiceImpl.class); + + private ExecutorService threadPool = Executors.newFixedThreadPool(100,new NamedThreadFactory("receiver")); + + @Override + public synchronized void start() { + if (isStarting) { + return; + } + isStarting = true; + nettyDistributeService.loadTxServer(); + + String host = Constants.txServer.getHost(); + int port = Constants.txServer.getPort(); + final int heart = Constants.txServer.getHeart(); + int delay = Constants.txServer.getDelay(); + + final TransactionHandler transactionHandler = new TransactionHandler(threadPool,nettyControlService, delay); + workerGroup = new NioEventLoopGroup(); + try { + Bootstrap b = new Bootstrap(); // (1) + b.group(workerGroup); // (2) + b.channel(NioSocketChannel.class); // (3) + b.option(ChannelOption.SO_KEEPALIVE, true); // (4) + b.handler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + + ch.pipeline().addLast("timeout", new IdleStateHandler(heart, heart, heart, TimeUnit.SECONDS)); + + ch.pipeline().addLast(new LengthFieldPrepender(4, false)); + ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4)); + + ch.pipeline().addLast(transactionHandler); + } + }); + // Start the client. + logger.info("connection txManager-socket-> host:" + host + ",port:" + port); + ChannelFuture future = b.connect(host, port); // (5) + + future.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture channelFuture) throws Exception { + if (!channelFuture.isSuccess()) { + channelFuture.channel().eventLoop().schedule(new Runnable() { + @Override + public void run() { + isStarting = false; + start(); + } + }, 5, TimeUnit.SECONDS); + } + } + }); + + } catch (Exception e) { + logger.error(e.getLocalizedMessage()); + } + } + + @Override + public synchronized void close() { + if (workerGroup != null) { + workerGroup.shutdownGracefully(); + workerGroup = null; + + SocketManager.getInstance().setNetState(false); + isStarting = false; + } + } + + + @Override + public boolean checkState() { + if (!SocketManager.getInstance().isNetState()) { + logger.error("socket not connection wait 2 seconds."); + try { + Thread.sleep(1000 * 2); + } catch (InterruptedException e) { + e.printStackTrace(); + } + if (!SocketManager.getInstance().isNetState()) { + logger.error("socket not connection,check txManager server ."); + return false; + } + } + + return true; + } + + + @Override + public void destroy() throws Exception { + close(); + SocketManager.getInstance().close(); + threadPool.shutdown(); + } +} diff --git a/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/utils/IpAddressUtils.java b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/utils/IpAddressUtils.java new file mode 100644 index 0000000..3c644db --- /dev/null +++ b/tx-lcn/tx-client/src/main/java/com/codingapi/tx/netty/utils/IpAddressUtils.java @@ -0,0 +1,20 @@ +package com.codingapi.tx.netty.utils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * create by lorne on 2017/12/12 + */ +public class IpAddressUtils { + + private final static String ipAddressRegex = "([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}:(\\d{1,5})"; + + private final static Pattern pattern = Pattern.compile(ipAddressRegex); + + public static boolean isIpAddress(String ipAddress){ + Matcher matcher = pattern.matcher(ipAddress); + return matcher.matches(); + } + +} diff --git a/tx-lcn/tx-manager/README.md b/tx-lcn/tx-manager/README.md new file mode 100644 index 0000000..07c8623 --- /dev/null +++ b/tx-lcn/tx-manager/README.md @@ -0,0 +1,14 @@ +# tx-manager + +LCN 分布式事务协调器 + +创建zip安装包 + +`mvn clean install ` +修改parent:spring boot --> tx-lcn +修改spring cloud版本:Dalston.SR1 --> Finchley.SR2 +修改对应的eureka-server依赖 +修改服务端口server.port +修改注册中心地址 +修改redis ip地址 +com.codingapi.tm.ServletInitializer 重新导包 \ No newline at end of file diff --git a/tx-lcn/tx-manager/pom.xml b/tx-lcn/tx-manager/pom.xml new file mode 100644 index 0000000..9a06dc2 --- /dev/null +++ b/tx-lcn/tx-manager/pom.xml @@ -0,0 +1,77 @@ + + + 4.0.0 + + com.codingapi + tx-manager + 4.2.0-SNAPSHOT + jar + + tx-manager + tx-manager + + + com.codingapi + tx-lcn + 4.2.0-SNAPSHOT + + + + 19.0 + true + true + + + + + + com.github.1991wangliang + lorne_core + 1.0.0 + + + org.slf4j + * + + + + + + + io.netty + netty-all + 4.1.12.Final + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-server + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-redis + 1.3.8.RELEASE + + + + com.google.guava + guava + ${guava.version} + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/tx-lcn/tx-manager/src/main/build/package.xml b/tx-lcn/tx-manager/src/main/build/package.xml new file mode 100644 index 0000000..5b5cf98 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/build/package.xml @@ -0,0 +1,37 @@ + + + package + + zip + + true + + + bin + / + + + src/main/resources + / + + + ${project.build.directory} + / + + *.jar + + + + + + lib + runtime + + ${groupId}:${artifactId} + + + + diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/Constants.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/Constants.java new file mode 100644 index 0000000..22252df --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/Constants.java @@ -0,0 +1,15 @@ +package com.codingapi.tm; + +/** + * Created by lorne on 2017/6/8. + */ +public class Constants { + + + public static int maxConnection; + + public static int socketPort; + + public static String address; + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/CorsConfig.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/CorsConfig.java new file mode 100644 index 0000000..53e4f9d --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/CorsConfig.java @@ -0,0 +1,40 @@ +package com.codingapi.tm; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +/** + * create by lorne on 2017/8/17 + */ +@Configuration +public class CorsConfig { + + /** + * 跨域过滤器 + * + * @return + */ + private CorsConfiguration buildConfig() { + CorsConfiguration corsConfiguration = new CorsConfiguration(); + corsConfiguration.addAllowedOrigin("*"); + corsConfiguration.addAllowedHeader("*"); + corsConfiguration.addAllowedMethod("*"); + return corsConfiguration; + } + + + /** + * 跨域过滤器 + * + * @return + */ + @Bean + public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", buildConfig()); // 4 + return new CorsFilter(source); + } +} \ No newline at end of file diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/RestConfig.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/RestConfig.java new file mode 100644 index 0000000..6583e36 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/RestConfig.java @@ -0,0 +1,24 @@ +package com.codingapi.tm; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +/** + * Created by lorne on 2017/7/5. + */ + +@Configuration +@EnableAutoConfiguration +public class RestConfig { + + + @Bean + public RestTemplate getRestTemplate() { + RestTemplate restTemplate = new RestTemplate(); + return restTemplate; + } + + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/ServletInitializer.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/ServletInitializer.java new file mode 100644 index 0000000..466d28d --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/ServletInitializer.java @@ -0,0 +1,18 @@ +package com.codingapi.tm; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * Created by lorne on 2017/7/3. + */ +public class ServletInitializer extends SpringBootServletInitializer { + + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { + return builder.sources(TxManagerApplication.class); + } + + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/TxManagerApplication.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/TxManagerApplication.java new file mode 100644 index 0000000..156b282 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/TxManagerApplication.java @@ -0,0 +1,18 @@ +package com.codingapi.tm; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + + +@SpringBootApplication +@EnableDiscoveryClient +public class TxManagerApplication { + + + + public static void main(String[] args) { + SpringApplication.run(TxManagerApplication.class, args); + } + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/controller/AdminController.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/controller/AdminController.java new file mode 100644 index 0000000..460d886 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/controller/AdminController.java @@ -0,0 +1,78 @@ +package com.codingapi.tm.api.controller; + +import com.codingapi.tm.api.service.ApiAdminService; +import com.codingapi.tm.api.service.ApiModelService; +import com.codingapi.tm.compensate.model.TxModel; +import com.codingapi.tm.model.ModelInfo; +import com.codingapi.tm.model.ModelName; +import com.codingapi.tm.model.TxState; +import com.lorne.core.framework.exception.ServiceException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * Created by lorne on 2017/7/1. + */ +@RestController +@RequestMapping("/admin") +public class AdminController { + + @Autowired + private ApiAdminService apiAdminService; + + @Autowired + private ApiModelService apiModelService; + + + @RequestMapping(value = "/onlines", method = RequestMethod.GET) + public List onlines() { + return apiModelService.onlines(); + } + + + @RequestMapping(value = "/setting", method = RequestMethod.GET) + public TxState setting() { + return apiAdminService.getState(); + } + + @RequestMapping(value = "/json", method = RequestMethod.GET) + public String json() { + return apiAdminService.loadNotifyJson(); + } + + @RequestMapping(value = "/modelList", method = RequestMethod.GET) + public List modelList() { + return apiAdminService.modelList(); + } + + @RequestMapping(value = "/modelTimes", method = RequestMethod.GET) + public List modelTimes(@RequestParam("model") String model) { + return apiAdminService.modelTimes(model); + } + + + @RequestMapping(value = "/modelInfos", method = RequestMethod.GET) + public List modelInfos(@RequestParam("path") String path) { + return apiAdminService.modelInfos(path); + } + + @RequestMapping(value = "/compensate", method = RequestMethod.GET) + public boolean compensate(@RequestParam("path") String path) throws ServiceException { + return apiAdminService.compensate(path); + } + + @RequestMapping(value = "/delCompensate", method = RequestMethod.GET) + public boolean delCompensate(@RequestParam("path") String path) throws ServiceException { + return apiAdminService.delCompensate(path); + } + + @RequestMapping(value = "/hasCompensate", method = RequestMethod.GET) + public boolean hasCompensate() throws ServiceException { + return apiAdminService.hasCompensate(); + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/controller/TxManagerController.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/controller/TxManagerController.java new file mode 100644 index 0000000..91e7a5c --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/controller/TxManagerController.java @@ -0,0 +1,56 @@ +package com.codingapi.tm.api.controller; + +import com.codingapi.tm.api.service.ApiTxManagerService; +import com.codingapi.tm.model.TxServer; +import com.codingapi.tm.model.TxState; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * Created by lorne on 2017/7/1. + */ +@RestController +@RequestMapping("/tx/manager") +public class TxManagerController { + + @Autowired + private ApiTxManagerService apiTxManagerService; + + + @RequestMapping("/getServer") + public TxServer getServer() { + return apiTxManagerService.getServer(); + } + + + @RequestMapping("/cleanNotifyTransaction") + public int cleanNotifyTransaction(@RequestParam("groupId") String groupId,@RequestParam("taskId") String taskId) { + return apiTxManagerService.cleanNotifyTransaction(groupId,taskId); + } + + + @RequestMapping("/sendMsg") + public String sendMsg(@RequestParam("msg") String msg,@RequestParam("model") String model) { + return apiTxManagerService.sendMsg(model,msg); + } + + + @RequestMapping("/sendCompensateMsg") + public boolean sendCompensateMsg(@RequestParam("model") String model, @RequestParam("uniqueKey") String uniqueKey, + @RequestParam("currentTime") long currentTime, + @RequestParam("groupId") String groupId, @RequestParam("className") String className, + @RequestParam("time") int time, @RequestParam("data") String data, + @RequestParam("methodStr") String methodStr, @RequestParam("address") String address, + @RequestParam("startError") int startError) { + return apiTxManagerService.sendCompensateMsg(currentTime, groupId, model, address, uniqueKey, className, methodStr, data, time,startError); + } + + + + @RequestMapping("/state") + public TxState state() { + return apiTxManagerService.getState(); + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/ApiAdminService.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/ApiAdminService.java new file mode 100644 index 0000000..cd799dc --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/ApiAdminService.java @@ -0,0 +1,32 @@ +package com.codingapi.tm.api.service; + +import com.codingapi.tm.compensate.model.TxModel; +import com.codingapi.tm.model.ModelName; +import com.codingapi.tm.model.TxState; +import com.lorne.core.framework.exception.ServiceException; + +import java.util.List; + +/** + * create by lorne on 2017/11/12 + */ +public interface ApiAdminService { + + TxState getState(); + + String loadNotifyJson(); + + List modelList(); + + + List modelTimes(String model); + + List modelInfos(String path); + + boolean compensate(String path) throws ServiceException; + + boolean hasCompensate(); + + boolean delCompensate(String path); + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/ApiModelService.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/ApiModelService.java new file mode 100644 index 0000000..7bad051 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/ApiModelService.java @@ -0,0 +1,15 @@ +package com.codingapi.tm.api.service; + +import com.codingapi.tm.model.ModelInfo; + +import java.util.List; + +/** + * create by lorne on 2017/11/13 + */ +public interface ApiModelService { + + List onlines(); + + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/ApiTxManagerService.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/ApiTxManagerService.java new file mode 100644 index 0000000..e5c5f87 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/ApiTxManagerService.java @@ -0,0 +1,61 @@ +package com.codingapi.tm.api.service; + +import com.codingapi.tm.model.TxServer; +import com.codingapi.tm.model.TxState; + +/** + * Created by lorne on 2017/7/1. + */ +public interface ApiTxManagerService { + + + /** + * 获取本服务的信息 + * + * @return 服务信息 + */ + TxServer getServer(); + + + /** + * 给 tx模块发送指令 + * @param model 模块名称 + * @param msg 指令 + * @return 指令返回结果 + */ + String sendMsg(String model, String msg); + + /** + * 检查并清理事务数据 + * @param groupId 事务组Id + * @param taskId 任务Id + * @param isGroup 是否合并事务 + * @return 事务状态 + */ + int cleanNotifyTransaction(String groupId, String taskId); + + + /** + * 保存事务补偿日志信息 + * @param currentTime 时间 + * @param groupId 事务组id + * @param model 模块名称 + * @param address 模块地址 + * @param uniqueKey 唯一标示 + * @param className 事务启动类 + * @param methodStr 事务启动方法 + * @param data 切面数据 + * @param time 执行时间 + * @param startError 启动模块异常 + * @return 是否保存成功 + */ + boolean sendCompensateMsg(long currentTime, String groupId, String model, String address, String uniqueKey, String className, String methodStr, String data, int time,int startError); + + /** + * 获取服务器状态 + * + * @return 状态 + */ + TxState getState(); + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/impl/ApiAdminServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/impl/ApiAdminServiceImpl.java new file mode 100644 index 0000000..35388bf --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/impl/ApiAdminServiceImpl.java @@ -0,0 +1,72 @@ +package com.codingapi.tm.api.service.impl; + +import com.codingapi.tm.api.service.ApiAdminService; +import com.codingapi.tm.compensate.model.TxModel; +import com.codingapi.tm.compensate.service.CompensateService; +import com.codingapi.tm.manager.service.MicroService; +import com.codingapi.tm.model.ModelName; +import com.codingapi.tm.model.TxState; +import com.codingapi.tm.redis.service.RedisServerService; +import com.lorne.core.framework.exception.ServiceException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * create by lorne on 2017/11/12 + */ +@Service +public class ApiAdminServiceImpl implements ApiAdminService { + + + @Autowired + private MicroService eurekaService; + + @Autowired + private RedisServerService redisServerService; + + @Autowired + private CompensateService compensateService; + + @Override + public TxState getState() { + return eurekaService.getState(); + } + + @Override + public String loadNotifyJson() { + return redisServerService.loadNotifyJson(); + } + + @Override + public List modelList() { + return compensateService.loadModelList(); + } + + + @Override + public List modelTimes(String model) { + return compensateService.loadCompensateTimes(model); + } + + @Override + public List modelInfos(String path) { + return compensateService.loadCompensateByModelAndTime(path); + } + + @Override + public boolean compensate(String path) throws ServiceException { + return compensateService.executeCompensate(path); + } + + @Override + public boolean delCompensate(String path) { + return compensateService.delCompensate(path); + } + + @Override + public boolean hasCompensate() { + return compensateService.hasCompensate(); + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/impl/ApiModelServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/impl/ApiModelServiceImpl.java new file mode 100644 index 0000000..c0d8cea --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/impl/ApiModelServiceImpl.java @@ -0,0 +1,23 @@ +package com.codingapi.tm.api.service.impl; + +import com.codingapi.tm.api.service.ApiModelService; +import com.codingapi.tm.manager.ModelInfoManager; +import com.codingapi.tm.model.ModelInfo; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * create by lorne on 2017/11/13 + */ +@Service +public class ApiModelServiceImpl implements ApiModelService { + + + @Override + public List onlines() { + return ModelInfoManager.getInstance().getOnlines(); + } + + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/impl/ApiTxManagerServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/impl/ApiTxManagerServiceImpl.java new file mode 100644 index 0000000..37376f8 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/api/service/impl/ApiTxManagerServiceImpl.java @@ -0,0 +1,68 @@ +package com.codingapi.tm.api.service.impl; + + +import com.codingapi.tm.api.service.ApiTxManagerService; +import com.codingapi.tm.compensate.model.TransactionCompensateMsg; +import com.codingapi.tm.compensate.service.CompensateService; +import com.codingapi.tm.config.ConfigReader; +import com.codingapi.tm.manager.service.MicroService; +import com.codingapi.tm.manager.service.TxManagerSenderService; +import com.codingapi.tm.manager.service.TxManagerService; +import com.codingapi.tm.model.TxServer; +import com.codingapi.tm.model.TxState; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * Created by lorne on 2017/7/1. + */ +@Service +public class ApiTxManagerServiceImpl implements ApiTxManagerService { + + + @Autowired + private TxManagerService managerService; + + @Autowired + private MicroService eurekaService; + + @Autowired + private CompensateService compensateService; + + + @Autowired + private TxManagerSenderService txManagerSenderService; + + @Autowired + private ConfigReader configReader; + + + @Override + public TxServer getServer() { + return eurekaService.getServer(); + } + + + @Override + public int cleanNotifyTransaction(String groupId, String taskId) { + return managerService.cleanNotifyTransaction(groupId,taskId); + } + + + @Override + public boolean sendCompensateMsg(long currentTime, String groupId, String model, String address, String uniqueKey, String className, String methodStr, String data, int time,int startError) { + TransactionCompensateMsg transactionCompensateMsg = new TransactionCompensateMsg(currentTime, groupId, model, address, uniqueKey, className, methodStr, data, time, 0,startError); + return compensateService.saveCompensateMsg(transactionCompensateMsg); + } + + @Override + public String sendMsg(String model,String msg) { + return txManagerSenderService.sendMsg(model, msg, configReader.getTransactionNettyDelayTime()); + } + + + @Override + public TxState getState() { + return eurekaService.getState(); + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/dao/CompensateDao.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/dao/CompensateDao.java new file mode 100644 index 0000000..e11f51e --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/dao/CompensateDao.java @@ -0,0 +1,29 @@ +package com.codingapi.tm.compensate.dao; + +import com.codingapi.tm.compensate.model.TransactionCompensateMsg; + +import java.util.List; + +/** + * create by lorne on 2017/11/11 + */ +public interface CompensateDao { + + String saveCompensateMsg(TransactionCompensateMsg transactionCompensateMsg); + + List loadCompensateKeys(); + + List loadCompensateTimes(String model); + + List loadCompensateByModelAndTime(String path); + + String getCompensate(String key); + + String getCompensateByGroupId(String groupId); + + void deleteCompensateByPath(String path); + + void deleteCompensateByKey(String key); + + boolean hasCompensate(); +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/dao/impl/CompensateDaoImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/dao/impl/CompensateDaoImpl.java new file mode 100644 index 0000000..46b7c22 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/dao/impl/CompensateDaoImpl.java @@ -0,0 +1,110 @@ +package com.codingapi.tm.compensate.dao.impl; + +import com.alibaba.fastjson.JSON; +import com.codingapi.tm.compensate.dao.CompensateDao; +import com.codingapi.tm.compensate.model.TransactionCompensateMsg; +import com.codingapi.tm.config.ConfigReader; +import com.codingapi.tm.redis.service.RedisServerService; +import com.lorne.core.framework.utils.DateUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * create by lorne on 2017/11/11 + */ +@Service +public class CompensateDaoImpl implements CompensateDao { + + + @Autowired + private RedisServerService redisServerService; + + @Autowired + private ConfigReader configReader; + + + @Override + public String saveCompensateMsg(TransactionCompensateMsg transactionCompensateMsg) { + + String name = String.format("%s%s:%s:%s.json", configReader.getKeyPrefixCompensate(), transactionCompensateMsg.getModel(), DateUtil.getCurrentDateFormat(), transactionCompensateMsg.getGroupId()); + + String json = JSON.toJSONString(transactionCompensateMsg); + + redisServerService.saveCompensateMsg(name, json); + + return name; + } + + + @Override + public List loadCompensateKeys() { + String key = configReader.getKeyPrefixCompensate() + "*"; + return redisServerService.getKeys(key); + } + + + @Override + public boolean hasCompensate() { + String key = configReader.getKeyPrefixCompensate() + "*"; + List keys = redisServerService.getKeys(key); + return keys != null && keys.size() > 0; + } + + @Override + public List loadCompensateTimes(String model) { + String key = configReader.getKeyPrefixCompensate() + model + ":*"; + List keys = redisServerService.getKeys(key); + List times = new ArrayList(); + for (String k : keys) { + if(k.length()>36) { + String time = k.substring(k.length() - 24, k.length() - 14); + if (!times.contains(time)) { + times.add(time); + } + } + } + return times; + } + + + @Override + public List loadCompensateByModelAndTime(String path) { + String key = String.format("%s%s*", configReader.getKeyPrefixCompensate(), path); + List keys = redisServerService.getKeys(key); + List values = redisServerService.getValuesByKeys(keys); + return values; + } + + @Override + public String getCompensate(String path) { + String key = String.format("%s%s.json", configReader.getKeyPrefixCompensate(), path); + return redisServerService.getValueByKey(key); + } + + + @Override + public void deleteCompensateByPath(String path) { + String key = String.format("%s%s.json", configReader.getKeyPrefixCompensate(), path); + redisServerService.deleteKey(key); + } + + + @Override + public void deleteCompensateByKey(String key) { + redisServerService.deleteKey(key); + } + + @Override + public String getCompensateByGroupId(String groupId) { + String key = String.format("%s*%s.json", configReader.getKeyPrefixCompensate(), groupId); + List keys = redisServerService.getKeys(key); + if (keys != null && keys.size() == 1) { + String k = keys.get(0); + return redisServerService.getValueByKey(k); + } + return null; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/model/TransactionCompensateMsg.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/model/TransactionCompensateMsg.java new file mode 100644 index 0000000..6374841 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/model/TransactionCompensateMsg.java @@ -0,0 +1,138 @@ +package com.codingapi.tm.compensate.model; + +import com.codingapi.tm.netty.model.TxGroup; + +/** + * create by lorne on 2017/11/11 + */ +public class TransactionCompensateMsg { + + private long currentTime; + private String groupId; + private String model; + private String address; + private String uniqueKey; + private String className; + private String methodStr; + private String data; + private int time; + private int startError; + + private TxGroup txGroup; + + private int state; + + + public TransactionCompensateMsg(long currentTime, String groupId, String model, String address, + String uniqueKey, String className, + String methodStr, String data, int time, int state,int startError) { + this.currentTime = currentTime; + this.groupId = groupId; + this.model = model; + this.uniqueKey = uniqueKey; + this.className = className; + this.methodStr = methodStr; + this.data = data; + this.time = time; + this.address = address; + this.state = state; + this.startError = startError; + } + + public int getStartError() { + return startError; + } + + public void setStartError(int startError) { + this.startError = startError; + } + + public int getState() { + return state; + } + + public void setState(int state) { + this.state = state; + } + + public long getCurrentTime() { + return currentTime; + } + + public void setCurrentTime(long currentTime) { + this.currentTime = currentTime; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public TxGroup getTxGroup() { + return txGroup; + } + + public void setTxGroup(TxGroup txGroup) { + this.txGroup = txGroup; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public String getUniqueKey() { + return uniqueKey; + } + + public void setUniqueKey(String uniqueKey) { + this.uniqueKey = uniqueKey; + } + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + + public String getMethodStr() { + return methodStr; + } + + public void setMethodStr(String methodStr) { + this.methodStr = methodStr; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public int getTime() { + return time; + } + + public void setTime(int time) { + this.time = time; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/model/TxModel.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/model/TxModel.java new file mode 100644 index 0000000..7e54afb --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/model/TxModel.java @@ -0,0 +1,87 @@ +package com.codingapi.tm.compensate.model; + +/** + * create by lorne on 2017/11/12 + */ +public class TxModel { + + private String time; + + private String className; + + private String method; + + private int executeTime; + + private String base64; + + private int state; + + private long order; + + private String key; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public long getOrder() { + return order; + } + + public void setOrder(long order) { + this.order = order; + } + + public String getBase64() { + return base64; + } + + public void setBase64(String base64) { + this.base64 = base64; + } + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public int getExecuteTime() { + return executeTime; + } + + public void setExecuteTime(int executeTime) { + this.executeTime = executeTime; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public int getState() { + return state; + } + + public void setState(int state) { + this.state = state; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/service/CompensateService.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/service/CompensateService.java new file mode 100644 index 0000000..94cac8a --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/service/CompensateService.java @@ -0,0 +1,35 @@ +package com.codingapi.tm.compensate.service; + +import com.codingapi.tm.compensate.model.TransactionCompensateMsg; +import com.codingapi.tm.compensate.model.TxModel; +import com.codingapi.tm.model.ModelName; +import com.codingapi.tm.netty.model.TxGroup; +import com.lorne.core.framework.exception.ServiceException; + +import java.util.List; + +/** + * create by lorne on 2017/11/11 + */ +public interface CompensateService { + + boolean saveCompensateMsg(TransactionCompensateMsg transactionCompensateMsg); + + List loadModelList(); + + List loadCompensateTimes(String model); + + List loadCompensateByModelAndTime(String path); + + void autoCompensate(String compensateKey, TransactionCompensateMsg transactionCompensateMsg); + + boolean executeCompensate(String key) throws ServiceException; + + void reloadCompensate(TxGroup txGroup); + + boolean hasCompensate(); + + boolean delCompensate(String path); + + TxGroup getCompensateByGroupId(String groupId); +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/service/impl/CompensateServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/service/impl/CompensateServiceImpl.java new file mode 100644 index 0000000..84d183b --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/compensate/service/impl/CompensateServiceImpl.java @@ -0,0 +1,336 @@ +package com.codingapi.tm.compensate.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tm.compensate.dao.CompensateDao; +import com.codingapi.tm.compensate.model.TransactionCompensateMsg; +import com.codingapi.tm.compensate.model.TxModel; +import com.codingapi.tm.compensate.service.CompensateService; +import com.codingapi.tm.config.ConfigReader; +import com.codingapi.tm.manager.ModelInfoManager; +import com.codingapi.tm.manager.service.TxManagerSenderService; +import com.codingapi.tm.manager.service.TxManagerService; +import com.codingapi.tm.model.ModelInfo; +import com.codingapi.tm.model.ModelName; +import com.codingapi.tm.netty.model.TxGroup; +import com.codingapi.tm.netty.model.TxInfo; +import com.google.common.collect.Lists; +import com.lorne.core.framework.exception.ServiceException; +import com.lorne.core.framework.utils.DateUtil; +import com.lorne.core.framework.utils.encode.Base64Utils; +import com.lorne.core.framework.utils.http.HttpUtils; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +/** + * create by lorne on 2017/11/11 + */ +@Service +public class CompensateServiceImpl implements CompensateService { + + + private Logger logger = LoggerFactory.getLogger(CompensateServiceImpl.class); + + @Autowired + private CompensateDao compensateDao; + + @Autowired + private ConfigReader configReader; + + @Autowired + private TxManagerSenderService managerSenderService; + + @Autowired + private TxManagerService managerService; + + + private Executor threadPool = Executors.newFixedThreadPool(20); + + @Override + public boolean saveCompensateMsg(final TransactionCompensateMsg transactionCompensateMsg) { + + TxGroup txGroup =managerService.getTxGroup(transactionCompensateMsg.getGroupId()); + if (txGroup == null) { + //仅发起方异常,其他模块正常 + txGroup = new TxGroup(); + txGroup.setNowTime(System.currentTimeMillis()); + txGroup.setGroupId(transactionCompensateMsg.getGroupId()); + txGroup.setIsCompensate(1); + }else { + managerService.deleteTxGroup(txGroup); + } + + transactionCompensateMsg.setTxGroup(txGroup); + + final String json = JSON.toJSONString(transactionCompensateMsg); + + logger.info("Compensate->" + json); + + final String compensateKey = compensateDao.saveCompensateMsg(transactionCompensateMsg); + + //调整自动补偿机制,若开启了自动补偿,需要通知业务返回success,方可执行自动补偿 + threadPool.execute(new Runnable() { + @Override + public void run() { + try { + String groupId = transactionCompensateMsg.getGroupId(); + JSONObject requestJson = new JSONObject(); + requestJson.put("action", "compensate"); + requestJson.put("groupId", groupId); + requestJson.put("json", json); + + String url = configReader.getCompensateNotifyUrl(); + logger.error("Compensate Callback Address->" + url); + String res = HttpUtils.postJson(url, requestJson.toJSONString()); + logger.error("Compensate Callback Result->" + res); + if (configReader.isCompensateAuto()) { + //自动补偿,是否自动执行补偿 + if (res.contains("success") || res.contains("SUCCESS")) { + //自动补偿 + autoCompensate(compensateKey, transactionCompensateMsg); + } + } + } catch (Exception e) { + logger.error("Compensate Callback Fails->" + e.getMessage()); + } + } + }); + + return StringUtils.isNotEmpty(compensateKey); + + + + } + + @Override + public void autoCompensate(final String compensateKey, TransactionCompensateMsg transactionCompensateMsg) { + final String json = JSON.toJSONString(transactionCompensateMsg); + logger.info("Auto Compensate->" + json); + //自动补偿业务执行... + final int tryTime = configReader.getCompensateTryTime(); + boolean autoExecuteRes = false; + try { + int executeCount = 0; + autoExecuteRes = _executeCompensate(json); + logger.info("Automatic Compensate Result->" + autoExecuteRes + ",json->" + json); + while (!autoExecuteRes) { + logger.info("Compensate Failure, Entering Compensate Queue->" + autoExecuteRes + ",json->" + json); + executeCount++; + if(executeCount==3){ + autoExecuteRes = false; + break; + } + try { + Thread.sleep(tryTime * 1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + autoExecuteRes = _executeCompensate(json); + } + + //执行成功删除数据 + if(autoExecuteRes) { + compensateDao.deleteCompensateByKey(compensateKey); + } + + }catch (Exception e){ + logger.error("Auto Compensate Fails,msg:"+e.getLocalizedMessage()); + //推送数据给第三方通知 + autoExecuteRes = false; + } + + //执行补偿以后通知给业务方 + String groupId = transactionCompensateMsg.getGroupId(); + JSONObject requestJson = new JSONObject(); + requestJson.put("action","notify"); + requestJson.put("groupId",groupId); + requestJson.put("resState",autoExecuteRes); + + String url = configReader.getCompensateNotifyUrl(); + logger.error("Compensate Result Callback Address->" + url); + String res = HttpUtils.postJson(url, requestJson.toJSONString()); + logger.error("Compensate Result Callback Result->" + res); + + } + + + + @Override + public List loadModelList() { + List keys = compensateDao.loadCompensateKeys(); + + Map models = new HashMap(); + + for(String key:keys){ + if(key.length()>36){ + String name = key.substring(11,key.length()-25); + int v = 1; + if(models.containsKey(name)){ + v = models.get(name)+1; + } + models.put(name,v); + } + } + List names = new ArrayList<>(); + + for(String key:models.keySet()){ + int v = models.get(key); + ModelName modelName = new ModelName(); + modelName.setName(key); + modelName.setCount(v); + names.add(modelName); + } + return names; + } + + @Override + public List loadCompensateTimes(String model) { + return compensateDao.loadCompensateTimes(model); + } + + @Override + public List loadCompensateByModelAndTime(String path) { + List logs = compensateDao.loadCompensateByModelAndTime(path); + + List models = new ArrayList<>(); + for (String json : logs) { + JSONObject jsonObject = JSON.parseObject(json); + TxModel model = new TxModel(); + long currentTime = jsonObject.getLong("currentTime"); + model.setTime(DateUtil.formatDate(new Date(currentTime), DateUtil.FULL_DATE_TIME_FORMAT)); + model.setClassName(jsonObject.getString("className")); + model.setMethod(jsonObject.getString("methodStr")); + model.setExecuteTime(jsonObject.getInteger("time")); + model.setBase64(Base64Utils.encode(json.getBytes())); + model.setState(jsonObject.getInteger("state")); + model.setOrder(currentTime); + + String groupId = jsonObject.getString("groupId"); + + String key = path + ":" + groupId; + model.setKey(key); + + models.add(model); + } + Collections.sort(models, new Comparator() { + @Override + public int compare(TxModel o1, TxModel o2) { + if (o2.getOrder() > o1.getOrder()) { + return 1; + } else { + return -1; + } + } + }); + return models; + } + + @Override + public boolean hasCompensate() { + return compensateDao.hasCompensate(); + } + + @Override + public boolean delCompensate(String path) { + compensateDao.deleteCompensateByPath(path); + return true; + } + + @Override + public void reloadCompensate(TxGroup txGroup) { + TxGroup compensateGroup = getCompensateByGroupId(txGroup.getGroupId()); + if (compensateGroup != null) { + + if(compensateGroup.getList() != null && !compensateGroup.getList().isEmpty()){ + //引用集合 iterator,方便匹配后剔除列表 + Iterator iterator = Lists.newArrayList(compensateGroup.getList()).iterator(); + for (TxInfo txInfo : txGroup.getList()) { + while (iterator.hasNext()) { + TxInfo cinfo = iterator.next(); + if (cinfo.getModel().equals(txInfo.getModel()) && cinfo.getMethodStr().equals(txInfo.getMethodStr())) { + //根据之前的数据补偿现在的事务 + int oldNotify = cinfo.getNotify(); + + if (oldNotify == 1) { + //本次回滚 + txInfo.setIsCommit(0); + } else { + //本次提交 + txInfo.setIsCommit(1); + } + //匹配后剔除列表 + iterator.remove(); + break; + } + } + } + }else{//当没有List数据只记录了补偿数据时,理解问仅发起方提交其他均回滚 + for (TxInfo txInfo : txGroup.getList()) { + //本次回滚 + txInfo.setIsCommit(0); + } + } + } + logger.info("Compensate Loaded->"+JSON.toJSONString(txGroup)); + } + + public TxGroup getCompensateByGroupId(String groupId) { + String json = compensateDao.getCompensateByGroupId(groupId); + if (json == null) { + return null; + } + JSONObject jsonObject = JSON.parseObject(json); + String txGroup = jsonObject.getString("txGroup"); + return JSON.parseObject(txGroup, TxGroup.class); + } + + + @Override + public boolean executeCompensate(String path) throws ServiceException { + + String json = compensateDao.getCompensate(path); + if (json == null) { + throw new ServiceException("no data existing"); + } + + boolean hasOk = _executeCompensate(json); + if (hasOk) { + // 删除本地补偿数据 + compensateDao.deleteCompensateByPath(path); + + return true; + } + return false; + } + + + private boolean _executeCompensate(String json) throws ServiceException { + JSONObject jsonObject = JSON.parseObject(json); + + String model = jsonObject.getString("model"); + + int startError = jsonObject.getInteger("startError"); + + ModelInfo modelInfo = ModelInfoManager.getInstance().getModelByModel(model); + if (modelInfo == null) { + throw new ServiceException("current model offline."); + } + + String data = jsonObject.getString("data"); + + String groupId = jsonObject.getString("groupId"); + + String res = managerSenderService.sendCompensateMsg(modelInfo.getChannelName(), groupId, data,startError); + + logger.debug("executeCompensate->"+json+",@@->"+res); + + return "1".equals(res); + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/config/ConfigReader.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/config/ConfigReader.java new file mode 100644 index 0000000..666f294 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/config/ConfigReader.java @@ -0,0 +1,106 @@ +package com.codingapi.tm.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * create by lorne on 2017/11/11 + */ +@Component +public class ConfigReader { + + @Value("${tm.socket.port}") + private int socketPort; + + @Value("${tm.socket.maxconnection}") + private int socketMaxConnection; + + @Value("${tm.transaction.netty.hearttime}") + private int transactionNettyHeartTime; + + @Value("${tm.transaction.netty.delaytime}") + private int transactionNettyDelayTime; + + @Value("${tm.redis.savemaxtime}") + private int redisSaveMaxTime; + + + @Value("${tm.compensate.notifyUrl}") + private String compensateNotifyUrl; + + + @Value("${tm.compensate.auto}") + private boolean isCompensateAuto; + + + @Value("${tm.compensate.tryTime}") + private int compensateTryTime; + + @Value("${tm.compensate.maxWaitTime}") + private int compensateMaxWaitTime; + + /** + * 事务默认数据的位置,有最大时间 + */ + private final String key_prefix = "tx:manager:default:"; + /** + * 负载均衡模块存储信息 + */ + private final String key_prefix_loadbalance = "tx:manager:loadbalance:"; + + /** + * 补偿事务永久存储数据 + */ + private final String key_prefix_compensate = "tx:manager:compensate:"; + + + public String getKeyPrefixLoadbalance() { + return key_prefix_loadbalance; + } + + public String getCompensateNotifyUrl() { + return compensateNotifyUrl; + } + + public String getKeyPrefix() { + return key_prefix; + } + + public String getKeyPrefixCompensate() { + return key_prefix_compensate; + } + + public int getSocketPort(){ + return socketPort; + } + + public int getSocketMaxConnection() { + return socketMaxConnection; + } + + public int getTransactionNettyHeartTime() { + return transactionNettyHeartTime; + } + + public int getRedisSaveMaxTime() { + return redisSaveMaxTime; + } + + public int getTransactionNettyDelayTime() { + return transactionNettyDelayTime; + } + + public boolean isCompensateAuto() { + return isCompensateAuto; + } + + public int getCompensateTryTime() { + return compensateTryTime; + } + + public int getCompensateMaxWaitTime() { + return compensateMaxWaitTime; + } + + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/framework/utils/SocketManager.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/framework/utils/SocketManager.java new file mode 100644 index 0000000..7476944 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/framework/utils/SocketManager.java @@ -0,0 +1,110 @@ +package com.codingapi.tm.framework.utils; + +import com.codingapi.tm.Constants; +import io.netty.channel.Channel; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Created by lorne on 2017/6/30. + */ +public class SocketManager { + + /** + * 最大连接数 + */ + private int maxConnection = Constants.maxConnection; + + /** + * 当前连接数 + */ + private int nowConnection; + + /** + * 允许连接请求 true允许 false拒绝 + */ + private boolean allowConnection = true; + + private List clients = null; + + private Map lines = null; + + private static SocketManager manager = null; + + public static SocketManager getInstance() { + if (manager == null){ + synchronized (SocketManager.class){ + if(manager==null){ + manager = new SocketManager(); + } + } + } + return manager; + } + + + public Channel getChannelByModelName(String name) { + for (Channel channel : clients) { + String modelName = channel.remoteAddress().toString(); + + if (modelName.equals(name)) { + return channel; + } + } + return null; + } + + private SocketManager() { + clients = new CopyOnWriteArrayList(); + lines = new ConcurrentHashMap(); + } + + public void addClient(Channel client) { + clients.add(client); + nowConnection = clients.size(); + + allowConnection = (maxConnection != nowConnection); + } + + public void removeClient(Channel client) { + clients.remove(client); + nowConnection = clients.size(); + + allowConnection = (maxConnection != nowConnection); + } + + + public int getMaxConnection() { + return maxConnection; + } + + public int getNowConnection() { + return nowConnection; + } + + public boolean isAllowConnection() { + return allowConnection; + } + + public void outLine(String modelName) { + lines.remove(modelName); + } + + public void onLine(String modelName, String uniqueKey) { + lines.put(modelName,uniqueKey); + } + + public Channel getChannelByUniqueKey(String uniqueKey) { + for (Channel channel : clients) { + String modelName = channel.remoteAddress().toString(); + String value = lines.get(modelName); + if (uniqueKey.equals(value)) { + return channel; + } + } + return null; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/framework/utils/SocketUtils.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/framework/utils/SocketUtils.java new file mode 100644 index 0000000..973b8bd --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/framework/utils/SocketUtils.java @@ -0,0 +1,36 @@ +package com.codingapi.tm.framework.utils; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.util.ReferenceCountUtil; + +/** + * Created by lorne on 2017/7/6. + */ +public class SocketUtils { + + public static String getJson(Object msg) { + String json; + try { + ByteBuf buf = (ByteBuf) msg; + byte[] bytes = new byte[buf.readableBytes()]; + buf.readBytes(bytes); + json = new String(bytes); + } finally { + ReferenceCountUtil.release(msg); + } + return json; + + } + + public static void sendMsg(ChannelHandlerContext ctx, String msg){ + ctx.writeAndFlush(Unpooled.buffer().writeBytes(msg.getBytes())); + } + + + public static void sendMsg(Channel ctx,String msg){ + ctx.writeAndFlush(Unpooled.buffer().writeBytes(msg.getBytes())); + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/ApplicationStartListener.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/ApplicationStartListener.java new file mode 100644 index 0000000..5d67810 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/ApplicationStartListener.java @@ -0,0 +1,37 @@ +package com.codingapi.tm.listener; + +import com.codingapi.tm.Constants; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * create by lorne on 2017/8/7 + */ +@Component +public class ApplicationStartListener implements ApplicationListener { + + + @Override + public void onApplicationEvent(ApplicationEvent event) { + //TODO Spring boot 2.0.0没有EmbeddedServletContainerInitializedEvent 此处写死 + //int serverPort = event.getEmbeddedServletContainer().getPort(); + String ip = getIp(); + Constants.address = ip+":"+4545; + } + + + + private String getIp(){ + String host = null; + try { + host = InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + e.printStackTrace(); + } + return host; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/ServerListener.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/ServerListener.java new file mode 100644 index 0000000..894e516 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/ServerListener.java @@ -0,0 +1,37 @@ +package com.codingapi.tm.listener; + +import com.codingapi.tm.listener.service.InitService; +import org.springframework.stereotype.Component; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +/** + * Created by lorne on 2017/7/1. + */ + +@Component +public class ServerListener implements ServletContextListener { + + private WebApplicationContext springContext; + + + private InitService initService; + + @Override + public void contextInitialized(ServletContextEvent event) { + springContext = WebApplicationContextUtils + .getWebApplicationContext(event.getServletContext()); + initService = springContext.getBean(InitService.class); + initService.start(); + } + + + @Override + public void contextDestroyed(ServletContextEvent event) { + initService.close(); + } + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/service/InitService.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/service/InitService.java new file mode 100644 index 0000000..58c53ea --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/service/InitService.java @@ -0,0 +1,12 @@ +package com.codingapi.tm.listener.service; + +/** + * Created by lorne on 2017/7/4. + */ +public interface InitService { + + void start(); + + + void close(); +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/service/impl/InitServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/service/impl/InitServiceImpl.java new file mode 100644 index 0000000..e9f4018 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/listener/service/impl/InitServiceImpl.java @@ -0,0 +1,37 @@ +package com.codingapi.tm.listener.service.impl; + +import com.codingapi.tm.Constants; +import com.codingapi.tm.config.ConfigReader; +import com.codingapi.tm.listener.service.InitService; +import com.codingapi.tm.netty.service.NettyServerService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + + +/** + * Created by lorne on 2017/7/4. + */ +@Service +public class InitServiceImpl implements InitService { + + @Autowired + private NettyServerService nettyServerService; + + @Autowired + private ConfigReader configReader; + + + @Override + public void start() { + /**加载本地服务信息**/ + + Constants.socketPort = configReader.getSocketPort(); + Constants.maxConnection = configReader.getSocketMaxConnection(); + nettyServerService.start(); + } + + @Override + public void close() { + nettyServerService.close(); + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/ModelInfoManager.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/ModelInfoManager.java new file mode 100644 index 0000000..0b36186 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/ModelInfoManager.java @@ -0,0 +1,73 @@ +package com.codingapi.tm.manager; + +import com.codingapi.tm.model.ModelInfo; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * create by lorne on 2017/11/13 + */ +public class ModelInfoManager { + + + private List modelInfos = new CopyOnWriteArrayList(); + + private static ModelInfoManager manager = null; + + + public static ModelInfoManager getInstance() { + if (manager == null) { + synchronized (ModelInfoManager.class) { + if (manager == null) { + manager = new ModelInfoManager(); + } + } + } + return manager; + } + + public void removeModelInfo(String channelName) { + for (ModelInfo modelInfo : modelInfos) { + if (channelName.equalsIgnoreCase(modelInfo.getChannelName())) { + modelInfos.remove(modelInfo); + } + } + } + + + public void addModelInfo(ModelInfo minfo) { + for (ModelInfo modelInfo : modelInfos) { + if (minfo.getChannelName().equalsIgnoreCase(modelInfo.getChannelName())) { + return; + } + + if (minfo.getIpAddress().equalsIgnoreCase(modelInfo.getIpAddress())) { + return; + } + } + modelInfos.add(minfo); + } + + public List getOnlines() { + return modelInfos; + } + + public ModelInfo getModelByChannelName(String channelName) { + for (ModelInfo modelInfo : modelInfos) { + if (channelName.equalsIgnoreCase(modelInfo.getChannelName())) { + return modelInfo; + } + } + return null; + } + + public ModelInfo getModelByModel(String model) { + for (ModelInfo modelInfo : modelInfos) { + if (model.equalsIgnoreCase(modelInfo.getModel())) { + return modelInfo; + } + } + return null; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/LoadBalanceService.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/LoadBalanceService.java new file mode 100644 index 0000000..3b90923 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/LoadBalanceService.java @@ -0,0 +1,18 @@ +package com.codingapi.tm.manager.service; + +import com.codingapi.tm.model.LoadBalanceInfo; + +/** + * create by lorne on 2017/12/5 + */ +public interface LoadBalanceService { + + boolean put(LoadBalanceInfo loadBalanceInfo); + + LoadBalanceInfo get(String groupId,String key); + + boolean remove(String groupId); + + String getLoadBalanceGroupName(String groupId); + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/MicroService.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/MicroService.java new file mode 100644 index 0000000..9f5ae32 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/MicroService.java @@ -0,0 +1,16 @@ +package com.codingapi.tm.manager.service; + +import com.codingapi.tm.model.TxServer; +import com.codingapi.tm.model.TxState; + +/** + * create by lorne on 2017/11/11 + */ +public interface MicroService { + + String tmKey = "tx-manager"; + + TxServer getServer(); + + TxState getState(); +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/TxManagerSenderService.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/TxManagerSenderService.java new file mode 100644 index 0000000..c7db265 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/TxManagerSenderService.java @@ -0,0 +1,15 @@ +package com.codingapi.tm.manager.service; + +import com.codingapi.tm.netty.model.TxGroup; + +/** + * Created by lorne on 2017/6/9. + */ +public interface TxManagerSenderService { + + int confirm(TxGroup group); + + String sendMsg(String model, String msg, int delay); + + String sendCompensateMsg(String model, String groupId, String data,int startState); +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/TxManagerService.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/TxManagerService.java new file mode 100644 index 0000000..74cfd08 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/TxManagerService.java @@ -0,0 +1,79 @@ +package com.codingapi.tm.manager.service; + +import com.codingapi.tm.netty.model.TxGroup; + +/** + * Created by lorne on 2017/6/7. + */ + +public interface TxManagerService { + + + /** + * 创建事物组 + * + * @param groupId 补偿事务组id + */ + TxGroup createTransactionGroup(String groupId); + + + /** + * 添加事务组子对象 + * + * @return + */ + + TxGroup addTransactionGroup(String groupId, String taskId,int isGroup, String channelAddress, String methodStr); + + + /** + * 关闭事务组 + * @param groupId 事务组id + * @param state 事务状态 + * @return 0 事务存在补偿 1 事务正常 -1 事务强制回滚 + */ + int closeTransactionGroup(String groupId,int state); + + + void dealTxGroup(TxGroup txGroup, boolean hasOk ); + + + /** + * 删除事务组 + * @param txGroup 事务组 + */ + void deleteTxGroup(TxGroup txGroup); + + + /** + * 获取事务组信息 + * @param groupId 事务组id + * @return 事务组 + */ + TxGroup getTxGroup(String groupId); + + + /** + * 获取事务组的key + * @param groupId 事务组id + * @return key + */ + String getTxGroupKey(String groupId); + + + /** + * 检查事务组数据 + * @param groupId 事务组id + * @param taskId 任务id + * @return 本次请求的是否提交 1提交 0回滚 + */ + int cleanNotifyTransaction(String groupId, String taskId); + + + /** + * 设置强制回滚事务 + * @param groupId 事务组id + * @return true 成功 false 失败 + */ + boolean rollbackTransactionGroup(String groupId); +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/LoadBalanceServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/LoadBalanceServiceImpl.java new file mode 100644 index 0000000..3fc8f8a --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/LoadBalanceServiceImpl.java @@ -0,0 +1,54 @@ +package com.codingapi.tm.manager.service.impl; + +import com.codingapi.tm.config.ConfigReader; +import com.codingapi.tm.manager.service.LoadBalanceService; +import com.codingapi.tm.model.LoadBalanceInfo; +import com.codingapi.tm.redis.service.RedisServerService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * create by lorne on 2017/12/5 + */ +@Service +public class LoadBalanceServiceImpl implements LoadBalanceService { + + @Autowired + private RedisServerService redisServerService; + + @Autowired + private ConfigReader configReader; + + @Override + public boolean put(LoadBalanceInfo loadBalanceInfo) { + String groupName = getLoadBalanceGroupName(loadBalanceInfo.getGroupId()); + redisServerService.saveLoadBalance(groupName,loadBalanceInfo.getKey(),loadBalanceInfo.getData()); + return true; + } + + @Override + public LoadBalanceInfo get(String groupId, String key) { + String groupName = getLoadBalanceGroupName(groupId); + String bytes = redisServerService.getLoadBalance(groupName,key); + if(bytes==null) { + return null; + } + + LoadBalanceInfo loadBalanceInfo = new LoadBalanceInfo(); + loadBalanceInfo.setGroupId(groupId); + loadBalanceInfo.setKey(key); + loadBalanceInfo.setData(bytes); + return loadBalanceInfo; + } + + @Override + public boolean remove(String groupId) { + redisServerService.deleteKey(getLoadBalanceGroupName(groupId)); + return true; + } + + @Override + public String getLoadBalanceGroupName(String groupId) { + return configReader.getKeyPrefixLoadbalance()+groupId; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/MicroServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/MicroServiceImpl.java new file mode 100644 index 0000000..49f18f4 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/MicroServiceImpl.java @@ -0,0 +1,130 @@ +package com.codingapi.tm.manager.service.impl; + +import com.codingapi.tm.Constants; +import com.codingapi.tm.config.ConfigReader; +import com.codingapi.tm.framework.utils.SocketManager; +import com.codingapi.tm.manager.service.MicroService; +import com.codingapi.tm.model.TxServer; +import com.codingapi.tm.model.TxState; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * create by lorne on 2017/11/11 + */ +@Service +public class MicroServiceImpl implements MicroService { + + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private ConfigReader configReader; + + + @Autowired + private DiscoveryClient discoveryClient; + + + + private boolean isIp(String ipAddress) { + String ip = "([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}"; + Pattern pattern = Pattern.compile(ip); + Matcher matcher = pattern.matcher(ipAddress); + return matcher.matches(); + } + + + + @Override + public TxState getState() { + TxState state = new TxState(); + String ipAddress = "";//discoveryClient.getLocalServiceInstance().getHost(); + //TODO Spring boot 2.0.0没有discoveryClient.getLocalServiceInstance() 用InetAddress获取host + try { + ipAddress = InetAddress.getLocalHost().getHostAddress(); + } catch (Exception e) { + e.printStackTrace(); + } + if(!isIp(ipAddress)){ + ipAddress = "127.0.0.1"; + } + state.setIp(ipAddress); + state.setPort(Constants.socketPort); + state.setMaxConnection(SocketManager.getInstance().getMaxConnection()); + state.setNowConnection(SocketManager.getInstance().getNowConnection()); + state.setRedisSaveMaxTime(configReader.getRedisSaveMaxTime()); + state.setTransactionNettyDelayTime(configReader.getTransactionNettyDelayTime()); + state.setTransactionNettyHeartTime(configReader.getTransactionNettyHeartTime()); + state.setNotifyUrl(configReader.getCompensateNotifyUrl()); + state.setCompensate(configReader.isCompensateAuto()); + state.setCompensateTryTime(configReader.getCompensateTryTime()); + state.setCompensateMaxWaitTime(configReader.getCompensateMaxWaitTime()); + state.setSlbList(getServices()); + return state; + } + + private List getServices(){ + List urls = new ArrayList<>(); + List serviceInstances = discoveryClient.getInstances(tmKey); + for (ServiceInstance instanceInfo : serviceInstances) { + urls.add(instanceInfo.getUri().toASCIIString()); + } + return urls; + } + + @Override + public TxServer getServer() { + List urls= getServices(); + List states = new ArrayList<>(); + for(String url:urls){ + try { + TxState state = restTemplate.getForObject(url + "/tx/manager/state", TxState.class); + states.add(state); + } catch (Exception e) { + } + + } + if(states.size()<=1) { + TxState state = getState(); + if (state.getMaxConnection() > state.getNowConnection()) { + return TxServer.format(state); + } else { + return null; + } + }else{ + //找默认数据 + TxState state = getDefault(states,0); + if (state == null) { + //没有满足的默认数据 + return null; + } + return TxServer.format(state); + } + } + + private TxState getDefault(List states, int index) { + TxState state = states.get(index); + if (state.getMaxConnection() == state.getNowConnection()) { + index++; + if (states.size() - 1 >= index) { + return getDefault(states, index); + } else { + return null; + } + } else { + return state; + } + } + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerSenderServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerSenderServiceImpl.java new file mode 100644 index 0000000..e5ee4e2 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerSenderServiceImpl.java @@ -0,0 +1,323 @@ +package com.codingapi.tm.manager.service.impl; + + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; + +import com.codingapi.tm.Constants; +import com.codingapi.tm.compensate.service.CompensateService; +import com.codingapi.tm.config.ConfigReader; +import com.codingapi.tm.framework.utils.SocketUtils; +import com.codingapi.tm.manager.service.TxManagerSenderService; +import com.codingapi.tm.manager.service.TxManagerService; +import com.codingapi.tm.framework.utils.SocketManager; +import com.codingapi.tm.netty.model.TxGroup; +import com.codingapi.tm.model.ChannelSender; +import com.codingapi.tm.redis.service.RedisServerService; +import com.lorne.core.framework.utils.KidUtils; +import com.lorne.core.framework.utils.task.ConditionUtils; +import com.lorne.core.framework.utils.task.IBack; +import com.lorne.core.framework.utils.task.Task; +import com.lorne.core.framework.utils.thread.CountDownLatchHelper; +import com.lorne.core.framework.utils.thread.IExecute; +import com.codingapi.tm.netty.model.TxInfo; + + +import io.netty.channel.Channel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.concurrent.*; + + +/** + * Created by lorne on 2017/6/9. + */ +@Service +public class TxManagerSenderServiceImpl implements TxManagerSenderService { + + + private Logger logger = LoggerFactory.getLogger(TxManagerSenderServiceImpl.class); + + private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(100); + + private Executor threadPool = Executors.newFixedThreadPool(100); + + @Autowired + private TxManagerService txManagerService; + + @Autowired + private RedisServerService redisServerService; + + @Autowired + private ConfigReader configReader; + + @Autowired + private CompensateService compensateService; + + @Override + public int confirm(TxGroup txGroup) { + //绑定管道对象,检查网络 + setChannel(txGroup.getList()); + + //事务不满足直接回滚事务 + if (txGroup.getState()==0) { + transaction(txGroup, 0); + return 0; + } + + if(txGroup.getRollback()==1){ + transaction(txGroup, 0); + return -1; + } + + boolean hasOk = transaction(txGroup, 1); + txManagerService.dealTxGroup(txGroup,hasOk); + return hasOk?1:0; + } + + + /** + * 匹配管道 + * + * @param list + */ + private void setChannel(List list) { + for (TxInfo info : list) { + if(Constants.address.equals(info.getAddress())){ + Channel channel = SocketManager.getInstance().getChannelByModelName(info.getChannelAddress()); + if (channel != null &&channel.isActive()) { + ChannelSender sender = new ChannelSender(); + sender.setChannel(channel); + + info.setChannel(sender); + } + }else{ + ChannelSender sender = new ChannelSender(); + sender.setAddress(info.getAddress()); + sender.setModelName(info.getChannelAddress()); + + info.setChannel(sender); + } + } + } + + + + /** + * 事务提交或回归 + * + * @param checkSate + */ + private boolean transaction(final TxGroup txGroup, final int checkSate) { + + + if (checkSate == 1) { + + //补偿请求,加载历史数据 + if (txGroup.getIsCompensate() == 1) { + compensateService.reloadCompensate(txGroup); + } + + CountDownLatchHelper countDownLatchHelper = new CountDownLatchHelper(); + for (final TxInfo txInfo : txGroup.getList()) { + if (txInfo.getIsGroup() == 0) { + countDownLatchHelper.addExecute(new IExecute() { + @Override + public Boolean execute() { + if(txInfo.getChannel()==null){ + return false; + } + + final JSONObject jsonObject = new JSONObject(); + jsonObject.put("a", "t"); + + + if (txGroup.getIsCompensate() == 1) { //补偿请求 + jsonObject.put("c", txInfo.getIsCommit()); + } else { //正常业务 + jsonObject.put("c", checkSate); + } + + jsonObject.put("t", txInfo.getKid()); + final String key = KidUtils.generateShortUuid(); + jsonObject.put("k", key); + + Task task = ConditionUtils.getInstance().createTask(key); + + ScheduledFuture future = schedule(key, configReader.getTransactionNettyDelayTime()); + + threadAwaitSend(task, txInfo, jsonObject.toJSONString()); + + task.awaitTask(); + + if (!future.isDone()) { + future.cancel(false); + } + + try { + String data = (String) task.getBack().doing(); + // 1 成功 0 失败 -1 task为空 -2 超过 + boolean res = "1".equals(data); + + if (res) { + txInfo.setNotify(1); + } + + return res; + } catch (Throwable throwable) { + throwable.printStackTrace(); + return false; + } finally { + task.remove(); + } + } + }); + } + } + + List hasOks = countDownLatchHelper.execute().getData(); + + String key = configReader.getKeyPrefix() + txGroup.getGroupId(); + redisServerService.saveTransaction(key, txGroup.toJsonString()); + + boolean hasOk = true; + for (boolean bl : hasOks) { + if (!bl) { + hasOk = false; + break; + } + } + logger.info("--->" + hasOk + ",group:" + txGroup.getGroupId() + ",state:" + checkSate + ",list:" + txGroup.toJsonString()); + return hasOk; + }else{ + //回滚操作只发送通过不需要等待确认 + for (TxInfo txInfo : txGroup.getList()) { + if(txInfo.getChannel()!=null) { + if (txInfo.getIsGroup() == 0) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("a", "t"); + jsonObject.put("c", checkSate); + jsonObject.put("t", txInfo.getKid()); + String key = KidUtils.generateShortUuid(); + jsonObject.put("k", key); + txInfo.getChannel().send(jsonObject.toJSONString()); + } + } + } + txManagerService.deleteTxGroup(txGroup); + return true; + } + + } + + @Override + public String sendCompensateMsg(String model, String groupId, String data,int startState) { + JSONObject newCmd = new JSONObject(); + newCmd.put("a", "c"); + newCmd.put("d", data); + newCmd.put("ss", startState); + newCmd.put("g", groupId); + newCmd.put("k", KidUtils.generateShortUuid()); + return sendMsg(model, newCmd.toJSONString(), configReader.getRedisSaveMaxTime()); + } + + @Override + public String sendMsg(final String model,final String msg, int delay) { + JSONObject jsonObject = JSON.parseObject(msg); + String key = jsonObject.getString("k"); + + //创建Task + final Task task = ConditionUtils.getInstance().createTask(key); + + threadPool.execute(new Runnable() { + @Override + public void run() { + while (!task.isAwait() && !Thread.currentThread().interrupted()) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + Channel channel = SocketManager.getInstance().getChannelByModelName(model); + if (channel != null && channel.isActive()) { + SocketUtils.sendMsg(channel, msg); + } + } + }); + + ScheduledFuture future = schedule(key, delay); + + task.awaitTask(); + + if (!future.isDone()) { + future.cancel(false); + } + + try { + return (String)task.getBack().doing(); + } catch (Throwable throwable) { + return "-1"; + }finally { + task.remove(); + } + } + + + + private void threadAwaitSend(final Task task, final TxInfo txInfo, final String msg){ + threadPool.execute(new Runnable() { + @Override + public void run() { + while (!task.isAwait() && !Thread.currentThread().interrupted()) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + if(txInfo!=null&&txInfo.getChannel()!=null) { + txInfo.getChannel().send(msg,task); + }else{ + task.setBack(new IBack() { + @Override + public Object doing(Object... objs) throws Throwable { + return "-2"; + } + }); + task.signalTask(); + } + } + }); + + } + + + private ScheduledFuture schedule(final String key, int delayTime) { + ScheduledFuture future = executorService.schedule(new Runnable() { + @Override + public void run() { + Task task = ConditionUtils.getInstance().getTask(key); + if(task!=null&&!task.isNotify()) { + task.setBack(new IBack() { + @Override + public Object doing(Object... objs) throws Throwable { + return "-2"; + } + }); + task.signalTask(); + } + } + }, delayTime, TimeUnit.SECONDS); + + return future; + } + + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerServiceImpl.java new file mode 100644 index 0000000..da6e136 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/manager/service/impl/TxManagerServiceImpl.java @@ -0,0 +1,213 @@ +package com.codingapi.tm.manager.service.impl; + + +import com.codingapi.tm.Constants; +import com.codingapi.tm.compensate.service.CompensateService; +import com.codingapi.tm.manager.ModelInfoManager; +import com.codingapi.tm.manager.service.LoadBalanceService; +import com.codingapi.tm.manager.service.TxManagerSenderService; +import com.codingapi.tm.manager.service.TxManagerService; +import com.codingapi.tm.config.ConfigReader; +import com.codingapi.tm.model.ModelInfo; +import com.codingapi.tm.netty.model.TxGroup; +import com.codingapi.tm.netty.model.TxInfo; +import com.codingapi.tm.redis.service.RedisServerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * Created by lorne on 2017/6/7. + */ +@Service +public class TxManagerServiceImpl implements TxManagerService { + + + + @Autowired + private ConfigReader configReader; + + @Autowired + private RedisServerService redisServerService; + + + @Autowired + private TxManagerSenderService transactionConfirmService; + + + @Autowired + private LoadBalanceService loadBalanceService; + + @Autowired + private CompensateService compensateService; + + + private Logger logger = LoggerFactory.getLogger(TxManagerServiceImpl.class); + + + @Override + public TxGroup createTransactionGroup(String groupId) { + TxGroup txGroup = new TxGroup(); + if (compensateService.getCompensateByGroupId(groupId)!=null) { + txGroup.setIsCompensate(1); + } + + txGroup.setStartTime(System.currentTimeMillis()); + txGroup.setGroupId(groupId); + + String key = configReader.getKeyPrefix() + groupId; + redisServerService.saveTransaction(key, txGroup.toJsonString()); + + return txGroup; + } + + + @Override + public TxGroup addTransactionGroup(String groupId, String taskId, int isGroup, String channelAddress, String methodStr) { + String key = getTxGroupKey(groupId); + TxGroup txGroup = getTxGroup(groupId); + if (txGroup==null) { + return null; + } + TxInfo txInfo = new TxInfo(); + txInfo.setChannelAddress(channelAddress); + txInfo.setKid(taskId); + txInfo.setAddress(Constants.address); + txInfo.setIsGroup(isGroup); + txInfo.setMethodStr(methodStr); + + + ModelInfo modelInfo = ModelInfoManager.getInstance().getModelByChannelName(channelAddress); + if(modelInfo!=null) { + txInfo.setUniqueKey(modelInfo.getUniqueKey()); + txInfo.setModelIpAddress(modelInfo.getIpAddress()); + txInfo.setModel(modelInfo.getModel()); + } + + txGroup.addTransactionInfo(txInfo); + + redisServerService.saveTransaction(key, txGroup.toJsonString()); + + return txGroup; + } + + @Override + public boolean rollbackTransactionGroup(String groupId) { + String key = getTxGroupKey(groupId); + TxGroup txGroup = getTxGroup(groupId); + if (txGroup==null) { + return false; + } + txGroup.setRollback(1); + redisServerService.saveTransaction(key, txGroup.toJsonString()); + return true; + } + + @Override + public int cleanNotifyTransaction(String groupId, String taskId) { + int res = 0; + logger.info("start-cleanNotifyTransaction->groupId:"+groupId+",taskId:"+taskId); + String key = getTxGroupKey(groupId); + TxGroup txGroup = getTxGroup(groupId); + if (txGroup==null) { + logger.info("cleanNotifyTransaction - > txGroup is null "); + return res; + } + + if(txGroup.getHasOver()==0){ + + //整个事务回滚. + txGroup.setRollback(1); + redisServerService.saveTransaction(key, txGroup.toJsonString()); + + logger.info("cleanNotifyTransaction - > groupId "+groupId+" not over,all transaction must rollback !"); + return 0; + } + + if(txGroup.getRollback()==1){ + logger.info("cleanNotifyTransaction - > groupId "+groupId+" only rollback !"); + return 0; + } + + //更新数据 + boolean hasSet = false; + for (TxInfo info : txGroup.getList()) { + if (info.getKid().equals(taskId)) { + if(info.getNotify()==0&&info.getIsGroup()==0) { + info.setNotify(1); + hasSet = true; + res = 1; + + break; + } + } + } + + //判断是否都结束 + boolean isOver = true; + for (TxInfo info : txGroup.getList()) { + if (info.getIsGroup() == 0 && info.getNotify() == 0) { + isOver = false; + break; + } + } + + if (isOver) { + deleteTxGroup(txGroup); + } + + //有更新的数据,需要修改记录 + if(!isOver&&hasSet) { + redisServerService.saveTransaction(key, txGroup.toJsonString()); + } + + logger.info("end-cleanNotifyTransaction->groupId:"+groupId+",taskId:"+taskId+",res(1:commit,0:rollback):"+res); + return res; + } + + + @Override + public int closeTransactionGroup(String groupId,int state) { + String key = getTxGroupKey(groupId); + TxGroup txGroup = getTxGroup(groupId); + if(txGroup==null){ + return 0; + } + txGroup.setState(state); + txGroup.setHasOver(1); + redisServerService.saveTransaction(key,txGroup.toJsonString()); + return transactionConfirmService.confirm(txGroup); + } + + + @Override + public void dealTxGroup(TxGroup txGroup, boolean hasOk) { + if(hasOk) { + deleteTxGroup(txGroup); + } + } + + + @Override + public void deleteTxGroup(TxGroup txGroup) { + String groupId = txGroup.getGroupId(); + + String key = getTxGroupKey(groupId); + redisServerService.deleteKey(key); + + loadBalanceService.remove(groupId); + } + + + @Override + public TxGroup getTxGroup(String groupId) { + String key = getTxGroupKey(groupId); + return redisServerService.getTxGroupByKey(key); + } + + @Override + public String getTxGroupKey(String groupId) { + return configReader.getKeyPrefix() + groupId; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/ChannelSender.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/ChannelSender.java new file mode 100644 index 0000000..2777b38 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/ChannelSender.java @@ -0,0 +1,72 @@ +package com.codingapi.tm.model; + +import com.codingapi.tm.framework.utils.SocketUtils; +import com.lorne.core.framework.utils.http.HttpUtils; +import com.lorne.core.framework.utils.task.IBack; +import com.lorne.core.framework.utils.task.Task; +import io.netty.channel.Channel; +import org.apache.commons.lang.StringUtils; + +/** + * create by lorne on 2017/8/7 + */ +public class ChannelSender { + + + private Channel channel; + + private String address; + + private String modelName; + + public void setModelName(String modelName) { + this.modelName = modelName; + } + + public void setChannel(Channel channel) { + this.channel = channel; + } + + public void setAddress(String address) { + this.address = address; + } + + + public void send(String msg){ + if(channel!=null){ + SocketUtils.sendMsg(channel,msg); + } + + } + + public void send(String msg,Task task){ + if(channel!=null){ + SocketUtils.sendMsg(channel,msg); + }else{ + String url = String.format("http://%s/tx/manager/sendMsg",address); + final String res = HttpUtils.post(url,"msg="+msg+"&model="+modelName); + if(StringUtils.isNotEmpty(res)){ + if(task!=null) { + task.setBack(new IBack() { + @Override + public Object doing(Object... objs) throws Throwable { + return res; + } + }); + task.signalTask(); + } + }else{ + if(task!=null) { + task.setBack(new IBack() { + @Override + public Object doing(Object... objs) throws Throwable { + return "-2"; + } + }); + task.signalTask(); + } + } + } + + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/LoadBalanceInfo.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/LoadBalanceInfo.java new file mode 100644 index 0000000..5dae5ea --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/LoadBalanceInfo.java @@ -0,0 +1,39 @@ +package com.codingapi.tm.model; + +/** + * 负载均衡模块信息 + * create by lorne on 2017/12/5 + */ +public class LoadBalanceInfo { + + private String groupId; + + private String key; + + private String data; + + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/ModelInfo.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/ModelInfo.java new file mode 100644 index 0000000..2ff743d --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/ModelInfo.java @@ -0,0 +1,48 @@ +package com.codingapi.tm.model; + +/** + * 模块信息 + * create by lorne on 2017/11/13 + */ +public class ModelInfo { + + private String model; + + private String ipAddress; + + private String channelName; + + private String uniqueKey; + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public String getChannelName() { + return channelName; + } + + public void setChannelName(String channelName) { + this.channelName = channelName; + } + + public String getUniqueKey() { + return uniqueKey; + } + + public void setUniqueKey(String uniqueKey) { + this.uniqueKey = uniqueKey; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/ModelName.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/ModelName.java new file mode 100644 index 0000000..f6c008d --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/ModelName.java @@ -0,0 +1,28 @@ + + +package com.codingapi.tm.model; + +/** + * create by lorne on 2017/11/22 + */ +public class ModelName { + + private String name; + private int count; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/TxServer.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/TxServer.java new file mode 100644 index 0000000..83c4d8a --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/TxServer.java @@ -0,0 +1,69 @@ +package com.codingapi.tm.model; + +/** + * Created by lorne on 2017/7/1. + */ +public class TxServer { + + private String ip; + private int port; + private int heart; + private int delay; + private int compensateMaxWaitTime; + + public static TxServer format(TxState state) { + TxServer txServer = new TxServer(); + txServer.setIp(state.getIp()); + txServer.setPort(state.getPort()); + txServer.setHeart(state.getTransactionNettyHeartTime()); + txServer.setDelay(state.getTransactionNettyDelayTime()); + txServer.setCompensateMaxWaitTime(state.getCompensateMaxWaitTime()); + return txServer; + } + + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public int getHeart() { + return heart; + } + + public void setHeart(int heart) { + this.heart = heart; + } + + public int getDelay() { + return delay; + } + + public void setDelay(int delay) { + this.delay = delay; + } + + + public int getCompensateMaxWaitTime() { + return compensateMaxWaitTime; + } + + + public void setCompensateMaxWaitTime(int compensateMaxWaitTime) { + this.compensateMaxWaitTime = compensateMaxWaitTime; + } + + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/TxState.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/TxState.java new file mode 100644 index 0000000..07c4008 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/model/TxState.java @@ -0,0 +1,170 @@ +package com.codingapi.tm.model; + +import java.util.List; + +/** + * Created by lorne on 2017/7/1. + */ +public class TxState { + + /** + * socket ip + */ + private String ip; + /** + * socket port + */ + private int port; + + /** + * max connection + */ + private int maxConnection; + + /** + * now connection + */ + private int nowConnection; + + + /** + * transaction_netty_heart_time + */ + private int transactionNettyHeartTime; + + /** + * transaction_netty_delay_time + */ + private int transactionNettyDelayTime; + + + /** + * redis_save_max_time + */ + private int redisSaveMaxTime; + + + /** + * 回调地址 + */ + private String notifyUrl; + + /** + * 自动补偿 + */ + private boolean isCompensate; + + /** + * 补偿尝试时间 + */ + private int compensateTryTime; + + /** + * slb list + */ + private List slbList; + + /** + * 自动补偿间隔时间 + */ + private int compensateMaxWaitTime; + + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public int getMaxConnection() { + return maxConnection; + } + + public void setMaxConnection(int maxConnection) { + this.maxConnection = maxConnection; + } + + public int getNowConnection() { + return nowConnection; + } + + public void setNowConnection(int nowConnection) { + this.nowConnection = nowConnection; + } + + public boolean isCompensate() { + return isCompensate; + } + + public void setCompensate(boolean compensate) { + isCompensate = compensate; + } + + public int getCompensateTryTime() { + return compensateTryTime; + } + + public void setCompensateTryTime(int compensateTryTime) { + this.compensateTryTime = compensateTryTime; + } + + public int getRedisSaveMaxTime() { + return redisSaveMaxTime; + } + + public void setRedisSaveMaxTime(int redisSaveMaxTime) { + this.redisSaveMaxTime = redisSaveMaxTime; + } + + public List getSlbList() { + return slbList; + } + + public void setSlbList(List slbList) { + this.slbList = slbList; + } + + public int getTransactionNettyHeartTime() { + return transactionNettyHeartTime; + } + + public void setTransactionNettyHeartTime(int transactionNettyHeartTime) { + this.transactionNettyHeartTime = transactionNettyHeartTime; + } + + public int getTransactionNettyDelayTime() { + return transactionNettyDelayTime; + } + + public void setTransactionNettyDelayTime(int transactionNettyDelayTime) { + this.transactionNettyDelayTime = transactionNettyDelayTime; + } + + public String getNotifyUrl() { + return notifyUrl; + } + + public void setNotifyUrl(String notifyUrl) { + this.notifyUrl = notifyUrl; + } + + public int getCompensateMaxWaitTime() { + return compensateMaxWaitTime; + } + + public void setCompensateMaxWaitTime(int compensateMaxWaitTime) { + this.compensateMaxWaitTime = compensateMaxWaitTime; + } + + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/handler/TxCoreServerHandler.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/handler/TxCoreServerHandler.java new file mode 100644 index 0000000..ed60b07 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/handler/TxCoreServerHandler.java @@ -0,0 +1,123 @@ +package com.codingapi.tm.netty.handler; + +/** + * Created by lorne on 2017/6/29. + */ + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tm.framework.utils.SocketManager; +import com.codingapi.tm.framework.utils.SocketUtils; +import com.codingapi.tm.manager.ModelInfoManager; +import com.codingapi.tm.netty.service.IActionService; +import com.codingapi.tm.netty.service.NettyService; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.timeout.IdleState; +import io.netty.handler.timeout.IdleStateEvent; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.Executor; + +/** + * Handles a server-side channel. + */ + +@ChannelHandler.Sharable +public class TxCoreServerHandler extends ChannelInboundHandlerAdapter { // (1) + + private NettyService nettyService; + + + private Logger logger = LoggerFactory.getLogger(TxCoreServerHandler.class); + + + private Executor threadPool; + + + public TxCoreServerHandler(Executor threadPool,NettyService nettyService) { + this.threadPool = threadPool; + this.nettyService = nettyService; + } + + @Override + public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception { + final String json = SocketUtils.getJson(msg); + logger.debug("request->"+json); + threadPool.execute(new Runnable() { + @Override + public void run() { + service(json,ctx); + } + }); + } + + private void service(String json,ChannelHandlerContext ctx){ + if (StringUtils.isNotEmpty(json)) { + JSONObject jsonObject = JSONObject.parseObject(json); + String action = jsonObject.getString("a"); + String key = jsonObject.getString("k"); + JSONObject params = JSONObject.parseObject(jsonObject.getString("p")); + String channelAddress = ctx.channel().remoteAddress().toString(); + + IActionService actionService = nettyService.getActionService(action); + + String res = actionService.execute(channelAddress,key,params); + + JSONObject resObj = new JSONObject(); + resObj.put("k", key); + resObj.put("d", res); + + SocketUtils.sendMsg(ctx,resObj.toString()); + + } + } + + @Override + public void channelRegistered(ChannelHandlerContext ctx) throws Exception { + + //是否到达最大上线连接数 + if (SocketManager.getInstance().isAllowConnection()) { + SocketManager.getInstance().addClient(ctx.channel()); + } else { + ctx.close(); + } + super.channelRegistered(ctx); + } + + @Override + public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { + + SocketManager.getInstance().removeClient(ctx.channel()); + String modelName = ctx.channel().remoteAddress().toString(); + SocketManager.getInstance().outLine(modelName); + + ModelInfoManager.getInstance().removeModelInfo(modelName); + super.channelUnregistered(ctx); + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + ctx.flush(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + cause.printStackTrace(); + //ctx.close(); + } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + //心跳配置 + if (IdleStateEvent.class.isAssignableFrom(evt.getClass())) { + IdleStateEvent event = (IdleStateEvent) evt; + if (event.state() == IdleState.READER_IDLE) { + ctx.close(); + } + } + } + +} \ No newline at end of file diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/model/TxGroup.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/model/TxGroup.java new file mode 100644 index 0000000..6432c61 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/model/TxGroup.java @@ -0,0 +1,183 @@ +package com.codingapi.tm.netty.model; + + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by lorne on 2017/6/7. + */ +public class TxGroup { + + private String groupId; + + private long startTime; + + private long nowTime; + + private int state; + + private int hasOver; + + /** + * 补偿请求 + */ + private int isCompensate; + + + /** + * 是否强制回滚(1:开启,0:关闭) + */ + private int rollback = 0 ; + + private List list; + + public TxGroup() { + list = new ArrayList(); + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + + public long getStartTime() { + return startTime; + } + + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + public int getIsCompensate() { + return isCompensate; + } + + public void setIsCompensate(int isCompensate) { + this.isCompensate = isCompensate; + } + + public int getState() { + return state; + } + + public void setState(int state) { + this.state = state; + } + + public void addTransactionInfo(TxInfo info) { + list.add(info); + } + + public long getNowTime() { + return nowTime; + } + + public void setNowTime(long nowTime) { + this.nowTime = nowTime; + } + + + public int getHasOver() { + return hasOver; + } + + public void setHasOver(int hasOver) { + this.hasOver = hasOver; + } + + public int getRollback() { + return rollback; + } + + public void setRollback(int rollback) { + this.rollback = rollback; + } + + public static TxGroup parser(String json) { + try { + JSONObject jsonObject = JSONObject.parseObject(json); + TxGroup txGroup = new TxGroup(); + txGroup.setGroupId(jsonObject.getString("g")); + txGroup.setStartTime(jsonObject.getLong("st")); + txGroup.setNowTime(jsonObject.getLong("nt")); + txGroup.setState(jsonObject.getInteger("s")); + txGroup.setIsCompensate(jsonObject.getInteger("i")); + txGroup.setRollback(jsonObject.getInteger("r")); + txGroup.setHasOver(jsonObject.getInteger("o")); + JSONArray array = jsonObject.getJSONArray("l"); + int length = array.size(); + for (int i = 0; i < length; i++) { + JSONObject object = array.getJSONObject(i); + TxInfo info = new TxInfo(); + info.setKid(object.getString("k")); + info.setChannelAddress(object.getString("ca")); + info.setNotify(object.getInteger("n")); + info.setIsGroup(object.getInteger("ig")); + info.setAddress(object.getString("a")); + info.setUniqueKey(object.getString("u")); + + info.setModel(object.getString("mn")); + info.setModelIpAddress(object.getString("ip")); + info.setMethodStr(object.getString("ms")); + + txGroup.getList().add(info); + } + return txGroup; + + } catch (Exception e) { + return null; + } + + } + + public String toJsonString(boolean noList) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("g", getGroupId()); + jsonObject.put("st", getStartTime()); + jsonObject.put("nt", getNowTime()); + jsonObject.put("s", getState()); + jsonObject.put("i", getIsCompensate()); + jsonObject.put("r", getRollback()); + jsonObject.put("o",getHasOver()); + if(noList) { + JSONArray jsonArray = new JSONArray(); + for (TxInfo info : getList()) { + JSONObject item = new JSONObject(); + item.put("k", info.getKid()); + item.put("ca", info.getChannelAddress()); + item.put("n", info.getNotify()); + item.put("ig", info.getIsGroup()); + item.put("a", info.getAddress()); + item.put("u", info.getUniqueKey()); + + item.put("mn", info.getModel()); + item.put("ip", info.getModelIpAddress()); + item.put("ms", info.getMethodStr()); + + + jsonArray.add(item); + } + jsonObject.put("l", jsonArray); + } + return jsonObject.toString(); + } + + public String toJsonString() { + return toJsonString(true); + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/model/TxInfo.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/model/TxInfo.java new file mode 100644 index 0000000..493ccfe --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/model/TxInfo.java @@ -0,0 +1,174 @@ +package com.codingapi.tm.netty.model; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tm.model.ChannelSender; +import com.lorne.core.framework.model.JsonModel; + +/** + * Created by lorne on 2017/6/7. + */ +public class TxInfo extends JsonModel { + + /** + * 任务唯一标示 + */ + private String kid; + + /** + * 模块管道名称(netty管道名称) + */ + private String channelAddress; + + /** + * 是否通知成功 + */ + private int notify; + + /** + * 0 不组合 + * 1 组合 + */ + private int isGroup; + + /** + * tm识别标示 + */ + private String address; + + /** + * tx识别标示 + */ + private String uniqueKey; + + + /** + * 管道发送数据 + */ + private ChannelSender channel; + + + /** + * 业务方法名称 + */ + private String methodStr; + + /** + * 模块名称 + */ + private String model; + + /** + * 模块地址 + */ + private String modelIpAddress; + + /** + * 是否提交(临时数据) + */ + private int isCommit; + + public int getIsCommit() { + return isCommit; + } + + public void setIsCommit(int isCommit) { + this.isCommit = isCommit; + } + + public String getMethodStr() { + return methodStr; + } + + public void setMethodStr(String methodStr) { + this.methodStr = methodStr; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public String getModelIpAddress() { + return modelIpAddress; + } + + public void setModelIpAddress(String modelIpAddress) { + this.modelIpAddress = modelIpAddress; + } + + public String getKid() { + return kid; + } + + public void setKid(String kid) { + this.kid = kid; + } + + public ChannelSender getChannel() { + return channel; + } + + public void setChannel(ChannelSender channel) { + this.channel = channel; + } + + public String getChannelAddress() { + return channelAddress; + } + + public void setChannelAddress(String channelAddress) { + this.channelAddress = channelAddress; + } + + public int getNotify() { + return notify; + } + + public void setNotify(int notify) { + this.notify = notify; + } + + public int getIsGroup() { + return isGroup; + } + + public void setIsGroup(int isGroup) { + this.isGroup = isGroup; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getUniqueKey() { + return uniqueKey; + } + + public void setUniqueKey(String uniqueKey) { + this.uniqueKey = uniqueKey; + } + + @Override + public String toString() { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("kid",getKid()); + jsonObject.put("channelAddress", getChannelAddress()); + jsonObject.put("notify",getNotify()); + jsonObject.put("isGroup",getIsGroup()); + jsonObject.put("address",getAddress()); + jsonObject.put("uniqueKey",getUniqueKey()); + + jsonObject.put("model", getModel()); + jsonObject.put("modelIpAddress", getModelIpAddress()); + jsonObject.put("methodStr", getMethodStr()); + + return jsonObject.toString(); + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/IActionService.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/IActionService.java new file mode 100644 index 0000000..5a7926b --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/IActionService.java @@ -0,0 +1,13 @@ +package com.codingapi.tm.netty.service; + +import com.alibaba.fastjson.JSONObject; + +/** + * create by lorne on 2017/11/11 + */ +public interface IActionService { + + + String execute(String channelAddress,String key,JSONObject params); + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/NettyServerService.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/NettyServerService.java new file mode 100644 index 0000000..64253dc --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/NettyServerService.java @@ -0,0 +1,13 @@ +package com.codingapi.tm.netty.service; + +/** + * Created by lorne on 2017/6/30. + */ +public interface NettyServerService { + + void start(); + + void close(); + + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/NettyService.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/NettyService.java new file mode 100644 index 0000000..19450b2 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/NettyService.java @@ -0,0 +1,11 @@ +package com.codingapi.tm.netty.service; + +/** + * create by lorne on 2017/11/11 + */ +public interface NettyService { + + + + IActionService getActionService(String action); +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionATGServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionATGServiceImpl.java new file mode 100644 index 0000000..85adf62 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionATGServiceImpl.java @@ -0,0 +1,39 @@ +package com.codingapi.tm.netty.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tm.manager.service.TxManagerService; +import com.codingapi.tm.netty.model.TxGroup; +import com.codingapi.tm.netty.service.IActionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 添加事务组 + * create by lorne on 2017/11/11 + */ +@Service(value = "atg") +public class ActionATGServiceImpl implements IActionService{ + + + @Autowired + private TxManagerService txManagerService; + + @Override + public String execute(String channelAddress,String key,JSONObject params ) { + String res = ""; + String groupId = params.getString("g"); + String taskId = params.getString("t"); + String methodStr = params.getString("ms"); + int isGroup = params.getInteger("s"); + + TxGroup txGroup = txManagerService.addTransactionGroup(groupId, taskId, isGroup, channelAddress, methodStr); + + if(txGroup!=null) { + txGroup.setNowTime(System.currentTimeMillis()); + res = txGroup.toJsonString(false); + }else { + res = ""; + } + return res; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCGServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCGServiceImpl.java new file mode 100644 index 0000000..a0648ac --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCGServiceImpl.java @@ -0,0 +1,34 @@ +package com.codingapi.tm.netty.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tm.manager.service.TxManagerService; +import com.codingapi.tm.netty.model.TxGroup; +import com.codingapi.tm.netty.service.IActionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 创建事务组 + * create by lorne on 2017/11/11 + */ +@Service(value = "cg") +public class ActionCGServiceImpl implements IActionService{ + + + @Autowired + private TxManagerService txManagerService; + + @Override + public String execute(String channelAddress, String key, JSONObject params ) { + String res = ""; + String groupId = params.getString("g"); + TxGroup txGroup = txManagerService.createTransactionGroup(groupId); + if(txGroup!=null) { + txGroup.setNowTime(System.currentTimeMillis()); + res = txGroup.toJsonString(false); + }else { + res = ""; + } + return res; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCKGServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCKGServiceImpl.java new file mode 100644 index 0000000..4a8d254 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCKGServiceImpl.java @@ -0,0 +1,30 @@ +package com.codingapi.tm.netty.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tm.manager.service.TxManagerService; +import com.codingapi.tm.netty.service.IActionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 检查事务组 + * create by lorne on 2017/11/11 + */ +@Service(value = "ckg") +public class ActionCKGServiceImpl implements IActionService{ + + + @Autowired + private TxManagerService txManagerService; + + @Override + public String execute(String channelAddress, String key, JSONObject params ) { + String res = ""; + String groupId = params.getString("g"); + String taskId = params.getString("t"); + int bs = txManagerService.cleanNotifyTransaction(groupId,taskId); + + res = String.valueOf(bs); + return res; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCServiceImpl.java new file mode 100644 index 0000000..87cd5c9 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCServiceImpl.java @@ -0,0 +1,14 @@ +package com.codingapi.tm.netty.service.impl; + +import com.codingapi.tm.netty.service.IActionService; +import org.springframework.stereotype.Service; + +/** + * 补偿回调 + * create by lorne on 2017/11/11 + */ +@Service(value = "c") +public class ActionCServiceImpl extends BaseSignalTaskService implements IActionService { + + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCTGServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCTGServiceImpl.java new file mode 100644 index 0000000..a4ac3a1 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionCTGServiceImpl.java @@ -0,0 +1,27 @@ +package com.codingapi.tm.netty.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tm.manager.service.TxManagerService; +import com.codingapi.tm.netty.service.IActionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 关闭事务组 + * create by lorne on 2017/11/11 + */ +@Service(value = "ctg") +public class ActionCTGServiceImpl implements IActionService{ + + + @Autowired + private TxManagerService txManagerService; + + @Override + public String execute(String channelAddress, String key, JSONObject params ) { + String groupId = params.getString("g"); + int state = params.getInteger("s"); + String res = String.valueOf(txManagerService.closeTransactionGroup(groupId,state)); + return res; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionGLBServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionGLBServiceImpl.java new file mode 100644 index 0000000..6e282e4 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionGLBServiceImpl.java @@ -0,0 +1,37 @@ +package com.codingapi.tm.netty.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tm.manager.service.LoadBalanceService; +import com.codingapi.tm.model.LoadBalanceInfo; +import com.codingapi.tm.netty.service.IActionService; +import com.lorne.core.framework.utils.encode.Base64Utils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 获取负载模块信息 + * create by lorne on 2017/11/11 + */ +@Service(value = "glb") +public class ActionGLBServiceImpl implements IActionService{ + + + @Autowired + private LoadBalanceService loadBalanceService; + + + @Override + public String execute(String channelAddress, String key, JSONObject params ) { + String res; + String groupId = params.getString("g"); + String k = params.getString("k"); + + LoadBalanceInfo loadBalanceInfo = loadBalanceService.get(groupId,k); + if(loadBalanceInfo==null){ + res = ""; + }else{ + res = loadBalanceInfo.getData(); + } + return res; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionHServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionHServiceImpl.java new file mode 100644 index 0000000..a479529 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionHServiceImpl.java @@ -0,0 +1,25 @@ +package com.codingapi.tm.netty.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tm.config.ConfigReader; +import com.codingapi.tm.manager.service.TxManagerService; +import com.codingapi.tm.netty.service.IActionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 心跳包 + * create by lorne on 2017/11/11 + */ +@Service(value = "h") +public class ActionHServiceImpl implements IActionService{ + + + @Autowired + private ConfigReader configReader; + + @Override + public String execute(String channelAddress, String key, JSONObject params ) { + return String.valueOf(configReader.getTransactionNettyDelayTime()); + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionPLBServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionPLBServiceImpl.java new file mode 100644 index 0000000..702ceb6 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionPLBServiceImpl.java @@ -0,0 +1,38 @@ +package com.codingapi.tm.netty.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tm.manager.service.LoadBalanceService; +import com.codingapi.tm.model.LoadBalanceInfo; +import com.codingapi.tm.netty.service.IActionService; +import com.lorne.core.framework.utils.encode.Base64Utils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 添加负载模块信息 + * create by lorne on 2017/11/11 + */ +@Service(value = "plb") +public class ActionPLBServiceImpl implements IActionService{ + + + @Autowired + private LoadBalanceService loadBalanceService; + + + @Override + public String execute(String channelAddress, String key, JSONObject params ) { + + String groupId = params.getString("g"); + String k = params.getString("k"); + String data = params.getString("d"); + + LoadBalanceInfo loadBalanceInfo = new LoadBalanceInfo(); + loadBalanceInfo.setData(data); + loadBalanceInfo.setKey(k); + loadBalanceInfo.setGroupId(groupId); + boolean ok = loadBalanceService.put(loadBalanceInfo); + + return ok?"1":"0"; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionRGServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionRGServiceImpl.java new file mode 100644 index 0000000..63ca280 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionRGServiceImpl.java @@ -0,0 +1,28 @@ +package com.codingapi.tm.netty.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tm.manager.service.TxManagerService; +import com.codingapi.tm.netty.service.IActionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 强制回滚事务组 + * create by lorne on 2017/11/11 + */ +@Service(value = "rg") +public class ActionRGServiceImpl implements IActionService{ + + + @Autowired + private TxManagerService txManagerService; + + @Override + public String execute(String channelAddress, String key, JSONObject params ) { + String res = ""; + String groupId = params.getString("g"); + boolean bs = txManagerService.rollbackTransactionGroup(groupId); + res = bs ? "1" : "0"; + return res; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionTServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionTServiceImpl.java new file mode 100644 index 0000000..7407a19 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionTServiceImpl.java @@ -0,0 +1,14 @@ +package com.codingapi.tm.netty.service.impl; + +import com.codingapi.tm.netty.service.IActionService; +import org.springframework.stereotype.Service; + +/** + * 通知事务回调 + * create by lorne on 2017/11/11 + */ +@Service(value = "t") +public class ActionTServiceImpl extends BaseSignalTaskService implements IActionService { + + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionUMIServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionUMIServiceImpl.java new file mode 100644 index 0000000..eb1d3ec --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/ActionUMIServiceImpl.java @@ -0,0 +1,39 @@ +package com.codingapi.tm.netty.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tm.framework.utils.SocketManager; +import com.codingapi.tm.manager.ModelInfoManager; +import com.codingapi.tm.model.ModelInfo; +import com.codingapi.tm.netty.service.IActionService; +import org.springframework.stereotype.Service; + +/** + * 上传模块信息 + * create by lorne on 2017/11/11 + */ +@Service(value = "umi") +public class ActionUMIServiceImpl implements IActionService { + + + @Override + public String execute(String channelAddress, String key, JSONObject params) { + String res = "1"; + + String uniqueKey = params.getString("u"); + String ipAddress = params.getString("i"); + String model = params.getString("m"); + + + ModelInfo modelInfo = new ModelInfo(); + modelInfo.setChannelName(channelAddress); + modelInfo.setIpAddress(ipAddress); + modelInfo.setModel(model); + modelInfo.setUniqueKey(uniqueKey); + + ModelInfoManager.getInstance().addModelInfo(modelInfo); + + SocketManager.getInstance().onLine(channelAddress, uniqueKey); + + return res; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/BaseSignalTaskService.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/BaseSignalTaskService.java new file mode 100644 index 0000000..ae77e24 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/BaseSignalTaskService.java @@ -0,0 +1,28 @@ +package com.codingapi.tm.netty.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.lorne.core.framework.utils.task.ConditionUtils; +import com.lorne.core.framework.utils.task.IBack; +import com.lorne.core.framework.utils.task.Task; + +/** + * create by lorne on 2017/11/13 + */ +public class BaseSignalTaskService { + + public String execute(String channelAddress, String key, JSONObject params) { + String res = ""; + final String data = params.getString("d"); + Task task = ConditionUtils.getInstance().getTask(key); + if (task != null) { + task.setBack(new IBack() { + @Override + public Object doing(Object... objs) throws Throwable { + return data; + } + }); + task.signalTask(); + } + return res; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/NettyServerServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/NettyServerServiceImpl.java new file mode 100644 index 0000000..4efa436 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/NettyServerServiceImpl.java @@ -0,0 +1,103 @@ +package com.codingapi.tm.netty.service.impl; + +import com.codingapi.tm.Constants; +import com.codingapi.tm.config.ConfigReader; +import com.codingapi.tm.netty.handler.TxCoreServerHandler; +import com.codingapi.tm.netty.service.NettyServerService; +import com.codingapi.tm.netty.service.NettyService; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import io.netty.handler.codec.LengthFieldPrepender; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import io.netty.handler.timeout.IdleStateHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +/** + * Created by lorne on 2017/6/30. + */ +@Service +public class NettyServerServiceImpl implements NettyServerService,DisposableBean { + + + @Autowired + private NettyService nettyService; + + private Logger logger = LoggerFactory.getLogger(NettyServerServiceImpl.class); + + private EventLoopGroup bossGroup; + private EventLoopGroup workerGroup; + + private TxCoreServerHandler txCoreServerHandler; + + private ExecutorService threadPool = Executors.newFixedThreadPool(100); + + @Autowired + private ConfigReader configReader; + + + @Override + public void start() { + final int heartTime = configReader.getTransactionNettyHeartTime()+10; + txCoreServerHandler = new TxCoreServerHandler(threadPool,nettyService); + bossGroup = new NioEventLoopGroup(50); // (1) + workerGroup = new NioEventLoopGroup(); + try { + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .option(ChannelOption.SO_BACKLOG, 100) + .handler(new LoggingHandler(LogLevel.INFO)) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ch.pipeline().addLast("timeout", new IdleStateHandler(heartTime, heartTime, heartTime, TimeUnit.SECONDS)); + + ch.pipeline().addLast(new LengthFieldPrepender(4, false)); + ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4)); + + ch.pipeline().addLast(txCoreServerHandler); + } + }); + + // Start the server. + b.bind(Constants.socketPort); + logger.info("Socket started on port(s): " + Constants.socketPort + " (socket)"); + + } catch (Exception e) { + // Shut down all event loops to terminate all threads. + e.printStackTrace(); + } + } + + @Override + public void close() { + if (workerGroup != null) { + workerGroup.shutdownGracefully(); + } + if (bossGroup != null) { + bossGroup.shutdownGracefully(); + } + + } + + @Override + public void destroy() throws Exception { + close(); + threadPool.shutdown(); + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/NettyServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/NettyServiceImpl.java new file mode 100644 index 0000000..033a990 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/netty/service/impl/NettyServiceImpl.java @@ -0,0 +1,22 @@ +package com.codingapi.tm.netty.service.impl; + +import com.codingapi.tm.netty.service.IActionService; +import com.codingapi.tm.netty.service.NettyService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; + +/** + * create by lorne on 2017/11/11 + */ +@Service +public class NettyServiceImpl implements NettyService{ + + @Autowired + private ApplicationContext spring; + + @Override + public IActionService getActionService(String action) { + return spring.getBean(action,IActionService.class); + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/JedisClusterConfig.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/JedisClusterConfig.java new file mode 100644 index 0000000..6f71124 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/JedisClusterConfig.java @@ -0,0 +1,89 @@ +package com.codingapi.tm.redis; + + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.core.env.MapPropertySource; +import org.springframework.data.redis.connection.RedisClusterConfiguration; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.JedisCluster; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Created by lorne on 2017/10/31. + */ +@ConditionalOnClass({JedisCluster.class}) +@EnableConfigurationProperties(RedisProperties.class) +public class JedisClusterConfig { + + @Autowired + private RedisProperties redisProperties; + + @Bean + public JedisCluster jedisClusterFactory() { + String[] serverArray = redisProperties.getNodes().split(","); + Set nodes = new HashSet(); + for (String ipPort: serverArray) { + String[] ipPortPair = ipPort.split(":"); + nodes.add(new HostAndPort(ipPortPair[0].trim(),Integer.valueOf(ipPortPair[1].trim()))); + } + return new JedisCluster(nodes, redisProperties.getCommandTimeout()); + } + + @Bean + public RedisTemplate redisTemplateFactory(){ + RedisTemplate redisTemplate =new RedisTemplate(); + redisTemplate.setConnectionFactory(jedisConnectionFactory()); + + //指定具体序列化方式 不过这种方式不是很好,一个系统中可能对应值的类型不一样,如果全部使用StringRedisSerializer 序列化 + //会照成其他类型报错,所以还是推荐使用第一种,直接指定泛型的类型,spring 会根据指定类型序列化。 +// redisTemplate.setKeySerializer( new StringRedisSerializer()); +// redisTemplate.setValueSerializer(new StringRedisSerializer()); +// redisTemplate.setHashKeySerializer(new StringRedisSerializer()); +// redisTemplate.setHashValueSerializer(new StringRedisSerializer()); + return redisTemplate; + } + + + /** + * redisCluster配置 + * @return + */ + @Bean + public RedisClusterConfiguration redisClusterConfiguration() { + Map source = new HashMap(); + source.put("spring.redis.cluster.nodes", redisProperties.getNodes()); + source.put("spring.redis.cluster.timeout", redisProperties.getCommandTimeout()); + return new RedisClusterConfiguration(new MapPropertySource("RedisClusterConfiguration", source)); + } + + + /** + * 其实在JedisConnectionFactory的afterPropertiesSet()方法 中 + * if(cluster !=null) this.cluster =createCluster(); + * 也就是当 + * spring.redis.cluster.nodes 配置好的情况下,就可以实例化 JedisCluster. + * 也就是说,我们使用JedisCluster 的方式只需要在application.properties 配置文件中 + * + * #redis cluster + * spring.redis.cluster.nodes=127.0.0.1:7000,127.0.0.1:7001,127.0.0.1:7002 + * + * RedisTemplate.afterPropertiesSet() 中查看到最终方法中使用了JedisCluster 对象。 + * 也就是说 redisTemplate依赖jedis ,内部操作的就是jedis,同理内部也操作jedisCluster. + * + * + * @return + */ + @Bean + public JedisConnectionFactory jedisConnectionFactory() { + return new JedisConnectionFactory(redisClusterConfiguration()); + } +} \ No newline at end of file diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/RedisConfig.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/RedisConfig.java new file mode 100644 index 0000000..81ceae8 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/RedisConfig.java @@ -0,0 +1,44 @@ +package com.codingapi.tm.redis; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import redis.clients.jedis.JedisPoolConfig; + +/** + * Created by lorne on 2017/7/5. + */ + +@EnableAutoConfiguration +public class RedisConfig { + + private static Logger logger = LoggerFactory.getLogger(RedisConfig.class); + + @Bean + @ConfigurationProperties(prefix = "spring.redis") + public JedisPoolConfig getRedisConfig() { + JedisPoolConfig config = new JedisPoolConfig(); + return config; + } + + @Bean + @ConfigurationProperties(prefix = "spring.redis") + public JedisConnectionFactory getConnectionFactory() { + JedisConnectionFactory factory = new JedisConnectionFactory(); + JedisPoolConfig config = getRedisConfig(); + factory.setPoolConfig(config); + logger.info("JedisConnectionFactory bean init success."); + return factory; + } + + + @Bean + public RedisTemplate getRedisTemplate() { + return new StringRedisTemplate(getConnectionFactory()); + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/RedisProperties.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/RedisProperties.java new file mode 100644 index 0000000..bd1815c --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/RedisProperties.java @@ -0,0 +1,35 @@ +package com.codingapi.tm.redis; + + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * Created by lorne on 2017/10/31. + */ + +@Component +@ConfigurationProperties(prefix = "spring.redis.cluster") +public class RedisProperties { + + + private String nodes; + + private Integer commandTimeout; + + public String getNodes() { + return nodes; + } + + public void setNodes(String nodes) { + this.nodes = nodes; + } + + public Integer getCommandTimeout() { + return commandTimeout; + } + + public void setCommandTimeout(Integer commandTimeout) { + this.commandTimeout = commandTimeout; + } +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/service/RedisServerService.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/service/RedisServerService.java new file mode 100644 index 0000000..0fadc5e --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/service/RedisServerService.java @@ -0,0 +1,34 @@ +package com.codingapi.tm.redis.service; + +import com.codingapi.tm.netty.model.TxGroup; + +import java.util.List; + +/** + * create by lorne on 2017/11/11 + */ +public interface RedisServerService { + + String loadNotifyJson(); + + void saveTransaction(String key, String json); + + TxGroup getTxGroupByKey(String key); + + void saveCompensateMsg(String name, String json); + + List getKeys(String key); + + List getValuesByKeys(List keys); + + String getValueByKey(String key); + + void deleteKey(String key); + + void saveLoadBalance(String groupName,String key,String data); + + + String getLoadBalance(String groupName,String key); + + +} diff --git a/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/service/impl/RedisServerServiceImpl.java b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/service/impl/RedisServerServiceImpl.java new file mode 100644 index 0000000..2b28ff3 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/java/com/codingapi/tm/redis/service/impl/RedisServerServiceImpl.java @@ -0,0 +1,115 @@ +package com.codingapi.tm.redis.service.impl; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.codingapi.tm.config.ConfigReader; +import com.codingapi.tm.netty.model.TxGroup; +import com.codingapi.tm.redis.service.RedisServerService; +import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * create by lorne on 2017/11/11 + */ +@Service +public class RedisServerServiceImpl implements RedisServerService{ + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private ConfigReader configReader; + + + public String loadNotifyJson() { + Set keys = redisTemplate.keys(configReader.getKeyPrefixCompensate()+"*"); + ValueOperations value = redisTemplate.opsForValue(); + JSONArray jsonArray = new JSONArray(); + for(String key:keys){ + String json = value.get(key); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("key",key); + jsonObject.put("value",JSONObject.parse(json)); + jsonArray.add(jsonObject); + } + return jsonArray.toJSONString(); + } + + @Override + public void saveTransaction(String key, String json) { + ValueOperations value = redisTemplate.opsForValue(); + value.set(key, json, configReader.getRedisSaveMaxTime(), TimeUnit.SECONDS); + } + + + @Override + public TxGroup getTxGroupByKey(String key) { + ValueOperations value = redisTemplate.opsForValue(); + String json = value.get(key); + if (StringUtils.isEmpty(json)) { + return null; + } + return TxGroup.parser(json); + } + + + @Override + public void saveCompensateMsg(String name, String json) { + ValueOperations value = redisTemplate.opsForValue(); + value.set(name, json); + } + + @Override + public List getKeys(String key) { + Set keys = redisTemplate.keys(key); + List list = new ArrayList(); + for (String k : keys) { + list.add(k); + } + return list; + } + + @Override + public List getValuesByKeys(List keys) { + ValueOperations value = redisTemplate.opsForValue(); + List list = new ArrayList<>(); + for (String key : keys) { + String json = value.get(key); + list.add(json); + } + return list; + } + + @Override + public String getValueByKey(String key) { + ValueOperations value = redisTemplate.opsForValue(); + return value.get(key); + } + + @Override + public void deleteKey(String key) { + redisTemplate.delete(key); + } + + @Override + public void saveLoadBalance(String groupName, String key, String data) { + HashOperations value = redisTemplate.opsForHash(); + value.put(groupName,key,data); + } + + + @Override + public String getLoadBalance(String groupName, String key) { + HashOperations value = redisTemplate.opsForHash(); + return value.get(groupName,key); + } +} diff --git a/tx-lcn/tx-manager/src/main/resources/application.properties b/tx-lcn/tx-manager/src/main/resources/application.properties new file mode 100644 index 0000000..f81122e --- /dev/null +++ b/tx-lcn/tx-manager/src/main/resources/application.properties @@ -0,0 +1,95 @@ + +#######################################txmanager-start################################################# +#\u670D\u52A1\u7AEF\u53E3 +server.port=7000 + +#tx-manager\u4E0D\u5F97\u4FEE\u6539 +spring.application.name=tx-manager + +spring.mvc.static-path-pattern=/** +spring.resources.static-locations=classpath:/static/ +#######################################txmanager-end################################################# + + +#zookeeper\u5730\u5740 +#spring.cloud.zookeeper.connect-string=127.0.0.1:2181 +#spring.cloud.zookeeper.discovery.preferIpAddress = true + +#eureka \u5730\u5740 +eureka.client.service-url.defaultZone=http://eureka:8001/eureka/ +eureka.instance.prefer-ip-address=true + +#######################################redis-start################################################# +#redis \u914D\u7F6E\u6587\u4EF6\uFF0C\u6839\u636E\u60C5\u51B5\u9009\u62E9\u96C6\u7FA4\u6216\u8005\u5355\u673A\u6A21\u5F0F + +##redis \u96C6\u7FA4\u73AF\u5883\u914D\u7F6E +##redis cluster +#spring.redis.cluster.nodes=127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003 +#spring.redis.cluster.commandTimeout=5000 + +##redis \u5355\u70B9\u73AF\u5883\u914D\u7F6E +#redis +#redis\u4E3B\u673A\u5730\u5740 +spring.redis.host=172.20.0.1 +#redis\u4E3B\u673A\u7AEF\u53E3 +spring.redis.port=6379 +#redis\u94FE\u63A5\u5BC6\u7801 +spring.redis.password= +#####################################redis-end################################################### + + + + +#######################################LCN-start################################################# +#\u4E1A\u52A1\u6A21\u5757\u4E0ETxManager\u4E4B\u95F4\u901A\u8BAF\u7684\u6700\u5927\u7B49\u5F85\u65F6\u95F4\uFF08\u5355\u4F4D\uFF1A\u79D2\uFF09 +#\u901A\u8BAF\u65F6\u95F4\u662F\u6307\uFF1A\u53D1\u8D77\u65B9\u4E0E\u54CD\u5E94\u65B9\u4E4B\u95F4\u5B8C\u6210\u4E00\u6B21\u7684\u901A\u8BAF\u65F6\u95F4\u3002 +#\u8BE5\u5B57\u6BB5\u4EE3\u8868\u7684\u662FTx-Client\u6A21\u5757\u4E0ETxManager\u6A21\u5757\u4E4B\u95F4\u7684\u6700\u5927\u901A\u8BAF\u65F6\u95F4\uFF0C\u8D85\u8FC7\u8BE5\u65F6\u95F4\u672A\u54CD\u5E94\u672C\u6B21\u8BF7\u6C42\u5931\u8D25\u3002 +tm.transaction.netty.delaytime = 5 + +#\u4E1A\u52A1\u6A21\u5757\u4E0ETxManager\u4E4B\u95F4\u901A\u8BAF\u7684\u5FC3\u8DF3\u65F6\u95F4\uFF08\u5355\u4F4D\uFF1A\u79D2\uFF09 +tm.transaction.netty.hearttime = 15 + +#\u5B58\u50A8\u5230redis\u4E0B\u7684\u6570\u636E\u6700\u5927\u4FDD\u5B58\u65F6\u95F4\uFF08\u5355\u4F4D\uFF1A\u79D2\uFF09 +#\u8BE5\u5B57\u6BB5\u4EC5\u4EE3\u8868\u7684\u4E8B\u52A1\u6A21\u5757\u6570\u636E\u7684\u6700\u5927\u4FDD\u5B58\u65F6\u95F4\uFF0C\u8865\u507F\u6570\u636E\u4F1A\u6C38\u4E45\u4FDD\u5B58\u3002 +tm.redis.savemaxtime=30 + +#socket server Socket\u5BF9\u5916\u670D\u52A1\u7AEF\u53E3 +#TxManager\u7684LCN\u534F\u8BAE\u7684\u7AEF\u53E3 +tm.socket.port=9999 + +#\u6700\u5927socket\u8FDE\u63A5\u6570 +#TxManager\u6700\u5927\u5141\u8BB8\u7684\u5EFA\u7ACB\u8FDE\u63A5\u6570\u91CF +tm.socket.maxconnection=100 + +#\u4E8B\u52A1\u81EA\u52A8\u8865\u507F (true:\u5F00\u542F\uFF0Cfalse:\u5173\u95ED) +# \u8BF4\u660E\uFF1A +# \u5F00\u542F\u81EA\u52A8\u8865\u507F\u4EE5\u540E\uFF0C\u5FC5\u987B\u8981\u914D\u7F6E tm.compensate.notifyUrl \u5730\u5740\uFF0C\u4EC5\u5F53tm.compensate.notifyUrl \u5728\u8BF7\u6C42\u8865\u507F\u786E\u8BA4\u65F6\u8FD4\u56DEsuccess\u6216\u8005SUCCESS\u65F6\uFF0C\u624D\u4F1A\u6267\u884C\u81EA\u52A8\u8865\u507F\uFF0C\u5426\u5219\u4E0D\u4F1A\u81EA\u52A8\u8865\u507F\u3002 +# \u5173\u95ED\u81EA\u52A8\u8865\u507F\uFF0C\u5F53\u51FA\u73B0\u6570\u636E\u65F6\u4E5F\u4F1A tm.compensate.notifyUrl \u5730\u5740\u3002 +# \u5F53tm.compensate.notifyUrl \u65E0\u6548\u65F6\uFF0C\u4E0D\u5F71\u54CDTxManager\u8FD0\u884C\uFF0C\u4EC5\u4F1A\u5F71\u54CD\u81EA\u52A8\u8865\u507F\u3002 +tm.compensate.auto=false + +#\u4E8B\u52A1\u8865\u507F\u8BB0\u5F55\u56DE\u8C03\u5730\u5740(rest api \u5730\u5740\uFF0Cpost json\u683C\u5F0F) +#\u8BF7\u6C42\u8865\u507F\u662F\u5728\u5F00\u542F\u81EA\u52A8\u8865\u507F\u65F6\u624D\u4F1A\u8BF7\u6C42\u7684\u5730\u5740\u3002\u8BF7\u6C42\u5206\u4E3A\u4E24\u79CD\uFF1A1.\u8865\u507F\u51B3\u7B56\uFF0C2.\u8865\u507F\u7ED3\u679C\u901A\u77E5\uFF0C\u53EF\u901A\u8FC7\u901A\u8FC7action\u53C2\u6570\u533A\u5206compensate\u4E3A\u8865\u507F\u8BF7\u6C42\u3001notify\u4E3A\u8865\u507F\u901A\u77E5\u3002 +#*\u6CE8\u610F\u5F53\u8BF7\u6C42\u8865\u507F\u51B3\u7B56\u65F6\uFF0C\u9700\u8981\u8865\u507F\u670D\u52A1\u8FD4\u56DE"SUCCESS"\u5B57\u7B26\u4E32\u4EE5\u540E\u624D\u53EF\u4EE5\u6267\u884C\u81EA\u52A8\u8865\u507F\u3002 +#\u8BF7\u6C42\u8865\u507F\u7ED3\u679C\u901A\u77E5\u5219\u53EA\u9700\u8981\u63A5\u53D7\u901A\u77E5\u5373\u53EF\u3002 +#\u8BF7\u6C42\u8865\u507F\u7684\u6837\u4F8B\u6570\u636E\u683C\u5F0F: +#{"groupId":"TtQxTwJP","action":"compensate","json":"{\"address\":\"133.133.5.100:8081\",\"className\":\"com.example.demo.service.impl.DemoServiceImpl\",\"currentTime\":1511356150413,\"data\":\"C5IBLWNvbS5leGFtcGxlLmRlbW8uc2VydmljZS5pbXBsLkRlbW9TZXJ2aWNlSW1wbAwSBHNhdmUbehBqYXZhLmxhbmcuT2JqZWN0GAAQARwjeg9qYXZhLmxhbmcuQ2xhc3MYABABJCo/cHVibGljIGludCBjb20uZXhhbXBsZS5kZW1vLnNlcnZpY2UuaW1wbC5EZW1vU2VydmljZUltcGwuc2F2ZSgp\",\"groupId\":\"TtQxTwJP\",\"methodStr\":\"public int com.example.demo.service.impl.DemoServiceImpl.save()\",\"model\":\"demo1\",\"state\":0,\"time\":36,\"txGroup\":{\"groupId\":\"TtQxTwJP\",\"hasOver\":1,\"isCompensate\":0,\"list\":[{\"address\":\"133.133.5.100:8899\",\"isCompensate\":0,\"isGroup\":0,\"kid\":\"wnlEJoSl\",\"methodStr\":\"public int com.example.demo.service.impl.DemoServiceImpl.save()\",\"model\":\"demo2\",\"modelIpAddress\":\"133.133.5.100:8082\",\"channelAddress\":\"/133.133.5.100:64153\",\"notify\":1,\"uniqueKey\":\"bc13881a5d2ab2ace89ae5d34d608447\"}],\"nowTime\":0,\"startTime\":1511356150379,\"state\":1},\"uniqueKey\":\"be6eea31e382f1f0878d07cef319e4d7\"}"} +#\u8BF7\u6C42\u8865\u507F\u7684\u8FD4\u56DE\u6570\u636E\u6837\u4F8B\u6570\u636E\u683C\u5F0F: +#SUCCESS +#\u8BF7\u6C42\u8865\u507F\u7ED3\u679C\u901A\u77E5\u7684\u6837\u4F8B\u6570\u636E\u683C\u5F0F: +#{"resState":true,"groupId":"TtQxTwJP","action":"notify"} +tm.compensate.notifyUrl=http://ip:port/path + +#\u8865\u507F\u5931\u8D25\uFF0C\u518D\u6B21\u5C1D\u8BD5\u95F4\u9694\uFF08\u79D2\uFF09\uFF0C\u6700\u5927\u5C1D\u8BD5\u6B21\u65703\u6B21\uFF0C\u5F53\u8D85\u8FC73\u6B21\u5373\u4E3A\u8865\u507F\u5931\u8D25,\u5931\u8D25\u7684\u6570\u636E\u4F9D\u65E7\u8FD8\u4F1A\u5B58\u5728TxManager\u4E0B\u3002 +tm.compensate.tryTime=30 + +#\u5404\u4E8B\u52A1\u6A21\u5757\u81EA\u52A8\u8865\u507F\u7684\u65F6\u95F4\u4E0A\u9650(\u6BEB\u79D2) +#\u6307\u7684\u662F\u6A21\u5757\u6267\u884C\u81EA\u52A8\u8D85\u65F6\u7684\u6700\u5927\u65F6\u95F4\uFF0C\u8BE5\u6700\u5927\u65F6\u95F4\u82E5\u8FC7\u6BB5\u4F1A\u5BFC\u81F4\u4E8B\u52A1\u673A\u5236\u5F02\u5E38\uFF0C\u8BE5\u65F6\u95F4\u5FC5\u987B\u8981\u6A21\u5757\u4E4B\u95F4\u901A\u8BAF\u7684\u6700\u5927\u8D85\u8FC7\u65F6\u95F4\u3002 +#\u4F8B\u5982\uFF0C\u82E5\u6A21\u5757A\u4E0E\u6A21\u5757B\uFF0C\u8BF7\u6C42\u8D85\u65F6\u7684\u6700\u5927\u65F6\u95F4\u662F5\u79D2\uFF0C\u5219\u5EFA\u8BAE\u6539\u65F6\u95F4\u81F3\u5C11\u5927\u4E8E5\u79D2\u3002 +tm.compensate.maxWaitTime=15000 +#######################################LCN-end################################################# + + + + +logging.level.com.codingapi=debug \ No newline at end of file diff --git a/tx-lcn/tx-manager/src/main/resources/banner.txt b/tx-lcn/tx-manager/src/main/resources/banner.txt new file mode 100644 index 0000000..879248a --- /dev/null +++ b/tx-lcn/tx-manager/src/main/resources/banner.txt @@ -0,0 +1,13 @@ + + >=> >=> >==> >=> + >=> >=> >=> >> >=> >=> + >=> >=> >=> >=> >=> + >=> >=> >=> >=>>=> + >=> >=> >=> > >=> + >=> >=> >=> >=> >>=> + >=======> >===> >=> >=> + + LCN-TxManager version:4.1.0 + + + diff --git a/tx-lcn/tx-manager/src/main/resources/static/index.html b/tx-lcn/tx-manager/src/main/resources/static/index.html new file mode 100644 index 0000000..4e8e321 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/resources/static/index.html @@ -0,0 +1,138 @@ + + + + + TxManager v4.1.0 + + + + + + + + + + + + +

+ +

TxManagerV4.1.0 服务已启动

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
属性名称属性值
+ Socket对外服务IP +
+ Socket对外服务端口 +
+ 最大连接数 +
+ 当前连接数 +
+ TxManager模块心跳间隔时间(秒) +
+ TxManager模块通讯最大等待时间(秒) +
+ redis服务状态 +
+ redis存储最大时间(秒) +
+ 负载均衡服务器地址 +
+ 补偿回调地址(rest api 地址,post json格式) +
+ 存在补偿数据 +
+ 开启自动补偿(true 开启,false 关闭) +
+ 补偿失败尝试间隔时间(秒) +
+ 各事务模块自动补偿的时间上限(毫秒) +
+ + + 在线模块 + 事务补偿 + +
+ + + +
+ + \ No newline at end of file diff --git a/tx-lcn/tx-manager/src/main/resources/static/log.html b/tx-lcn/tx-manager/src/main/resources/static/log.html new file mode 100644 index 0000000..84aa09d --- /dev/null +++ b/tx-lcn/tx-manager/src/main/resources/static/log.html @@ -0,0 +1,192 @@ + + + + + 事务补偿 + + + + + + + + + + + + + + +
+ + 返回首页 + +
+ 说明:
+ 补偿记录展示的数据是分布事务发起方的切面数据信息。
+ 记录时间是指发起方出现补偿时记录下的时间,并非是TxManager的记录数据的时间。
+
+ + + +
+
+
+ + + + + + + + + + + + + + + + + +
模块名称条数
+
+
+ +
+
+ + + + + + + + + + + + + + +
记录日期
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
记录时间执行方法执行时间操作
+
+
+
+ + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/tx-lcn/tx-manager/src/main/resources/static/model.html b/tx-lcn/tx-manager/src/main/resources/static/model.html new file mode 100644 index 0000000..45853cb --- /dev/null +++ b/tx-lcn/tx-manager/src/main/resources/static/model.html @@ -0,0 +1,65 @@ + + + + + 在线模块 + + + + + + + + + + + + + + +
+ + 返回首页 + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
模块名称唯一标示模块地址管道名称
+
+ + + + +
+ + \ No newline at end of file diff --git a/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.css b/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.css new file mode 100644 index 0000000..09e3f04 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.css @@ -0,0 +1,650 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +.btn-default, +.btn-primary, +.btn-success, +.btn-info, +.btn-warning, +.btn-danger { + text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); +} + +.btn-default:active, +.btn-primary:active, +.btn-success:active, +.btn-info:active, +.btn-warning:active, +.btn-danger:active, +.btn-default.active, +.btn-primary.active, +.btn-success.active, +.btn-info.active, +.btn-warning.active, +.btn-danger.active { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} + +.btn-default.disabled, +.btn-primary.disabled, +.btn-success.disabled, +.btn-info.disabled, +.btn-warning.disabled, +.btn-danger.disabled, +.btn-default[disabled], +.btn-primary[disabled], +.btn-success[disabled], +.btn-info[disabled], +.btn-warning[disabled], +.btn-danger[disabled], +fieldset[disabled] .btn-default, +fieldset[disabled] .btn-primary, +fieldset[disabled] .btn-success, +fieldset[disabled] .btn-info, +fieldset[disabled] .btn-warning, +fieldset[disabled] .btn-danger { + -webkit-box-shadow: none; + box-shadow: none; +} + +.btn-default .badge, +.btn-primary .badge, +.btn-success .badge, +.btn-info .badge, +.btn-warning .badge, +.btn-danger .badge { + text-shadow: none; +} + +.btn:active, +.btn.active { + background-image: none; +} + +.btn-default { + text-shadow: 0 1px 0 #fff; + background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); + background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + background-repeat: repeat-x; + border-color: #dbdbdb; + border-color: #ccc; +} + +.btn-default:hover, +.btn-default:focus { + background-color: #e0e0e0; + background-position: 0 -15px; +} + +.btn-default:active, +.btn-default.active { + background-color: #e0e0e0; + border-color: #dbdbdb; +} + +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #e0e0e0; + background-image: none; +} + +.btn-primary { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); + background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + background-repeat: repeat-x; + border-color: #245580; +} + +.btn-primary:hover, +.btn-primary:focus { + background-color: #265a88; + background-position: 0 -15px; +} + +.btn-primary:active, +.btn-primary.active { + background-color: #265a88; + border-color: #245580; +} + +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #265a88; + background-image: none; +} + +.btn-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); + background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); + background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + background-repeat: repeat-x; + border-color: #3e8f3e; +} + +.btn-success:hover, +.btn-success:focus { + background-color: #419641; + background-position: 0 -15px; +} + +.btn-success:active, +.btn-success.active { + background-color: #419641; + border-color: #3e8f3e; +} + +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #419641; + background-image: none; +} + +.btn-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); + background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); + background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + background-repeat: repeat-x; + border-color: #28a4c9; +} + +.btn-info:hover, +.btn-info:focus { + background-color: #2aabd2; + background-position: 0 -15px; +} + +.btn-info:active, +.btn-info.active { + background-color: #2aabd2; + border-color: #28a4c9; +} + +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #2aabd2; + background-image: none; +} + +.btn-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); + background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + background-repeat: repeat-x; + border-color: #e38d13; +} + +.btn-warning:hover, +.btn-warning:focus { + background-color: #eb9316; + background-position: 0 -15px; +} + +.btn-warning:active, +.btn-warning.active { + background-color: #eb9316; + border-color: #e38d13; +} + +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #eb9316; + background-image: none; +} + +.btn-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); + background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); + background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + background-repeat: repeat-x; + border-color: #b92c28; +} + +.btn-danger:hover, +.btn-danger:focus { + background-color: #c12e2a; + background-position: 0 -15px; +} + +.btn-danger:active, +.btn-danger.active { + background-color: #c12e2a; + border-color: #b92c28; +} + +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #c12e2a; + background-image: none; +} + +.thumbnail, +.img-thumbnail { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); + box-shadow: 0 1px 2px rgba(0, 0, 0, .075); +} + +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + background-color: #e8e8e8; + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); + background-repeat: repeat-x; +} + +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + background-color: #2e6da4; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; +} + +.navbar-default { + background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); + background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); + background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + background-repeat: repeat-x; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); +} + +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); + background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); + background-repeat: repeat-x; + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); +} + +.navbar-brand, +.navbar-nav > li > a { + text-shadow: 0 1px 0 rgba(255, 255, 255, .25); +} + +.navbar-inverse { + background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); + background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); + background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + background-repeat: repeat-x; + border-radius: 4px; +} + +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); + background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); + background-repeat: repeat-x; + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); +} + +.navbar-inverse .navbar-brand, +.navbar-inverse .navbar-nav > li > a { + text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); +} + +.navbar-static-top, +.navbar-fixed-top, +.navbar-fixed-bottom { + border-radius: 0; +} + +@media (max-width: 767px) { + .navbar .navbar-nav .open .dropdown-menu > .active > a, + .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; + } +} + +.alert { + text-shadow: 0 1px 0 rgba(255, 255, 255, .2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); +} + +.alert-success { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); + background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); + background-repeat: repeat-x; + border-color: #b2dba1; +} + +.alert-info { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); + background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); + background-repeat: repeat-x; + border-color: #9acfea; +} + +.alert-warning { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); + background-repeat: repeat-x; + border-color: #f5e79e; +} + +.alert-danger { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); + background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); + background-repeat: repeat-x; + border-color: #dca7a7; +} + +.progress { + background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); + background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); + background-repeat: repeat-x; +} + +.progress-bar { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); + background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); + background-repeat: repeat-x; +} + +.progress-bar-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); + background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); + background-repeat: repeat-x; +} + +.progress-bar-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); + background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); + background-repeat: repeat-x; +} + +.progress-bar-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); + background-repeat: repeat-x; +} + +.progress-bar-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); + background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); + background-repeat: repeat-x; +} + +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} + +.list-group { + border-radius: 4px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); + box-shadow: 0 1px 2px rgba(0, 0, 0, .075); +} + +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + text-shadow: 0 -1px 0 #286090; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); + background-repeat: repeat-x; + border-color: #2b669a; +} + +.list-group-item.active .badge, +.list-group-item.active:hover .badge, +.list-group-item.active:focus .badge { + text-shadow: none; +} + +.panel { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); + box-shadow: 0 1px 2px rgba(0, 0, 0, .05); +} + +.panel-default > .panel-heading { + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); + background-repeat: repeat-x; +} + +.panel-primary > .panel-heading { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; +} + +.panel-success > .panel-heading { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); + background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); + background-repeat: repeat-x; +} + +.panel-info > .panel-heading { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); + background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); + background-repeat: repeat-x; +} + +.panel-warning > .panel-heading { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); + background-repeat: repeat-x; +} + +.panel-danger > .panel-heading { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); + background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); + background-repeat: repeat-x; +} + +.well { + background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); + background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); + background-repeat: repeat-x; + border-color: #dcdcdc; + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); +} + +/*# sourceMappingURL=bootstrap-theme.css.map */ diff --git a/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.css.map b/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.css.map new file mode 100644 index 0000000..cda06aa --- /dev/null +++ b/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.css.map @@ -0,0 +1,20 @@ +{ + "version": 3, + "sources": [ + "bootstrap-theme.css", + "less/theme.less", + "less/mixins/vendor-prefixes.less", + "less/mixins/gradients.less", + "less/mixins/reset-filter.less" + ], + "names": [], + "mappings": "AAAA;;;;GAIG;ACeH;;;;;;EAME,yCAAA;EC2CA,4FAAA;EACQ,oFAAA;CFvDT;ACgBC;;;;;;;;;;;;ECsCA,yDAAA;EACQ,iDAAA;CFxCT;ACMC;;;;;;;;;;;;;;;;;;ECiCA,yBAAA;EACQ,iBAAA;CFnBT;AC/BD;;;;;;EAuBI,kBAAA;CDgBH;ACyBC;;EAEE,uBAAA;CDvBH;AC4BD;EErEI,sEAAA;EACA,iEAAA;EACA,2FAAA;EAAA,oEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;EAuC2C,0BAAA;EAA2B,mBAAA;CDjBvE;ACpBC;;EAEE,0BAAA;EACA,6BAAA;CDsBH;ACnBC;;EAEE,0BAAA;EACA,sBAAA;CDqBH;ACfG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6BL;ACbD;EEtEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8DD;AC5DC;;EAEE,0BAAA;EACA,6BAAA;CD8DH;AC3DC;;EAEE,0BAAA;EACA,sBAAA;CD6DH;ACvDG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqEL;ACpDD;EEvEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsGD;ACpGC;;EAEE,0BAAA;EACA,6BAAA;CDsGH;ACnGC;;EAEE,0BAAA;EACA,sBAAA;CDqGH;AC/FG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6GL;AC3FD;EExEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ID;AC5IC;;EAEE,0BAAA;EACA,6BAAA;CD8IH;AC3IC;;EAEE,0BAAA;EACA,sBAAA;CD6IH;ACvIG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqJL;AClID;EEzEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsLD;ACpLC;;EAEE,0BAAA;EACA,6BAAA;CDsLH;ACnLC;;EAEE,0BAAA;EACA,sBAAA;CDqLH;AC/KG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6LL;ACzKD;EE1EI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ND;AC5NC;;EAEE,0BAAA;EACA,6BAAA;CD8NH;AC3NC;;EAEE,0BAAA;EACA,sBAAA;CD6NH;ACvNG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqOL;AC1MD;;EClCE,mDAAA;EACQ,2CAAA;CFgPT;ACrMD;;EE3FI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF0FF,0BAAA;CD2MD;ACzMD;;;EEhGI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFgGF,0BAAA;CD+MD;ACtMD;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EH+HA,mBAAA;ECjEA,4FAAA;EACQ,oFAAA;CF8QT;ACjND;;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,yDAAA;EACQ,iDAAA;CFwRT;AC9MD;;EAEE,+CAAA;CDgND;AC5MD;EEhII,sEAAA;EACA,iEAAA;EACA,2FAAA;EAAA,oEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EHkJA,mBAAA;CDkND;ACrND;;EEhII,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,wDAAA;EACQ,gDAAA;CF+ST;AC/ND;;EAYI,0CAAA;CDuNH;AClND;;;EAGE,iBAAA;CDoND;AC/LD;EAfI;;;IAGE,YAAA;IE7JF,yEAAA;IACA,oEAAA;IACA,8FAAA;IAAA,uEAAA;IACA,4BAAA;IACA,uHAAA;GH+WD;CACF;AC3MD;EACE,8CAAA;EC3HA,2FAAA;EACQ,mFAAA;CFyUT;ACnMD;EEtLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+MD;AC1MD;EEvLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuND;ACjND;EExLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+ND;ACxND;EEzLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuOD;ACxND;EEjMI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH4ZH;ACrND;EE3MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHmaH;AC3ND;EE5MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH0aH;ACjOD;EE7MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHibH;ACvOD;EE9MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHwbH;AC7OD;EE/MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH+bH;AChPD;EElLI,8MAAA;EACA,yMAAA;EACA,sMAAA;CHqaH;AC5OD;EACE,mBAAA;EC9KA,mDAAA;EACQ,2CAAA;CF6ZT;AC7OD;;;EAGE,8BAAA;EEnOE,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFiOF,sBAAA;CDmPD;ACxPD;;;EAQI,kBAAA;CDqPH;AC3OD;ECnME,kDAAA;EACQ,0CAAA;CFibT;ACrOD;EE5PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHoeH;AC3OD;EE7PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH2eH;ACjPD;EE9PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHkfH;ACvPD;EE/PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHyfH;AC7PD;EEhQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHggBH;ACnQD;EEjQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHugBH;ACnQD;EExQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFsQF,sBAAA;EC3NA,0FAAA;EACQ,kFAAA;CFqeT", + "file": "bootstrap-theme.css", + "sourcesContent": [ + "/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-default.disabled,\n.btn-primary.disabled,\n.btn-success.disabled,\n.btn-info.disabled,\n.btn-warning.disabled,\n.btn-danger.disabled,\n.btn-default[disabled],\n.btn-primary[disabled],\n.btn-success[disabled],\n.btn-info[disabled],\n.btn-warning[disabled],\n.btn-danger[disabled],\nfieldset[disabled] .btn-default,\nfieldset[disabled] .btn-primary,\nfieldset[disabled] .btn-success,\nfieldset[disabled] .btn-info,\nfieldset[disabled] .btn-warning,\nfieldset[disabled] .btn-danger {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n text-shadow: none;\n}\n.btn:active,\n.btn.active {\n background-image: none;\n}\n.btn-default {\n background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #dbdbdb;\n text-shadow: 0 1px 0 #fff;\n border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n background-color: #e0e0e0;\n background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n background-color: #e0e0e0;\n border-color: #dbdbdb;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n background-color: #e0e0e0;\n background-image: none;\n}\n.btn-primary {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n background-color: #265a88;\n background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n background-color: #265a88;\n border-color: #245580;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n background-color: #265a88;\n background-image: none;\n}\n.btn-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n background-color: #419641;\n background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n background-color: #419641;\n border-color: #3e8f3e;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n background-color: #419641;\n background-image: none;\n}\n.btn-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n background-color: #2aabd2;\n background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n background-color: #2aabd2;\n border-color: #28a4c9;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n background-color: #2aabd2;\n background-image: none;\n}\n.btn-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n background-color: #eb9316;\n background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n background-color: #eb9316;\n border-color: #e38d13;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n background-color: #eb9316;\n background-image: none;\n}\n.btn-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n background-color: #c12e2a;\n background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n background-color: #c12e2a;\n border-color: #b92c28;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n background-color: #c12e2a;\n background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-color: #e8e8e8;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-color: #2e6da4;\n}\n.navbar-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n.navbar-inverse {\n background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n@media (max-width: 767px) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n }\n}\n.alert {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.alert-success {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n border-color: #b2dba1;\n}\n.alert-info {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n border-color: #9acfea;\n}\n.alert-warning {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n border-color: #f5e79e;\n}\n.alert-danger {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n border-color: #dca7a7;\n}\n.progress {\n background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n}\n.progress-bar {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n}\n.progress-bar-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n}\n.progress-bar-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n}\n.progress-bar-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n}\n.progress-bar-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n}\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.list-group {\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 #286090;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n text-shadow: none;\n}\n.panel {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.panel-default > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n}\n.panel-primary > .panel-heading {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n}\n.panel-success > .panel-heading {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n}\n.panel-info > .panel-heading {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n}\n.panel-warning > .panel-heading {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n}\n.panel-danger > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n}\n.well {\n background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n border-color: #dcdcdc;\n -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */", + "/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n", + "// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n", + "// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n", + "// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n" + ] +} \ No newline at end of file diff --git a/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.min.css b/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.min.css new file mode 100644 index 0000000..5e39401 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} +/*# sourceMappingURL=bootstrap-theme.min.css.map */ \ No newline at end of file diff --git a/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.min.css.map b/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.min.css.map new file mode 100644 index 0000000..c7cb37c --- /dev/null +++ b/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap-theme.min.css.map @@ -0,0 +1,17 @@ +{ + "version": 3, + "sources": [ + "less/theme.less", + "less/mixins/vendor-prefixes.less", + "less/mixins/gradients.less", + "less/mixins/reset-filter.less" + ], + "names": [], + "mappings": ";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA", + "sourcesContent": [ + "/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n", + "// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n", + "// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n", + "// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n" + ] +} \ No newline at end of file diff --git a/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap.css b/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap.css new file mode 100644 index 0000000..33b6ac7 --- /dev/null +++ b/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap.css @@ -0,0 +1,8198 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +body { + margin: 0; +} + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} + +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} + +audio:not([controls]) { + display: none; + height: 0; +} + +[hidden], +template { + display: none; +} + +a { + background-color: transparent; +} + +a:active, +a:hover { + outline: 0; +} + +abbr[title] { + border-bottom: 1px dotted; +} + +b, +strong { + font-weight: bold; +} + +dfn { + font-style: italic; +} + +h1 { + margin: .67em 0; + font-size: 2em; +} + +mark { + color: #000; + background: #ff0; +} + +small { + font-size: 80%; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sup { + top: -.5em; +} + +sub { + bottom: -.25em; +} + +img { + border: 0; +} + +svg:not(:root) { + overflow: hidden; +} + +figure { + margin: 1em 40px; +} + +hr { + height: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +pre { + overflow: auto; +} + +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +button, +input, +optgroup, +select, +textarea { + margin: 0; + font: inherit; + color: inherit; +} + +button { + overflow: visible; +} + +button, +select { + text-transform: none; +} + +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} + +button[disabled], +html input[disabled] { + cursor: default; +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} + +input { + line-height: normal; +} + +input[type="checkbox"], +input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} + +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} + +legend { + padding: 0; + border: 0; +} + +textarea { + overflow: auto; +} + +optgroup { + font-weight: bold; +} + +table { + border-spacing: 0; + border-collapse: collapse; +} + +td, +th { + padding: 0; +} + +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, + *:before, + *:after { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + } + + a, + a:visited { + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + + pre, + blockquote { + border: 1px solid #999; + + page-break-inside: avoid; + } + + thead { + display: table-header-group; + } + + tr, + img { + page-break-inside: avoid; + } + + img { + max-width: 100% !important; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } + + .navbar { + display: none; + } + + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + + .label { + border: 1px solid #000; + } + + .table { + border-collapse: collapse !important; + } + + .table td, + .table th { + background-color: #fff !important; + } + + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} + +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} + +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.glyphicon-asterisk:before { + content: "\002a"; +} + +.glyphicon-plus:before { + content: "\002b"; +} + +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} + +.glyphicon-minus:before { + content: "\2212"; +} + +.glyphicon-cloud:before { + content: "\2601"; +} + +.glyphicon-envelope:before { + content: "\2709"; +} + +.glyphicon-pencil:before { + content: "\270f"; +} + +.glyphicon-glass:before { + content: "\e001"; +} + +.glyphicon-music:before { + content: "\e002"; +} + +.glyphicon-search:before { + content: "\e003"; +} + +.glyphicon-heart:before { + content: "\e005"; +} + +.glyphicon-star:before { + content: "\e006"; +} + +.glyphicon-star-empty:before { + content: "\e007"; +} + +.glyphicon-user:before { + content: "\e008"; +} + +.glyphicon-film:before { + content: "\e009"; +} + +.glyphicon-th-large:before { + content: "\e010"; +} + +.glyphicon-th:before { + content: "\e011"; +} + +.glyphicon-th-list:before { + content: "\e012"; +} + +.glyphicon-ok:before { + content: "\e013"; +} + +.glyphicon-remove:before { + content: "\e014"; +} + +.glyphicon-zoom-in:before { + content: "\e015"; +} + +.glyphicon-zoom-out:before { + content: "\e016"; +} + +.glyphicon-off:before { + content: "\e017"; +} + +.glyphicon-signal:before { + content: "\e018"; +} + +.glyphicon-cog:before { + content: "\e019"; +} + +.glyphicon-trash:before { + content: "\e020"; +} + +.glyphicon-home:before { + content: "\e021"; +} + +.glyphicon-file:before { + content: "\e022"; +} + +.glyphicon-time:before { + content: "\e023"; +} + +.glyphicon-road:before { + content: "\e024"; +} + +.glyphicon-download-alt:before { + content: "\e025"; +} + +.glyphicon-download:before { + content: "\e026"; +} + +.glyphicon-upload:before { + content: "\e027"; +} + +.glyphicon-inbox:before { + content: "\e028"; +} + +.glyphicon-play-circle:before { + content: "\e029"; +} + +.glyphicon-repeat:before { + content: "\e030"; +} + +.glyphicon-refresh:before { + content: "\e031"; +} + +.glyphicon-list-alt:before { + content: "\e032"; +} + +.glyphicon-lock:before { + content: "\e033"; +} + +.glyphicon-flag:before { + content: "\e034"; +} + +.glyphicon-headphones:before { + content: "\e035"; +} + +.glyphicon-volume-off:before { + content: "\e036"; +} + +.glyphicon-volume-down:before { + content: "\e037"; +} + +.glyphicon-volume-up:before { + content: "\e038"; +} + +.glyphicon-qrcode:before { + content: "\e039"; +} + +.glyphicon-barcode:before { + content: "\e040"; +} + +.glyphicon-tag:before { + content: "\e041"; +} + +.glyphicon-tags:before { + content: "\e042"; +} + +.glyphicon-book:before { + content: "\e043"; +} + +.glyphicon-bookmark:before { + content: "\e044"; +} + +.glyphicon-print:before { + content: "\e045"; +} + +.glyphicon-camera:before { + content: "\e046"; +} + +.glyphicon-font:before { + content: "\e047"; +} + +.glyphicon-bold:before { + content: "\e048"; +} + +.glyphicon-italic:before { + content: "\e049"; +} + +.glyphicon-text-height:before { + content: "\e050"; +} + +.glyphicon-text-width:before { + content: "\e051"; +} + +.glyphicon-align-left:before { + content: "\e052"; +} + +.glyphicon-align-center:before { + content: "\e053"; +} + +.glyphicon-align-right:before { + content: "\e054"; +} + +.glyphicon-align-justify:before { + content: "\e055"; +} + +.glyphicon-list:before { + content: "\e056"; +} + +.glyphicon-indent-left:before { + content: "\e057"; +} + +.glyphicon-indent-right:before { + content: "\e058"; +} + +.glyphicon-facetime-video:before { + content: "\e059"; +} + +.glyphicon-picture:before { + content: "\e060"; +} + +.glyphicon-map-marker:before { + content: "\e062"; +} + +.glyphicon-adjust:before { + content: "\e063"; +} + +.glyphicon-tint:before { + content: "\e064"; +} + +.glyphicon-edit:before { + content: "\e065"; +} + +.glyphicon-share:before { + content: "\e066"; +} + +.glyphicon-check:before { + content: "\e067"; +} + +.glyphicon-move:before { + content: "\e068"; +} + +.glyphicon-step-backward:before { + content: "\e069"; +} + +.glyphicon-fast-backward:before { + content: "\e070"; +} + +.glyphicon-backward:before { + content: "\e071"; +} + +.glyphicon-play:before { + content: "\e072"; +} + +.glyphicon-pause:before { + content: "\e073"; +} + +.glyphicon-stop:before { + content: "\e074"; +} + +.glyphicon-forward:before { + content: "\e075"; +} + +.glyphicon-fast-forward:before { + content: "\e076"; +} + +.glyphicon-step-forward:before { + content: "\e077"; +} + +.glyphicon-eject:before { + content: "\e078"; +} + +.glyphicon-chevron-left:before { + content: "\e079"; +} + +.glyphicon-chevron-right:before { + content: "\e080"; +} + +.glyphicon-plus-sign:before { + content: "\e081"; +} + +.glyphicon-minus-sign:before { + content: "\e082"; +} + +.glyphicon-remove-sign:before { + content: "\e083"; +} + +.glyphicon-ok-sign:before { + content: "\e084"; +} + +.glyphicon-question-sign:before { + content: "\e085"; +} + +.glyphicon-info-sign:before { + content: "\e086"; +} + +.glyphicon-screenshot:before { + content: "\e087"; +} + +.glyphicon-remove-circle:before { + content: "\e088"; +} + +.glyphicon-ok-circle:before { + content: "\e089"; +} + +.glyphicon-ban-circle:before { + content: "\e090"; +} + +.glyphicon-arrow-left:before { + content: "\e091"; +} + +.glyphicon-arrow-right:before { + content: "\e092"; +} + +.glyphicon-arrow-up:before { + content: "\e093"; +} + +.glyphicon-arrow-down:before { + content: "\e094"; +} + +.glyphicon-share-alt:before { + content: "\e095"; +} + +.glyphicon-resize-full:before { + content: "\e096"; +} + +.glyphicon-resize-small:before { + content: "\e097"; +} + +.glyphicon-exclamation-sign:before { + content: "\e101"; +} + +.glyphicon-gift:before { + content: "\e102"; +} + +.glyphicon-leaf:before { + content: "\e103"; +} + +.glyphicon-fire:before { + content: "\e104"; +} + +.glyphicon-eye-open:before { + content: "\e105"; +} + +.glyphicon-eye-close:before { + content: "\e106"; +} + +.glyphicon-warning-sign:before { + content: "\e107"; +} + +.glyphicon-plane:before { + content: "\e108"; +} + +.glyphicon-calendar:before { + content: "\e109"; +} + +.glyphicon-random:before { + content: "\e110"; +} + +.glyphicon-comment:before { + content: "\e111"; +} + +.glyphicon-magnet:before { + content: "\e112"; +} + +.glyphicon-chevron-up:before { + content: "\e113"; +} + +.glyphicon-chevron-down:before { + content: "\e114"; +} + +.glyphicon-retweet:before { + content: "\e115"; +} + +.glyphicon-shopping-cart:before { + content: "\e116"; +} + +.glyphicon-folder-close:before { + content: "\e117"; +} + +.glyphicon-folder-open:before { + content: "\e118"; +} + +.glyphicon-resize-vertical:before { + content: "\e119"; +} + +.glyphicon-resize-horizontal:before { + content: "\e120"; +} + +.glyphicon-hdd:before { + content: "\e121"; +} + +.glyphicon-bullhorn:before { + content: "\e122"; +} + +.glyphicon-bell:before { + content: "\e123"; +} + +.glyphicon-certificate:before { + content: "\e124"; +} + +.glyphicon-thumbs-up:before { + content: "\e125"; +} + +.glyphicon-thumbs-down:before { + content: "\e126"; +} + +.glyphicon-hand-right:before { + content: "\e127"; +} + +.glyphicon-hand-left:before { + content: "\e128"; +} + +.glyphicon-hand-up:before { + content: "\e129"; +} + +.glyphicon-hand-down:before { + content: "\e130"; +} + +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} + +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} + +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} + +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} + +.glyphicon-globe:before { + content: "\e135"; +} + +.glyphicon-wrench:before { + content: "\e136"; +} + +.glyphicon-tasks:before { + content: "\e137"; +} + +.glyphicon-filter:before { + content: "\e138"; +} + +.glyphicon-briefcase:before { + content: "\e139"; +} + +.glyphicon-fullscreen:before { + content: "\e140"; +} + +.glyphicon-dashboard:before { + content: "\e141"; +} + +.glyphicon-paperclip:before { + content: "\e142"; +} + +.glyphicon-heart-empty:before { + content: "\e143"; +} + +.glyphicon-link:before { + content: "\e144"; +} + +.glyphicon-phone:before { + content: "\e145"; +} + +.glyphicon-pushpin:before { + content: "\e146"; +} + +.glyphicon-usd:before { + content: "\e148"; +} + +.glyphicon-gbp:before { + content: "\e149"; +} + +.glyphicon-sort:before { + content: "\e150"; +} + +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} + +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} + +.glyphicon-sort-by-order:before { + content: "\e153"; +} + +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} + +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} + +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} + +.glyphicon-unchecked:before { + content: "\e157"; +} + +.glyphicon-expand:before { + content: "\e158"; +} + +.glyphicon-collapse-down:before { + content: "\e159"; +} + +.glyphicon-collapse-up:before { + content: "\e160"; +} + +.glyphicon-log-in:before { + content: "\e161"; +} + +.glyphicon-flash:before { + content: "\e162"; +} + +.glyphicon-log-out:before { + content: "\e163"; +} + +.glyphicon-new-window:before { + content: "\e164"; +} + +.glyphicon-record:before { + content: "\e165"; +} + +.glyphicon-save:before { + content: "\e166"; +} + +.glyphicon-open:before { + content: "\e167"; +} + +.glyphicon-saved:before { + content: "\e168"; +} + +.glyphicon-import:before { + content: "\e169"; +} + +.glyphicon-export:before { + content: "\e170"; +} + +.glyphicon-send:before { + content: "\e171"; +} + +.glyphicon-floppy-disk:before { + content: "\e172"; +} + +.glyphicon-floppy-saved:before { + content: "\e173"; +} + +.glyphicon-floppy-remove:before { + content: "\e174"; +} + +.glyphicon-floppy-save:before { + content: "\e175"; +} + +.glyphicon-floppy-open:before { + content: "\e176"; +} + +.glyphicon-credit-card:before { + content: "\e177"; +} + +.glyphicon-transfer:before { + content: "\e178"; +} + +.glyphicon-cutlery:before { + content: "\e179"; +} + +.glyphicon-header:before { + content: "\e180"; +} + +.glyphicon-compressed:before { + content: "\e181"; +} + +.glyphicon-earphone:before { + content: "\e182"; +} + +.glyphicon-phone-alt:before { + content: "\e183"; +} + +.glyphicon-tower:before { + content: "\e184"; +} + +.glyphicon-stats:before { + content: "\e185"; +} + +.glyphicon-sd-video:before { + content: "\e186"; +} + +.glyphicon-hd-video:before { + content: "\e187"; +} + +.glyphicon-subtitles:before { + content: "\e188"; +} + +.glyphicon-sound-stereo:before { + content: "\e189"; +} + +.glyphicon-sound-dolby:before { + content: "\e190"; +} + +.glyphicon-sound-5-1:before { + content: "\e191"; +} + +.glyphicon-sound-6-1:before { + content: "\e192"; +} + +.glyphicon-sound-7-1:before { + content: "\e193"; +} + +.glyphicon-copyright-mark:before { + content: "\e194"; +} + +.glyphicon-registration-mark:before { + content: "\e195"; +} + +.glyphicon-cloud-download:before { + content: "\e197"; +} + +.glyphicon-cloud-upload:before { + content: "\e198"; +} + +.glyphicon-tree-conifer:before { + content: "\e199"; +} + +.glyphicon-tree-deciduous:before { + content: "\e200"; +} + +.glyphicon-cd:before { + content: "\e201"; +} + +.glyphicon-save-file:before { + content: "\e202"; +} + +.glyphicon-open-file:before { + content: "\e203"; +} + +.glyphicon-level-up:before { + content: "\e204"; +} + +.glyphicon-copy:before { + content: "\e205"; +} + +.glyphicon-paste:before { + content: "\e206"; +} + +.glyphicon-alert:before { + content: "\e209"; +} + +.glyphicon-equalizer:before { + content: "\e210"; +} + +.glyphicon-king:before { + content: "\e211"; +} + +.glyphicon-queen:before { + content: "\e212"; +} + +.glyphicon-pawn:before { + content: "\e213"; +} + +.glyphicon-bishop:before { + content: "\e214"; +} + +.glyphicon-knight:before { + content: "\e215"; +} + +.glyphicon-baby-formula:before { + content: "\e216"; +} + +.glyphicon-tent:before { + content: "\26fa"; +} + +.glyphicon-blackboard:before { + content: "\e218"; +} + +.glyphicon-bed:before { + content: "\e219"; +} + +.glyphicon-apple:before { + content: "\f8ff"; +} + +.glyphicon-erase:before { + content: "\e221"; +} + +.glyphicon-hourglass:before { + content: "\231b"; +} + +.glyphicon-lamp:before { + content: "\e223"; +} + +.glyphicon-duplicate:before { + content: "\e224"; +} + +.glyphicon-piggy-bank:before { + content: "\e225"; +} + +.glyphicon-scissors:before { + content: "\e226"; +} + +.glyphicon-bitcoin:before { + content: "\e227"; +} + +.glyphicon-btc:before { + content: "\e227"; +} + +.glyphicon-xbt:before { + content: "\e227"; +} + +.glyphicon-yen:before { + content: "\00a5"; +} + +.glyphicon-jpy:before { + content: "\00a5"; +} + +.glyphicon-ruble:before { + content: "\20bd"; +} + +.glyphicon-rub:before { + content: "\20bd"; +} + +.glyphicon-scale:before { + content: "\e230"; +} + +.glyphicon-ice-lolly:before { + content: "\e231"; +} + +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} + +.glyphicon-education:before { + content: "\e233"; +} + +.glyphicon-option-horizontal:before { + content: "\e234"; +} + +.glyphicon-option-vertical:before { + content: "\e235"; +} + +.glyphicon-menu-hamburger:before { + content: "\e236"; +} + +.glyphicon-modal-window:before { + content: "\e237"; +} + +.glyphicon-oil:before { + content: "\e238"; +} + +.glyphicon-grain:before { + content: "\e239"; +} + +.glyphicon-sunglasses:before { + content: "\e240"; +} + +.glyphicon-text-size:before { + content: "\e241"; +} + +.glyphicon-text-color:before { + content: "\e242"; +} + +.glyphicon-text-background:before { + content: "\e243"; +} + +.glyphicon-object-align-top:before { + content: "\e244"; +} + +.glyphicon-object-align-bottom:before { + content: "\e245"; +} + +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} + +.glyphicon-object-align-left:before { + content: "\e247"; +} + +.glyphicon-object-align-vertical:before { + content: "\e248"; +} + +.glyphicon-object-align-right:before { + content: "\e249"; +} + +.glyphicon-triangle-right:before { + content: "\e250"; +} + +.glyphicon-triangle-left:before { + content: "\e251"; +} + +.glyphicon-triangle-bottom:before { + content: "\e252"; +} + +.glyphicon-triangle-top:before { + content: "\e253"; +} + +.glyphicon-console:before { + content: "\e254"; +} + +.glyphicon-superscript:before { + content: "\e255"; +} + +.glyphicon-subscript:before { + content: "\e256"; +} + +.glyphicon-menu-left:before { + content: "\e257"; +} + +.glyphicon-menu-right:before { + content: "\e258"; +} + +.glyphicon-menu-down:before { + content: "\e259"; +} + +.glyphicon-menu-up:before { + content: "\e260"; +} + +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +html { + font-size: 10px; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333; + background-color: #fff; +} + +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +a { + color: #337ab7; + text-decoration: none; +} + +a:hover, +a:focus { + color: #23527c; + text-decoration: underline; +} + +a:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +figure { + margin: 0; +} + +img { + vertical-align: middle; +} + +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} + +.img-rounded { + border-radius: 6px; +} + +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} + +.img-circle { + border-radius: 50%; +} + +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee; +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} + +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} + +[role="button"] { + cursor: pointer; +} + +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #777; +} + +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} + +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} + +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} + +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} + +h1, +.h1 { + font-size: 36px; +} + +h2, +.h2 { + font-size: 30px; +} + +h3, +.h3 { + font-size: 24px; +} + +h4, +.h4 { + font-size: 18px; +} + +h5, +.h5 { + font-size: 14px; +} + +h6, +.h6 { + font-size: 12px; +} + +p { + margin: 0 0 10px; +} + +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} + +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} + +small, +.small { + font-size: 85%; +} + +mark, +.mark { + padding: .2em; + background-color: #fcf8e3; +} + +.text-left { + text-align: left; +} + +.text-right { + text-align: right; +} + +.text-center { + text-align: center; +} + +.text-justify { + text-align: justify; +} + +.text-nowrap { + white-space: nowrap; +} + +.text-lowercase { + text-transform: lowercase; +} + +.text-uppercase { + text-transform: uppercase; +} + +.text-capitalize { + text-transform: capitalize; +} + +.text-muted { + color: #777; +} + +.text-primary { + color: #337ab7; +} + +a.text-primary:hover, +a.text-primary:focus { + color: #286090; +} + +.text-success { + color: #3c763d; +} + +a.text-success:hover, +a.text-success:focus { + color: #2b542c; +} + +.text-info { + color: #31708f; +} + +a.text-info:hover, +a.text-info:focus { + color: #245269; +} + +.text-warning { + color: #8a6d3b; +} + +a.text-warning:hover, +a.text-warning:focus { + color: #66512c; +} + +.text-danger { + color: #a94442; +} + +a.text-danger:hover, +a.text-danger:focus { + color: #843534; +} + +.bg-primary { + color: #fff; + background-color: #337ab7; +} + +a.bg-primary:hover, +a.bg-primary:focus { + background-color: #286090; +} + +.bg-success { + background-color: #dff0d8; +} + +a.bg-success:hover, +a.bg-success:focus { + background-color: #c1e2b3; +} + +.bg-info { + background-color: #d9edf7; +} + +a.bg-info:hover, +a.bg-info:focus { + background-color: #afd9ee; +} + +.bg-warning { + background-color: #fcf8e3; +} + +a.bg-warning:hover, +a.bg-warning:focus { + background-color: #f7ecb5; +} + +.bg-danger { + background-color: #f2dede; +} + +a.bg-danger:hover, +a.bg-danger:focus { + background-color: #e4b9b9; +} + +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} + +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} + +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} + +.list-unstyled { + padding-left: 0; + list-style: none; +} + +.list-inline { + padding-left: 0; + margin-left: -5px; + list-style: none; +} + +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} + +dl { + margin-top: 0; + margin-bottom: 20px; +} + +dt, +dd { + line-height: 1.42857143; +} + +dt { + font-weight: bold; +} + +dd { + margin-left: 0; +} + +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + + .dl-horizontal dd { + margin-left: 180px; + } +} + +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #777; +} + +.initialism { + font-size: 90%; + text-transform: uppercase; +} + +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; +} + +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} + +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777; +} + +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} + +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0; +} + +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} + +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} + +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} + +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} + +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; +} + +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); +} + +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + -webkit-box-shadow: none; + box-shadow: none; +} + +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; +} + +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} + +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +@media (min-width: 768px) { + .container { + width: 750px; + } +} + +@media (min-width: 992px) { + .container { + width: 970px; + } +} + +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} + +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +.row { + margin-right: -15px; + margin-left: -15px; +} + +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} + +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} + +.col-xs-12 { + width: 100%; +} + +.col-xs-11 { + width: 91.66666667%; +} + +.col-xs-10 { + width: 83.33333333%; +} + +.col-xs-9 { + width: 75%; +} + +.col-xs-8 { + width: 66.66666667%; +} + +.col-xs-7 { + width: 58.33333333%; +} + +.col-xs-6 { + width: 50%; +} + +.col-xs-5 { + width: 41.66666667%; +} + +.col-xs-4 { + width: 33.33333333%; +} + +.col-xs-3 { + width: 25%; +} + +.col-xs-2 { + width: 16.66666667%; +} + +.col-xs-1 { + width: 8.33333333%; +} + +.col-xs-pull-12 { + right: 100%; +} + +.col-xs-pull-11 { + right: 91.66666667%; +} + +.col-xs-pull-10 { + right: 83.33333333%; +} + +.col-xs-pull-9 { + right: 75%; +} + +.col-xs-pull-8 { + right: 66.66666667%; +} + +.col-xs-pull-7 { + right: 58.33333333%; +} + +.col-xs-pull-6 { + right: 50%; +} + +.col-xs-pull-5 { + right: 41.66666667%; +} + +.col-xs-pull-4 { + right: 33.33333333%; +} + +.col-xs-pull-3 { + right: 25%; +} + +.col-xs-pull-2 { + right: 16.66666667%; +} + +.col-xs-pull-1 { + right: 8.33333333%; +} + +.col-xs-pull-0 { + right: auto; +} + +.col-xs-push-12 { + left: 100%; +} + +.col-xs-push-11 { + left: 91.66666667%; +} + +.col-xs-push-10 { + left: 83.33333333%; +} + +.col-xs-push-9 { + left: 75%; +} + +.col-xs-push-8 { + left: 66.66666667%; +} + +.col-xs-push-7 { + left: 58.33333333%; +} + +.col-xs-push-6 { + left: 50%; +} + +.col-xs-push-5 { + left: 41.66666667%; +} + +.col-xs-push-4 { + left: 33.33333333%; +} + +.col-xs-push-3 { + left: 25%; +} + +.col-xs-push-2 { + left: 16.66666667%; +} + +.col-xs-push-1 { + left: 8.33333333%; +} + +.col-xs-push-0 { + left: auto; +} + +.col-xs-offset-12 { + margin-left: 100%; +} + +.col-xs-offset-11 { + margin-left: 91.66666667%; +} + +.col-xs-offset-10 { + margin-left: 83.33333333%; +} + +.col-xs-offset-9 { + margin-left: 75%; +} + +.col-xs-offset-8 { + margin-left: 66.66666667%; +} + +.col-xs-offset-7 { + margin-left: 58.33333333%; +} + +.col-xs-offset-6 { + margin-left: 50%; +} + +.col-xs-offset-5 { + margin-left: 41.66666667%; +} + +.col-xs-offset-4 { + margin-left: 33.33333333%; +} + +.col-xs-offset-3 { + margin-left: 25%; +} + +.col-xs-offset-2 { + margin-left: 16.66666667%; +} + +.col-xs-offset-1 { + margin-left: 8.33333333%; +} + +.col-xs-offset-0 { + margin-left: 0; +} + +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left; + } + + .col-sm-12 { + width: 100%; + } + + .col-sm-11 { + width: 91.66666667%; + } + + .col-sm-10 { + width: 83.33333333%; + } + + .col-sm-9 { + width: 75%; + } + + .col-sm-8 { + width: 66.66666667%; + } + + .col-sm-7 { + width: 58.33333333%; + } + + .col-sm-6 { + width: 50%; + } + + .col-sm-5 { + width: 41.66666667%; + } + + .col-sm-4 { + width: 33.33333333%; + } + + .col-sm-3 { + width: 25%; + } + + .col-sm-2 { + width: 16.66666667%; + } + + .col-sm-1 { + width: 8.33333333%; + } + + .col-sm-pull-12 { + right: 100%; + } + + .col-sm-pull-11 { + right: 91.66666667%; + } + + .col-sm-pull-10 { + right: 83.33333333%; + } + + .col-sm-pull-9 { + right: 75%; + } + + .col-sm-pull-8 { + right: 66.66666667%; + } + + .col-sm-pull-7 { + right: 58.33333333%; + } + + .col-sm-pull-6 { + right: 50%; + } + + .col-sm-pull-5 { + right: 41.66666667%; + } + + .col-sm-pull-4 { + right: 33.33333333%; + } + + .col-sm-pull-3 { + right: 25%; + } + + .col-sm-pull-2 { + right: 16.66666667%; + } + + .col-sm-pull-1 { + right: 8.33333333%; + } + + .col-sm-pull-0 { + right: auto; + } + + .col-sm-push-12 { + left: 100%; + } + + .col-sm-push-11 { + left: 91.66666667%; + } + + .col-sm-push-10 { + left: 83.33333333%; + } + + .col-sm-push-9 { + left: 75%; + } + + .col-sm-push-8 { + left: 66.66666667%; + } + + .col-sm-push-7 { + left: 58.33333333%; + } + + .col-sm-push-6 { + left: 50%; + } + + .col-sm-push-5 { + left: 41.66666667%; + } + + .col-sm-push-4 { + left: 33.33333333%; + } + + .col-sm-push-3 { + left: 25%; + } + + .col-sm-push-2 { + left: 16.66666667%; + } + + .col-sm-push-1 { + left: 8.33333333%; + } + + .col-sm-push-0 { + left: auto; + } + + .col-sm-offset-12 { + margin-left: 100%; + } + + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + + .col-sm-offset-9 { + margin-left: 75%; + } + + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + + .col-sm-offset-6 { + margin-left: 50%; + } + + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + + .col-sm-offset-3 { + margin-left: 25%; + } + + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + + .col-sm-offset-0 { + margin-left: 0; + } +} + +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left; + } + + .col-md-12 { + width: 100%; + } + + .col-md-11 { + width: 91.66666667%; + } + + .col-md-10 { + width: 83.33333333%; + } + + .col-md-9 { + width: 75%; + } + + .col-md-8 { + width: 66.66666667%; + } + + .col-md-7 { + width: 58.33333333%; + } + + .col-md-6 { + width: 50%; + } + + .col-md-5 { + width: 41.66666667%; + } + + .col-md-4 { + width: 33.33333333%; + } + + .col-md-3 { + width: 25%; + } + + .col-md-2 { + width: 16.66666667%; + } + + .col-md-1 { + width: 8.33333333%; + } + + .col-md-pull-12 { + right: 100%; + } + + .col-md-pull-11 { + right: 91.66666667%; + } + + .col-md-pull-10 { + right: 83.33333333%; + } + + .col-md-pull-9 { + right: 75%; + } + + .col-md-pull-8 { + right: 66.66666667%; + } + + .col-md-pull-7 { + right: 58.33333333%; + } + + .col-md-pull-6 { + right: 50%; + } + + .col-md-pull-5 { + right: 41.66666667%; + } + + .col-md-pull-4 { + right: 33.33333333%; + } + + .col-md-pull-3 { + right: 25%; + } + + .col-md-pull-2 { + right: 16.66666667%; + } + + .col-md-pull-1 { + right: 8.33333333%; + } + + .col-md-pull-0 { + right: auto; + } + + .col-md-push-12 { + left: 100%; + } + + .col-md-push-11 { + left: 91.66666667%; + } + + .col-md-push-10 { + left: 83.33333333%; + } + + .col-md-push-9 { + left: 75%; + } + + .col-md-push-8 { + left: 66.66666667%; + } + + .col-md-push-7 { + left: 58.33333333%; + } + + .col-md-push-6 { + left: 50%; + } + + .col-md-push-5 { + left: 41.66666667%; + } + + .col-md-push-4 { + left: 33.33333333%; + } + + .col-md-push-3 { + left: 25%; + } + + .col-md-push-2 { + left: 16.66666667%; + } + + .col-md-push-1 { + left: 8.33333333%; + } + + .col-md-push-0 { + left: auto; + } + + .col-md-offset-12 { + margin-left: 100%; + } + + .col-md-offset-11 { + margin-left: 91.66666667%; + } + + .col-md-offset-10 { + margin-left: 83.33333333%; + } + + .col-md-offset-9 { + margin-left: 75%; + } + + .col-md-offset-8 { + margin-left: 66.66666667%; + } + + .col-md-offset-7 { + margin-left: 58.33333333%; + } + + .col-md-offset-6 { + margin-left: 50%; + } + + .col-md-offset-5 { + margin-left: 41.66666667%; + } + + .col-md-offset-4 { + margin-left: 33.33333333%; + } + + .col-md-offset-3 { + margin-left: 25%; + } + + .col-md-offset-2 { + margin-left: 16.66666667%; + } + + .col-md-offset-1 { + margin-left: 8.33333333%; + } + + .col-md-offset-0 { + margin-left: 0; + } +} + +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left; + } + + .col-lg-12 { + width: 100%; + } + + .col-lg-11 { + width: 91.66666667%; + } + + .col-lg-10 { + width: 83.33333333%; + } + + .col-lg-9 { + width: 75%; + } + + .col-lg-8 { + width: 66.66666667%; + } + + .col-lg-7 { + width: 58.33333333%; + } + + .col-lg-6 { + width: 50%; + } + + .col-lg-5 { + width: 41.66666667%; + } + + .col-lg-4 { + width: 33.33333333%; + } + + .col-lg-3 { + width: 25%; + } + + .col-lg-2 { + width: 16.66666667%; + } + + .col-lg-1 { + width: 8.33333333%; + } + + .col-lg-pull-12 { + right: 100%; + } + + .col-lg-pull-11 { + right: 91.66666667%; + } + + .col-lg-pull-10 { + right: 83.33333333%; + } + + .col-lg-pull-9 { + right: 75%; + } + + .col-lg-pull-8 { + right: 66.66666667%; + } + + .col-lg-pull-7 { + right: 58.33333333%; + } + + .col-lg-pull-6 { + right: 50%; + } + + .col-lg-pull-5 { + right: 41.66666667%; + } + + .col-lg-pull-4 { + right: 33.33333333%; + } + + .col-lg-pull-3 { + right: 25%; + } + + .col-lg-pull-2 { + right: 16.66666667%; + } + + .col-lg-pull-1 { + right: 8.33333333%; + } + + .col-lg-pull-0 { + right: auto; + } + + .col-lg-push-12 { + left: 100%; + } + + .col-lg-push-11 { + left: 91.66666667%; + } + + .col-lg-push-10 { + left: 83.33333333%; + } + + .col-lg-push-9 { + left: 75%; + } + + .col-lg-push-8 { + left: 66.66666667%; + } + + .col-lg-push-7 { + left: 58.33333333%; + } + + .col-lg-push-6 { + left: 50%; + } + + .col-lg-push-5 { + left: 41.66666667%; + } + + .col-lg-push-4 { + left: 33.33333333%; + } + + .col-lg-push-3 { + left: 25%; + } + + .col-lg-push-2 { + left: 16.66666667%; + } + + .col-lg-push-1 { + left: 8.33333333%; + } + + .col-lg-push-0 { + left: auto; + } + + .col-lg-offset-12 { + margin-left: 100%; + } + + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + + .col-lg-offset-9 { + margin-left: 75%; + } + + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + + .col-lg-offset-6 { + margin-left: 50%; + } + + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + + .col-lg-offset-3 { + margin-left: 25%; + } + + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + + .col-lg-offset-0 { + margin-left: 0; + } +} + +table { + background-color: transparent; +} + +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777; + text-align: left; +} + +th { + text-align: left; +} + +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} + +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd; +} + +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} + +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} + +.table > tbody + tbody { + border-top: 2px solid #ddd; +} + +.table .table { + background-color: #fff; +} + +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} + +.table-bordered { + border: 1px solid #ddd; +} + +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} + +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} + +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #f9f9f9; +} + +.table-hover > tbody > tr:hover { + background-color: #f5f5f5; +} + +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} + +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; +} + +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} + +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr:hover > .active, +.table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8; +} + +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} + +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} + +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} + +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr:hover > .info, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} + +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} + +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr:hover > .warning, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} + +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} + +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr:hover > .danger, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} + +.table-responsive { + min-height: .01%; + overflow-x: auto; +} + +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd; + } + + .table-responsive > .table { + margin-bottom: 0; + } + + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + + .table-responsive > .table-bordered { + border: 0; + } + + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} + +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} + +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; +} + +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; +} + +input[type="file"] { + display: block; +} + +input[type="range"] { + display: block; + width: 100%; +} + +select[multiple], +select[size] { + height: auto; +} + +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555; +} + +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} + +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6); +} + +.form-control::-moz-placeholder { + color: #999; + opacity: 1; +} + +.form-control:-ms-input-placeholder { + color: #999; +} + +.form-control::-webkit-input-placeholder { + color: #999; +} + +.form-control::-ms-expand { + background-color: transparent; + border: 0; +} + +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + background-color: #eee; + opacity: 1; +} + +.form-control[disabled], +fieldset[disabled] .form-control { + cursor: not-allowed; +} + +textarea.form-control { + height: auto; +} + +input[type="search"] { + -webkit-appearance: none; +} + +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"].form-control, + input[type="time"].form-control, + input[type="datetime-local"].form-control, + input[type="month"].form-control { + line-height: 34px; + } + + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm, + .input-group-sm input[type="date"], + .input-group-sm input[type="time"], + .input-group-sm input[type="datetime-local"], + .input-group-sm input[type="month"] { + line-height: 30px; + } + + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg, + .input-group-lg input[type="date"], + .input-group-lg input[type="time"], + .input-group-lg input[type="datetime-local"], + .input-group-lg input[type="month"] { + line-height: 46px; + } +} + +.form-group { + margin-bottom: 15px; +} + +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} + +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} + +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px; +} + +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} + +.radio-inline, +.checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} + +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} + +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"].disabled, +input[type="checkbox"].disabled, +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed; +} + +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} + +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} + +.form-control-static { + min-height: 34px; + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0; +} + +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-right: 0; + padding-left: 0; +} + +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +select.input-sm { + height: 30px; + line-height: 30px; +} + +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} + +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.form-group-sm select.form-control { + height: 30px; + line-height: 30px; +} + +.form-group-sm textarea.form-control, +.form-group-sm select[multiple].form-control { + height: auto; +} + +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5; +} + +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} + +select.input-lg { + height: 46px; + line-height: 46px; +} + +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} + +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} + +.form-group-lg select.form-control { + height: 46px; + line-height: 46px; +} + +.form-group-lg textarea.form-control, +.form-group-lg select[multiple].form-control { + height: auto; +} + +.form-group-lg .form-control-static { + height: 46px; + min-height: 38px; + padding: 11px 16px; + font-size: 18px; + line-height: 1.3333333; +} + +.has-feedback { + position: relative; +} + +.has-feedback .form-control { + padding-right: 42.5px; +} + +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + pointer-events: none; +} + +.input-lg + .form-control-feedback, +.input-group-lg + .form-control-feedback, +.form-group-lg .form-control + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px; +} + +.input-sm + .form-control-feedback, +.input-group-sm + .form-control-feedback, +.form-group-sm .form-control + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} + +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { + color: #3c763d; +} + +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} + +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; +} + +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} + +.has-success .form-control-feedback { + color: #3c763d; +} + +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { + color: #8a6d3b; +} + +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} + +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; +} + +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} + +.has-warning .form-control-feedback { + color: #8a6d3b; +} + +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { + color: #a94442; +} + +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} + +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; +} + +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} + +.has-error .form-control-feedback { + color: #a94442; +} + +.has-feedback label ~ .form-control-feedback { + top: 25px; +} + +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} + +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} + +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + + .form-inline .form-control-static { + display: inline-block; + } + + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + + .form-inline .input-group > .form-control { + width: 100%; + } + + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} + +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} + +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} + +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} + +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right; + } +} + +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; +} + +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 11px; + font-size: 18px; + } +} + +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + font-size: 12px; + } +} + +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} + +.btn:focus, +.btn:active:focus, +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.btn:hover, +.btn:focus, +.btn.focus { + color: #333; + text-decoration: none; +} + +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} + +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65; +} + +a.btn.disabled, +fieldset[disabled] a.btn { + pointer-events: none; +} + +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} + +.btn-default:focus, +.btn-default.focus { + color: #333; + background-color: #e6e6e6; + border-color: #8c8c8c; +} + +.btn-default:hover { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} + +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} + +.btn-default:active:hover, +.btn-default.active:hover, +.open > .dropdown-toggle.btn-default:hover, +.btn-default:active:focus, +.btn-default.active:focus, +.open > .dropdown-toggle.btn-default:focus, +.btn-default:active.focus, +.btn-default.active.focus, +.open > .dropdown-toggle.btn-default.focus { + color: #333; + background-color: #d4d4d4; + border-color: #8c8c8c; +} + +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; +} + +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus { + background-color: #fff; + border-color: #ccc; +} + +.btn-default .badge { + color: #fff; + background-color: #333; +} + +.btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; +} + +.btn-primary:focus, +.btn-primary.focus { + color: #fff; + background-color: #286090; + border-color: #122b40; +} + +.btn-primary:hover { + color: #fff; + background-color: #286090; + border-color: #204d74; +} + +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #286090; + border-color: #204d74; +} + +.btn-primary:active:hover, +.btn-primary.active:hover, +.open > .dropdown-toggle.btn-primary:hover, +.btn-primary:active:focus, +.btn-primary.active:focus, +.open > .dropdown-toggle.btn-primary:focus, +.btn-primary:active.focus, +.btn-primary.active.focus, +.open > .dropdown-toggle.btn-primary.focus { + color: #fff; + background-color: #204d74; + border-color: #122b40; +} + +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; +} + +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus { + background-color: #337ab7; + border-color: #2e6da4; +} + +.btn-primary .badge { + color: #337ab7; + background-color: #fff; +} + +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; +} + +.btn-success:focus, +.btn-success.focus { + color: #fff; + background-color: #449d44; + border-color: #255625; +} + +.btn-success:hover { + color: #fff; + background-color: #449d44; + border-color: #398439; +} + +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #449d44; + border-color: #398439; +} + +.btn-success:active:hover, +.btn-success.active:hover, +.open > .dropdown-toggle.btn-success:hover, +.btn-success:active:focus, +.btn-success.active:focus, +.open > .dropdown-toggle.btn-success:focus, +.btn-success:active.focus, +.btn-success.active.focus, +.open > .dropdown-toggle.btn-success.focus { + color: #fff; + background-color: #398439; + border-color: #255625; +} + +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; +} + +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus { + background-color: #5cb85c; + border-color: #4cae4c; +} + +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} + +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} + +.btn-info:focus, +.btn-info.focus { + color: #fff; + background-color: #31b0d5; + border-color: #1b6d85; +} + +.btn-info:hover { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} + +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} + +.btn-info:active:hover, +.btn-info.active:hover, +.open > .dropdown-toggle.btn-info:hover, +.btn-info:active:focus, +.btn-info.active:focus, +.open > .dropdown-toggle.btn-info:focus, +.btn-info:active.focus, +.btn-info.active.focus, +.open > .dropdown-toggle.btn-info.focus { + color: #fff; + background-color: #269abc; + border-color: #1b6d85; +} + +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; +} + +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus { + background-color: #5bc0de; + border-color: #46b8da; +} + +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} + +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} + +.btn-warning:focus, +.btn-warning.focus { + color: #fff; + background-color: #ec971f; + border-color: #985f0d; +} + +.btn-warning:hover { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} + +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} + +.btn-warning:active:hover, +.btn-warning.active:hover, +.open > .dropdown-toggle.btn-warning:hover, +.btn-warning:active:focus, +.btn-warning.active:focus, +.open > .dropdown-toggle.btn-warning:focus, +.btn-warning:active.focus, +.btn-warning.active.focus, +.open > .dropdown-toggle.btn-warning.focus { + color: #fff; + background-color: #d58512; + border-color: #985f0d; +} + +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background-image: none; +} + +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus { + background-color: #f0ad4e; + border-color: #eea236; +} + +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} + +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} + +.btn-danger:focus, +.btn-danger.focus { + color: #fff; + background-color: #c9302c; + border-color: #761c19; +} + +.btn-danger:hover { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} + +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} + +.btn-danger:active:hover, +.btn-danger.active:hover, +.open > .dropdown-toggle.btn-danger:hover, +.btn-danger:active:focus, +.btn-danger.active:focus, +.open > .dropdown-toggle.btn-danger:focus, +.btn-danger:active.focus, +.btn-danger.active.focus, +.open > .dropdown-toggle.btn-danger.focus { + color: #fff; + background-color: #ac2925; + border-color: #761c19; +} + +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; +} + +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus { + background-color: #d9534f; + border-color: #d43f3a; +} + +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} + +.btn-link { + font-weight: normal; + color: #337ab7; + border-radius: 0; +} + +.btn-link, +.btn-link:active, +.btn-link.active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} + +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} + +.btn-link:hover, +.btn-link:focus { + color: #23527c; + text-decoration: underline; + background-color: transparent; +} + +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #777; + text-decoration: none; +} + +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} + +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.btn-block { + display: block; + width: 100%; +} + +.btn-block + .btn-block { + margin-top: 5px; +} + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} + +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + -o-transition: opacity .15s linear; + transition: opacity .15s linear; +} + +.fade.in { + opacity: 1; +} + +.collapse { + display: none; +} + +.collapse.in { + display: block; +} + +tr.collapse.in { + display: table-row; +} + +tbody.collapse.in { + display: table-row-group; +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; + -webkit-transition-duration: .35s; + -o-transition-duration: .35s; + transition-duration: .35s; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; +} + +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-top: 4px solid \9; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} + +.dropup, +.dropdown { + position: relative; +} + +.dropdown-toggle:focus { + outline: 0; +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} + +.dropdown-menu.pull-right { + right: 0; + left: auto; +} + +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} + +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} + +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} + +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + text-decoration: none; + background-color: #337ab7; + outline: 0; +} + +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #777; +} + +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} + +.open > .dropdown-menu { + display: block; +} + +.open > a { + outline: 0; +} + +.dropdown-menu-right { + right: 0; + left: auto; +} + +.dropdown-menu-left { + right: auto; + left: 0; +} + +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #777; + white-space: nowrap; +} + +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} + +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} + +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px dashed; + border-bottom: 4px solid \9; +} + +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; +} + +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } +} + +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} + +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} + +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} + +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} + +.btn-toolbar { + margin-left: -5px; +} + +.btn-toolbar .btn, +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} + +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} + +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} + +.btn-group > .btn:first-child { + margin-left: 0; +} + +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.btn-group > .btn-group { + float: left; +} + +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} + +.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} + +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} + +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} + +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} + +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} + +.btn .caret { + margin-left: 0; +} + +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} + +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} + +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} + +.btn-group-vertical > .btn-group > .btn { + float: none; +} + +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} + +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} + +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} + +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} + +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} + +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} + +.btn-group-justified > .btn-group .btn { + width: 100%; +} + +.btn-group-justified > .btn-group .dropdown-menu { + left: auto; +} + +[data-toggle="buttons"] > .btn input[type="radio"], +[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], +[data-toggle="buttons"] > .btn input[type="checkbox"], +[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} + +.input-group { + position: relative; + display: table; + border-collapse: separate; +} + +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} + +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} + +.input-group .form-control:focus { + z-index: 3; +} + +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} + +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} + +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} + +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} + +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} + +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} + +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} + +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} + +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px; +} + +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} + +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} + +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} + +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group-addon:first-child { + border-right: 0; +} + +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group-addon:last-child { + border-left: 0; +} + +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} + +.input-group-btn > .btn { + position: relative; +} + +.input-group-btn > .btn + .btn { + margin-left: -1px; +} + +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} + +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} + +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + z-index: 2; + margin-left: -1px; +} + +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.nav > li { + position: relative; + display: block; +} + +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} + +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eee; +} + +.nav > li.disabled > a { + color: #777; +} + +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #777; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} + +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eee; + border-color: #337ab7; +} + +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} + +.nav > li > a > img { + max-width: none; +} + +.nav-tabs { + border-bottom: 1px solid #ddd; +} + +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} + +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} + +.nav-tabs > li > a:hover { + border-color: #eee #eee #ddd; +} + +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} + +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} + +.nav-tabs.nav-justified > li { + float: none; +} + +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} + +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} + +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} + +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} + +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} + +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} + +.nav-pills > li { + float: left; +} + +.nav-pills > li > a { + border-radius: 4px; +} + +.nav-pills > li + li { + margin-left: 2px; +} + +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #fff; + background-color: #337ab7; +} + +.nav-stacked > li { + float: none; +} + +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} + +.nav-justified { + width: 100%; +} + +.nav-justified > li { + float: none; +} + +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} + +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} + +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + + .nav-justified > li > a { + margin-bottom: 0; + } +} + +.nav-tabs-justified { + border-bottom: 0; +} + +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} + +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} + +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} + +.tab-content > .tab-pane { + display: none; +} + +.tab-content > .active { + display: block; +} + +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} + +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} + +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} + +.navbar-collapse { + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); +} + +.navbar-collapse.in { + overflow-y: auto; +} + +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + + .navbar-collapse.in { + overflow-y: visible; + } + + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} + +.navbar-fixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} + +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} + +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} + +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} + +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} + +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} + +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} + +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} + +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} + +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} + +.navbar-brand { + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} + +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} + +.navbar-brand > img { + display: block; +} + +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} + +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} + +.navbar-toggle:focus { + outline: 0; +} + +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} + +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} + +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} + +.navbar-nav { + margin: 7.5px -15px; +} + +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} + +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} + +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + + .navbar-nav > li { + float: left; + } + + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } +} + +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); +} + +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + + .navbar-form .form-control-static { + display: inline-block; + } + + .navbar-form .input-group { + display: inline-table; + vertical-align: middle; + } + + .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn, + .navbar-form .input-group .form-control { + width: auto; + } + + .navbar-form .input-group > .form-control { + width: 100%; + } + + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + + .navbar-form .radio label, + .navbar-form .checkbox label { + padding-left: 0; + } + + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} + +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } + + .navbar-form .form-group:last-child { + margin-bottom: 0; + } +} + +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } +} + +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} + +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} + +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} + +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} + +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } +} + +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + + .navbar-right { + float: right !important; + margin-right: -15px; + } + + .navbar-right ~ .navbar-right { + margin-right: 0; + } +} + +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} + +.navbar-default .navbar-brand { + color: #777; +} + +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} + +.navbar-default .navbar-text { + color: #777; +} + +.navbar-default .navbar-nav > li > a { + color: #777; +} + +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333; + background-color: transparent; +} + +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #e7e7e7; +} + +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} + +.navbar-default .navbar-toggle { + border-color: #ddd; +} + +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} + +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} + +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e7e7e7; +} + +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555; + background-color: #e7e7e7; +} + +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333; + background-color: transparent; + } + + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #e7e7e7; + } + + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} + +.navbar-default .navbar-link { + color: #777; +} + +.navbar-default .navbar-link:hover { + color: #333; +} + +.navbar-default .btn-link { + color: #777; +} + +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #333; +} + +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #ccc; +} + +.navbar-inverse { + background-color: #222; + border-color: #080808; +} + +.navbar-inverse .navbar-brand { + color: #9d9d9d; +} + +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} + +.navbar-inverse .navbar-text { + color: #9d9d9d; +} + +.navbar-inverse .navbar-nav > li > a { + color: #9d9d9d; +} + +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} + +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #080808; +} + +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; +} + +.navbar-inverse .navbar-toggle { + border-color: #333; +} + +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333; +} + +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} + +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} + +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #080808; +} + +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #9d9d9d; + } + + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #080808; + } + + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } +} + +.navbar-inverse .navbar-link { + color: #9d9d9d; +} + +.navbar-inverse .navbar-link:hover { + color: #fff; +} + +.navbar-inverse .btn-link { + color: #9d9d9d; +} + +.navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link:focus { + color: #fff; +} + +.navbar-inverse .btn-link[disabled]:hover, +fieldset[disabled] .navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link[disabled]:focus, +fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #444; +} + +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} + +.breadcrumb > li { + display: inline-block; +} + +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0"; +} + +.breadcrumb > .active { + color: #777; +} + +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} + +.pagination > li { + display: inline; +} + +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #337ab7; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} + +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} + +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + z-index: 2; + color: #23527c; + background-color: #eee; + border-color: #ddd; +} + +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 3; + color: #fff; + cursor: default; + background-color: #337ab7; + border-color: #337ab7; +} + +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #777; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} + +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; +} + +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} + +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} + +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; +} + +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} + +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} + +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} + +.pager li { + display: inline; +} + +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} + +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eee; +} + +.pager .next > a, +.pager .next > span { + float: right; +} + +.pager .previous > a, +.pager .previous > span { + float: left; +} + +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #777; + cursor: not-allowed; + background-color: #fff; +} + +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} + +a.label:hover, +a.label:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} + +.label:empty { + display: none; +} + +.btn .label { + position: relative; + top: -1px; +} + +.label-default { + background-color: #777; +} + +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #5e5e5e; +} + +.label-primary { + background-color: #337ab7; +} + +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #286090; +} + +.label-success { + background-color: #5cb85c; +} + +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} + +.label-info { + background-color: #5bc0de; +} + +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} + +.label-warning { + background-color: #f0ad4e; +} + +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} + +.label-danger { + background-color: #d9534f; +} + +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} + +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: middle; + background-color: #777; + border-radius: 10px; +} + +.badge:empty { + display: none; +} + +.btn .badge { + position: relative; + top: -1px; +} + +.btn-xs .badge, +.btn-group-xs > .btn .badge { + top: 0; + padding: 1px 5px; +} + +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} + +.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #337ab7; + background-color: #fff; +} + +.list-group-item > .badge { + float: right; +} + +.list-group-item > .badge + .badge { + margin-right: 5px; +} + +.nav-pills > li > a > .badge { + margin-left: 3px; +} + +.jumbotron { + padding-top: 30px; + padding-bottom: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #eee; +} + +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} + +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} + +.jumbotron > hr { + border-top-color: #d5d5d5; +} + +.container .jumbotron, +.container-fluid .jumbotron { + padding-right: 15px; + padding-left: 15px; + border-radius: 6px; +} + +.jumbotron .container { + max-width: 100%; +} + +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + + .container .jumbotron, + .container-fluid .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} + +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: border .2s ease-in-out; + -o-transition: border .2s ease-in-out; + transition: border .2s ease-in-out; +} + +.thumbnail > img, +.thumbnail a > img { + margin-right: auto; + margin-left: auto; +} + +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #337ab7; +} + +.thumbnail .caption { + padding: 9px; + color: #333; +} + +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} + +.alert h4 { + margin-top: 0; + color: inherit; +} + +.alert .alert-link { + font-weight: bold; +} + +.alert > p, +.alert > ul { + margin-bottom: 0; +} + +.alert > p + p { + margin-top: 5px; +} + +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} + +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} + +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.alert-success hr { + border-top-color: #c9e2b3; +} + +.alert-success .alert-link { + color: #2b542c; +} + +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} + +.alert-info hr { + border-top-color: #a6e1ec; +} + +.alert-info .alert-link { + color: #245269; +} + +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} + +.alert-warning hr { + border-top-color: #f7e1b5; +} + +.alert-warning .alert-link { + color: #66512c; +} + +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} + +.alert-danger hr { + border-top-color: #e4b9c0; +} + +.alert-danger .alert-link { + color: #843534; +} + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} + +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); +} + +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #337ab7; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + -o-transition: width .6s ease; + transition: width .6s ease; +} + +.progress-striped .progress-bar, +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px; +} + +.progress.active .progress-bar, +.progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} + +.progress-bar-success { + background-color: #5cb85c; +} + +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} + +.progress-bar-info { + background-color: #5bc0de; +} + +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} + +.progress-bar-warning { + background-color: #f0ad4e; +} + +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} + +.progress-bar-danger { + background-color: #d9534f; +} + +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} + +.media { + margin-top: 15px; +} + +.media:first-child { + margin-top: 0; +} + +.media, +.media-body { + overflow: hidden; + zoom: 1; +} + +.media-body { + width: 10000px; +} + +.media-object { + display: block; +} + +.media-object.img-thumbnail { + max-width: none; +} + +.media-right, +.media > .pull-right { + padding-left: 10px; +} + +.media-left, +.media > .pull-left { + padding-right: 10px; +} + +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} + +.media-middle { + vertical-align: middle; +} + +.media-bottom { + vertical-align: bottom; +} + +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} + +.media-list { + padding-left: 0; + list-style: none; +} + +.list-group { + padding-left: 0; + margin-bottom: 20px; +} + +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} + +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} + +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} + +a.list-group-item, +button.list-group-item { + color: #555; +} + +a.list-group-item .list-group-item-heading, +button.list-group-item .list-group-item-heading { + color: #333; +} + +a.list-group-item:hover, +button.list-group-item:hover, +a.list-group-item:focus, +button.list-group-item:focus { + color: #555; + text-decoration: none; + background-color: #f5f5f5; +} + +button.list-group-item { + width: 100%; + text-align: left; +} + +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + color: #777; + cursor: not-allowed; + background-color: #eee; +} + +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} + +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #777; +} + +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} + +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} + +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #c7ddef; +} + +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} + +a.list-group-item-success, +button.list-group-item-success { + color: #3c763d; +} + +a.list-group-item-success .list-group-item-heading, +button.list-group-item-success .list-group-item-heading { + color: inherit; +} + +a.list-group-item-success:hover, +button.list-group-item-success:hover, +a.list-group-item-success:focus, +button.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} + +a.list-group-item-success.active, +button.list-group-item-success.active, +a.list-group-item-success.active:hover, +button.list-group-item-success.active:hover, +a.list-group-item-success.active:focus, +button.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} + +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} + +a.list-group-item-info, +button.list-group-item-info { + color: #31708f; +} + +a.list-group-item-info .list-group-item-heading, +button.list-group-item-info .list-group-item-heading { + color: inherit; +} + +a.list-group-item-info:hover, +button.list-group-item-info:hover, +a.list-group-item-info:focus, +button.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} + +a.list-group-item-info.active, +button.list-group-item-info.active, +a.list-group-item-info.active:hover, +button.list-group-item-info.active:hover, +a.list-group-item-info.active:focus, +button.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} + +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} + +a.list-group-item-warning, +button.list-group-item-warning { + color: #8a6d3b; +} + +a.list-group-item-warning .list-group-item-heading, +button.list-group-item-warning .list-group-item-heading { + color: inherit; +} + +a.list-group-item-warning:hover, +button.list-group-item-warning:hover, +a.list-group-item-warning:focus, +button.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} + +a.list-group-item-warning.active, +button.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +button.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus, +button.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} + +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} + +a.list-group-item-danger, +button.list-group-item-danger { + color: #a94442; +} + +a.list-group-item-danger .list-group-item-heading, +button.list-group-item-danger .list-group-item-heading { + color: inherit; +} + +a.list-group-item-danger:hover, +button.list-group-item-danger:hover, +a.list-group-item-danger:focus, +button.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} + +a.list-group-item-danger.active, +button.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +button.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus, +button.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} + +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} + +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} + +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} + +.panel-body { + padding: 15px; +} + +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} + +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} + +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} + +.panel-title > a, +.panel-title > small, +.panel-title > .small, +.panel-title > small > a, +.panel-title > .small > a { + color: inherit; +} + +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} + +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} + +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} + +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} + +.list-group + .panel-footer { + border-top-width: 0; +} + +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} + +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-right: 15px; + padding-left: 15px; +} + +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} + +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} + +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} + +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} + +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} + +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} + +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} + +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #ddd; +} + +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} + +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} + +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} + +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} + +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} + +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} + +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} + +.panel-group { + margin-bottom: 20px; +} + +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} + +.panel-group .panel + .panel { + margin-top: 5px; +} + +.panel-group .panel-heading { + border-bottom: 0; +} + +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #ddd; +} + +.panel-group .panel-footer { + border-top: 0; +} + +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} + +.panel-default { + border-color: #ddd; +} + +.panel-default > .panel-heading { + color: #333; + background-color: #f5f5f5; + border-color: #ddd; +} + +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ddd; +} + +.panel-default > .panel-heading .badge { + color: #f5f5f5; + background-color: #333; +} + +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ddd; +} + +.panel-primary { + border-color: #337ab7; +} + +.panel-primary > .panel-heading { + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} + +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #337ab7; +} + +.panel-primary > .panel-heading .badge { + color: #337ab7; + background-color: #fff; +} + +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #337ab7; +} + +.panel-success { + border-color: #d6e9c6; +} + +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} + +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6; +} + +.panel-success > .panel-heading .badge { + color: #dff0d8; + background-color: #3c763d; +} + +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6; +} + +.panel-info { + border-color: #bce8f1; +} + +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} + +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #bce8f1; +} + +.panel-info > .panel-heading .badge { + color: #d9edf7; + background-color: #31708f; +} + +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #bce8f1; +} + +.panel-warning { + border-color: #faebcc; +} + +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} + +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; +} + +.panel-warning > .panel-heading .badge { + color: #fcf8e3; + background-color: #8a6d3b; +} + +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #faebcc; +} + +.panel-danger { + border-color: #ebccd1; +} + +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} + +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; +} + +.panel-danger > .panel-heading .badge { + color: #f2dede; + background-color: #a94442; +} + +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; +} + +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; +} + +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} + +.embed-responsive-16by9 { + padding-bottom: 56.25%; +} + +.embed-responsive-4by3 { + padding-bottom: 75%; +} + +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); +} + +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, .15); +} + +.well-lg { + padding: 24px; + border-radius: 6px; +} + +.well-sm { + padding: 9px; + border-radius: 3px; +} + +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .2; +} + +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5; +} + +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: transparent; + border: 0; +} + +.modal-open { + overflow: hidden; +} + +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: hidden; + -webkit-overflow-scrolling: touch; + outline: 0; +} + +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); +} + +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} + +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} + +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} + +.modal-content { + position: relative; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5); +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; +} + +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} + +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5; +} + +.modal-header { + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} + +.modal-header .close { + margin-top: -2px; +} + +.modal-title { + margin: 0; + line-height: 1.42857143; +} + +.modal-body { + position: relative; + padding: 15px; +} + +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} + +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} + +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} + +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} + +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} + +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + } + + .modal-sm { + width: 300px; + } +} + +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} + +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; + font-style: normal; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + filter: alpha(opacity=0); + opacity: 0; + + line-break: auto; +} + +.tooltip.in { + filter: alpha(opacity=90); + opacity: .9; +} + +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} + +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} + +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} + +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} + +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 4px; +} + +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} + +.tooltip.top-left .tooltip-arrow { + right: 5px; + bottom: 0; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} + +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} + +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} + +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} + +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} + +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} + +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + font-style: normal; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + + line-break: auto; +} + +.popover.top { + margin-top: -10px; +} + +.popover.right { + margin-left: 10px; +} + +.popover.bottom { + margin-top: 10px; +} + +.popover.left { + margin-left: -10px; +} + +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} + +.popover-content { + padding: 9px 14px; +} + +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} + +.popover > .arrow { + border-width: 11px; +} + +.popover > .arrow:after { + content: ""; + border-width: 10px; +} + +.popover.top > .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0; +} + +.popover.top > .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0; +} + +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0; +} + +.popover.right > .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0; +} + +.popover.bottom > .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25); +} + +.popover.bottom > .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} + +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25); +} + +.popover.left > .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} + +.carousel { + position: relative; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} + +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + -o-transition: .6s ease-in-out left; + transition: .6s ease-in-out left; +} + +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} + +@media all and (transform-3d), (-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform .6s ease-in-out; + -o-transition: -o-transform .6s ease-in-out; + transition: transform .6s ease-in-out; + + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000px; + perspective: 1000px; + } + + .carousel-inner > .item.next, + .carousel-inner > .item.active.right { + left: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + + .carousel-inner > .item.prev, + .carousel-inner > .item.active.left { + left: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + + .carousel-inner > .item.next.left, + .carousel-inner > .item.prev.right, + .carousel-inner > .item.active { + left: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} + +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} + +.carousel-inner > .active { + left: 0; +} + +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} + +.carousel-inner > .next { + left: 100%; +} + +.carousel-inner > .prev { + left: -100%; +} + +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} + +.carousel-inner > .active.left { + left: -100%; +} + +.carousel-inner > .active.right { + left: 100%; +} + +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + background-color: rgba(0, 0, 0, 0); + filter: alpha(opacity=50); + opacity: .5; +} + +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x; +} + +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x; +} + +.carousel-control:hover, +.carousel-control:focus { + color: #fff; + text-decoration: none; + filter: alpha(opacity=90); + outline: 0; + opacity: .9; +} + +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; + margin-top: -10px; +} + +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; +} + +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; +} + +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + font-family: serif; + line-height: 1; +} + +.carousel-control .icon-prev:before { + content: '\2039'; +} + +.carousel-control .icon-next:before { + content: '\203a'; +} + +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} + +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px; +} + +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #fff; +} + +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); +} + +.carousel-caption .btn { + text-shadow: none; +} + +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -10px; + font-size: 30px; + } + + .carousel-control .glyphicon-chevron-left, + .carousel-control .icon-prev { + margin-left: -10px; + } + + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next { + margin-right: -10px; + } + + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + + .carousel-indicators { + bottom: 20px; + } +} + +.clearfix:before, +.clearfix:after, +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-header:before, +.modal-header:after, +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} + +.clearfix:after, +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-header:after, +.modal-footer:after { + clear: both; +} + +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} + +.pull-right { + float: right !important; +} + +.pull-left { + float: left !important; +} + +.hide { + display: none !important; +} + +.show { + display: block !important; +} + +.invisible { + visibility: hidden; +} + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.hidden { + display: none !important; +} + +.affix { + position: fixed; +} + +@-ms-viewport { + width: device-width; +} + +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} + +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} + +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + + table.visible-xs { + display: table !important; + } + + tr.visible-xs { + display: table-row !important; + } + + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} + +@media (max-width: 767px) { + .visible-xs-block { + display: block !important; + } +} + +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; + } +} + +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + + table.visible-sm { + display: table !important; + } + + tr.visible-sm { + display: table-row !important; + } + + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + + table.visible-md { + display: table !important; + } + + tr.visible-md { + display: table-row !important; + } + + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important; + } +} + +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + + table.visible-lg { + display: table !important; + } + + tr.visible-lg { + display: table-row !important; + } + + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} + +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; + } +} + +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; + } +} + +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important; + } +} + +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} + +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} + +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} + +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} + +.visible-print { + display: none !important; +} + +@media print { + .visible-print { + display: block !important; + } + + table.visible-print { + display: table !important; + } + + tr.visible-print { + display: table-row !important; + } + + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} + +.visible-print-block { + display: none !important; +} + +@media print { + .visible-print-block { + display: block !important; + } +} + +.visible-print-inline { + display: none !important; +} + +@media print { + .visible-print-inline { + display: inline !important; + } +} + +.visible-print-inline-block { + display: none !important; +} + +@media print { + .visible-print-inline-block { + display: inline-block !important; + } +} + +@media print { + .hidden-print { + display: none !important; + } +} + +/*# sourceMappingURL=bootstrap.css.map */ diff --git a/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap.css.map b/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap.css.map new file mode 100644 index 0000000..7314b4e --- /dev/null +++ b/tx-lcn/tx-manager/src/main/resources/static/static/bootstrap/css/bootstrap.css.map @@ -0,0 +1,142 @@ +{ + "version": 3, + "sources": [ + "bootstrap.css", + "less/normalize.less", + "less/print.less", + "less/glyphicons.less", + "less/scaffolding.less", + "less/mixins/vendor-prefixes.less", + "less/mixins/tab-focus.less", + "less/mixins/image.less", + "less/type.less", + "less/mixins/text-emphasis.less", + "less/mixins/background-variant.less", + "less/mixins/text-overflow.less", + "less/code.less", + "less/grid.less", + "less/mixins/grid.less", + "less/mixins/grid-framework.less", + "less/tables.less", + "less/mixins/table-row.less", + "less/forms.less", + "less/mixins/forms.less", + "less/buttons.less", + "less/mixins/buttons.less", + "less/mixins/opacity.less", + "less/component-animations.less", + "less/dropdowns.less", + "less/mixins/nav-divider.less", + "less/mixins/reset-filter.less", + "less/button-groups.less", + "less/mixins/border-radius.less", + "less/input-groups.less", + "less/navs.less", + "less/navbar.less", + "less/mixins/nav-vertical-align.less", + "less/utilities.less", + "less/breadcrumbs.less", + "less/pagination.less", + "less/mixins/pagination.less", + "less/pager.less", + "less/labels.less", + "less/mixins/labels.less", + "less/badges.less", + "less/jumbotron.less", + "less/thumbnails.less", + "less/alerts.less", + "less/mixins/alerts.less", + "less/progress-bars.less", + "less/mixins/gradients.less", + "less/mixins/progress-bar.less", + "less/media.less", + "less/list-group.less", + "less/mixins/list-group.less", + "less/panels.less", + "less/mixins/panels.less", + "less/responsive-embed.less", + "less/wells.less", + "less/close.less", + "less/modals.less", + "less/tooltip.less", + "less/mixins/reset-text.less", + "less/popovers.less", + "less/carousel.less", + "less/mixins/clearfix.less", + "less/mixins/center-block.less", + "less/mixins/hide-text.less", + "less/responsive-utilities.less", + "less/mixins/responsive-visibility.less" + ], + "names": [], + "mappings": "AAAA;;;;GAIG;AACH,4EAA4E;ACG5E;EACE,wBAAA;EACA,2BAAA;EACA,+BAAA;CDDD;ACQD;EACE,UAAA;CDND;ACmBD;;;;;;;;;;;;;EAaE,eAAA;CDjBD;ACyBD;;;;EAIE,sBAAA;EACA,yBAAA;CDvBD;AC+BD;EACE,cAAA;EACA,UAAA;CD7BD;ACqCD;;EAEE,cAAA;CDnCD;AC6CD;EACE,8BAAA;CD3CD;ACmDD;;EAEE,WAAA;CDjDD;AC2DD;EACE,0BAAA;CDzDD;ACgED;;EAEE,kBAAA;CD9DD;ACqED;EACE,mBAAA;CDnED;AC2ED;EACE,eAAA;EACA,iBAAA;CDzED;ACgFD;EACE,iBAAA;EACA,YAAA;CD9ED;ACqFD;EACE,eAAA;CDnFD;AC0FD;;EAEE,eAAA;EACA,eAAA;EACA,mBAAA;EACA,yBAAA;CDxFD;AC2FD;EACE,YAAA;CDzFD;AC4FD;EACE,gBAAA;CD1FD;ACoGD;EACE,UAAA;CDlGD;ACyGD;EACE,iBAAA;CDvGD;ACiHD;EACE,iBAAA;CD/GD;ACsHD;EACE,gCAAA;KAAA,6BAAA;UAAA,wBAAA;EACA,UAAA;CDpHD;AC2HD;EACE,eAAA;CDzHD;ACgID;;;;EAIE,kCAAA;EACA,eAAA;CD9HD;ACgJD;;;;;EAKE,eAAA;EACA,cAAA;EACA,UAAA;CD9ID;ACqJD;EACE,kBAAA;CDnJD;AC6JD;;EAEE,qBAAA;CD3JD;ACsKD;;;;EAIE,2BAAA;EACA,gBAAA;CDpKD;AC2KD;;EAEE,gBAAA;CDzKD;ACgLD;;EAEE,UAAA;EACA,WAAA;CD9KD;ACsLD;EACE,oBAAA;CDpLD;AC+LD;;EAEE,+BAAA;KAAA,4BAAA;UAAA,uBAAA;EACA,WAAA;CD7LD;ACsMD;;EAEE,aAAA;CDpMD;AC4MD;EACE,8BAAA;EACA,gCAAA;KAAA,6BAAA;UAAA,wBAAA;CD1MD;ACmND;;EAEE,yBAAA;CDjND;ACwND;EACE,0BAAA;EACA,cAAA;EACA,+BAAA;CDtND;AC8ND;EACE,UAAA;EACA,WAAA;CD5ND;ACmOD;EACE,eAAA;CDjOD;ACyOD;EACE,kBAAA;CDvOD;ACiPD;EACE,0BAAA;EACA,kBAAA;CD/OD;ACkPD;;EAEE,WAAA;CDhPD;AACD,qFAAqF;AElFrF;EA7FI;;;IAGI,mCAAA;IACA,uBAAA;IACA,oCAAA;YAAA,4BAAA;IACA,6BAAA;GFkLL;EE/KC;;IAEI,2BAAA;GFiLL;EE9KC;IACI,6BAAA;GFgLL;EE7KC;IACI,8BAAA;GF+KL;EE1KC;;IAEI,YAAA;GF4KL;EEzKC;;IAEI,uBAAA;IACA,yBAAA;GF2KL;EExKC;IACI,4BAAA;GF0KL;EEvKC;;IAEI,yBAAA;GFyKL;EEtKC;IACI,2BAAA;GFwKL;EErKC;;;IAGI,WAAA;IACA,UAAA;GFuKL;EEpKC;;IAEI,wBAAA;GFsKL;EEhKC;IACI,cAAA;GFkKL;EEhKC;;IAGQ,kCAAA;GFiKT;EE9JC;IACI,uBAAA;GFgKL;EE7JC;IACI,qCAAA;GF+JL;EEhKC;;IAKQ,kCAAA;GF+JT;EE5JC;;IAGQ,kCAAA;GF6JT;CACF;AGnPD;EACE,oCAAA;EACA,sDAAA;EACA,gYAAA;CHqPD;AG7OD;EACE,mBAAA;EACA,SAAA;EACA,sBAAA;EACA,oCAAA;EACA,mBAAA;EACA,oBAAA;EACA,eAAA;EACA,oCAAA;EACA,mCAAA;CH+OD;AG3OmC;EAAW,iBAAA;CH8O9C;AG7OmC;EAAW,iBAAA;CHgP9C;AG9OmC;;EAAW,iBAAA;CHkP9C;AGjPmC;EAAW,iBAAA;CHoP9C;AGnPmC;EAAW,iBAAA;CHsP9C;AGrPmC;EAAW,iBAAA;CHwP9C;AGvPmC;EAAW,iBAAA;CH0P9C;AGzPmC;EAAW,iBAAA;CH4P9C;AG3PmC;EAAW,iBAAA;CH8P9C;AG7PmC;EAAW,iBAAA;CHgQ9C;AG/PmC;EAAW,iBAAA;CHkQ9C;AGjQmC;EAAW,iBAAA;CHoQ9C;AGnQmC;EAAW,iBAAA;CHsQ9C;AGrQmC;EAAW,iBAAA;CHwQ9C;AGvQmC;EAAW,iBAAA;CH0Q9C;AGzQmC;EAAW,iBAAA;CH4Q9C;AG3QmC;EAAW,iBAAA;CH8Q9C;AG7QmC;EAAW,iBAAA;CHgR9C;AG/QmC;EAAW,iBAAA;CHkR9C;AGjRmC;EAAW,iBAAA;CHoR9C;AGnRmC;EAAW,iBAAA;CHsR9C;AGrRmC;EAAW,iBAAA;CHwR9C;AGvRmC;EAAW,iBAAA;CH0R9C;AGzRmC;EAAW,iBAAA;CH4R9C;AG3RmC;EAAW,iBAAA;CH8R9C;AG7RmC;EAAW,iBAAA;CHgS9C;AG/RmC;EAAW,iBAAA;CHkS9C;AGjSmC;EAAW,iBAAA;CHoS9C;AGnSmC;EAAW,iBAAA;CHsS9C;AGrSmC;EAAW,iBAAA;CHwS9C;AGvSmC;EAAW,iBAAA;CH0S9C;AGzSmC;EAAW,iBAAA;CH4S9C;AG3SmC;EAAW,iBAAA;CH8S9C;AG7SmC;EAAW,iBAAA;CHgT9C;AG/SmC;EAAW,iBAAA;CHkT9C;AGjTmC;EAAW,iBAAA;CHoT9C;AGnTmC;EAAW,iBAAA;CHsT9C;AGrTmC;EAAW,iBAAA;CHwT9C;AGvTmC;EAAW,iBAAA;CH0T9C;AGzTmC;EAAW,iBAAA;CH4T9C;AG3TmC;EAAW,iBAAA;CH8T9C;AG7TmC;EAAW,iBAAA;CHgU9C;AG/TmC;EAAW,iBAAA;CHkU9C;AGjUmC;EAAW,iBAAA;CHoU9C;AGnUmC;EAAW,iBAAA;CHsU9C;AGrUmC;EAAW,iBAAA;CHwU9C;AGvUmC;EAAW,iBAAA;CH0U9C;AGzUmC;EAAW,iBAAA;CH4U9C;AG3UmC;EAAW,iBAAA;CH8U9C;AG7UmC;EAAW,iBAAA;CHgV9C;AG/UmC;EAAW,iBAAA;CHkV9C;AGjVmC;EAAW,iBAAA;CHoV9C;AGnVmC;EAAW,iBAAA;CHsV9C;AGrVmC;EAAW,iBAAA;CHwV9C;AGvVmC;EAAW,iBAAA;CH0V9C;AGzVmC;EAAW,iBAAA;CH4V9C;AG3VmC;EAAW,iBAAA;CH8V9C;AG7VmC;EAAW,iBAAA;CHgW9C;AG/VmC;EAAW,iBAAA;CHkW9C;AGjWmC;EAAW,iBAAA;CHoW9C;AGnWmC;EAAW,iBAAA;CHsW9C;AGrWmC;EAAW,iBAAA;CHwW9C;AGvWmC;EAAW,iBAAA;CH0W9C;AGzWmC;EAAW,iBAAA;CH4W9C;AG3WmC;EAAW,iBAAA;CH8W9C;AG7WmC;EAAW,iBAAA;CHgX9C;AG/WmC;EAAW,iBAAA;CHkX9C;AGjXmC;EAAW,iBAAA;CHoX9C;AGnXmC;EAAW,iBAAA;CHsX9C;AGrXmC;EAAW,iBAAA;CHwX9C;AGvXmC;EAAW,iBAAA;CH0X9C;AGzXmC;EAAW,iBAAA;CH4X9C;AG3XmC;EAAW,iBAAA;CH8X9C;AG7XmC;EAAW,iBAAA;CHgY9C;AG/XmC;EAAW,iBAAA;CHkY9C;AGjYmC;EAAW,iBAAA;CHoY9C;AGnYmC;EAAW,iBAAA;CHsY9C;AGrYmC;EAAW,iBAAA;CHwY9C;AGvYmC;EAAW,iBAAA;CH0Y9C;AGzYmC;EAAW,iBAAA;CH4Y9C;AG3YmC;EAAW,iBAAA;CH8Y9C;AG7YmC;EAAW,iBAAA;CHgZ9C;AG/YmC;EAAW,iBAAA;CHkZ9C;AGjZmC;EAAW,iBAAA;CHoZ9C;AGnZmC;EAAW,iBAAA;CHsZ9C;AGrZmC;EAAW,iBAAA;CHwZ9C;AGvZmC;EAAW,iBAAA;CH0Z9C;AGzZmC;EAAW,iBAAA;CH4Z9C;AG3ZmC;EAAW,iBAAA;CH8Z9C;AG7ZmC;EAAW,iBAAA;CHga9C;AG/ZmC;EAAW,iBAAA;CHka9C;AGjamC;EAAW,iBAAA;CHoa9C;AGnamC;EAAW,iBAAA;CHsa9C;AGramC;EAAW,iBAAA;CHwa9C;AGvamC;EAAW,iBAAA;CH0a9C;AGzamC;EAAW,iBAAA;CH4a9C;AG3amC;EAAW,iBAAA;CH8a9C;AG7amC;EAAW,iBAAA;CHgb9C;AG/amC;EAAW,iBAAA;CHkb9C;AGjbmC;EAAW,iBAAA;CHob9C;AGnbmC;EAAW,iBAAA;CHsb9C;AGrbmC;EAAW,iBAAA;CHwb9C;AGvbmC;EAAW,iBAAA;CH0b9C;AGzbmC;EAAW,iBAAA;CH4b9C;AG3bmC;EAAW,iBAAA;CH8b9C;AG7bmC;EAAW,iBAAA;CHgc9C;AG/bmC;EAAW,iBAAA;CHkc9C;AGjcmC;EAAW,iBAAA;CHoc9C;AGncmC;EAAW,iBAAA;CHsc9C;AGrcmC;EAAW,iBAAA;CHwc9C;AGvcmC;EAAW,iBAAA;CH0c9C;AGzcmC;EAAW,iBAAA;CH4c9C;AG3cmC;EAAW,iBAAA;CH8c9C;AG7cmC;EAAW,iBAAA;CHgd9C;AG/cmC;EAAW,iBAAA;CHkd9C;AGjdmC;EAAW,iBAAA;CHod9C;AGndmC;EAAW,iBAAA;CHsd9C;AGrdmC;EAAW,iBAAA;CHwd9C;AGvdmC;EAAW,iBAAA;CH0d9C;AGzdmC;EAAW,iBAAA;CH4d9C;AG3dmC;EAAW,iBAAA;CH8d9C;AG7dmC;EAAW,iBAAA;CHge9C;AG/dmC;EAAW,iBAAA;CHke9C;AGjemC;EAAW,iBAAA;CHoe9C;AGnemC;EAAW,iBAAA;CHse9C;AGremC;EAAW,iBAAA;CHwe9C;AGvemC;EAAW,iBAAA;CH0e9C;AGzemC;EAAW,iBAAA;CH4e9C;AG3emC;EAAW,iBAAA;CH8e9C;AG7emC;EAAW,iBAAA;CHgf9C;AG/emC;EAAW,iBAAA;CHkf9C;AGjfmC;EAAW,iBAAA;CHof9C;AGnfmC;EAAW,iBAAA;CHsf9C;AGrfmC;EAAW,iBAAA;CHwf9C;AGvfmC;EAAW,iBAAA;CH0f9C;AGzfmC;EAAW,iBAAA;CH4f9C;AG3fmC;EAAW,iBAAA;CH8f9C;AG7fmC;EAAW,iBAAA;CHggB9C;AG/fmC;EAAW,iBAAA;CHkgB9C;AGjgBmC;EAAW,iBAAA;CHogB9C;AGngBmC;EAAW,iBAAA;CHsgB9C;AGrgBmC;EAAW,iBAAA;CHwgB9C;AGvgBmC;EAAW,iBAAA;CH0gB9C;AGzgBmC;EAAW,iBAAA;CH4gB9C;AG3gBmC;EAAW,iBAAA;CH8gB9C;AG7gBmC;EAAW,iBAAA;CHghB9C;AG/gBmC;EAAW,iBAAA;CHkhB9C;AGjhBmC;EAAW,iBAAA;CHohB9C;AGnhBmC;EAAW,iBAAA;CHshB9C;AGrhBmC;EAAW,iBAAA;CHwhB9C;AGvhBmC;EAAW,iBAAA;CH0hB9C;AGzhBmC;EAAW,iBAAA;CH4hB9C;AG3hBmC;EAAW,iBAAA;CH8hB9C;AG7hBmC;EAAW,iBAAA;CHgiB9C;AG/hBmC;EAAW,iBAAA;CHkiB9C;AGjiBmC;EAAW,iBAAA;CHoiB9C;AGniBmC;EAAW,iBAAA;CHsiB9C;AGriBmC;EAAW,iBAAA;CHwiB9C;AGviBmC;EAAW,iBAAA;CH0iB9C;AGziBmC;EAAW,iBAAA;CH4iB9C;AG3iBmC;EAAW,iBAAA;CH8iB9C;AG7iBmC;EAAW,iBAAA;CHgjB9C;AG/iBmC;EAAW,iBAAA;CHkjB9C;AGjjBmC;EAAW,iBAAA;CHojB9C;AGnjBmC;EAAW,iBAAA;CHsjB9C;AGrjBmC;EAAW,iBAAA;CHwjB9C;AGvjBmC;EAAW,iBAAA;CH0jB9C;AGzjBmC;EAAW,iBAAA;CH4jB9C;AG3jBmC;EAAW,iBAAA;CH8jB9C;AG7jBmC;EAAW,iBAAA;CHgkB9C;AG/jBmC;EAAW,iBAAA;CHkkB9C;AGjkBmC;EAAW,iBAAA;CHokB9C;AGnkBmC;EAAW,iBAAA;CHskB9C;AGrkBmC;EAAW,iBAAA;CHwkB9C;AGvkBmC;EAAW,iBAAA;CH0kB9C;AGzkBmC;EAAW,iBAAA;CH4kB9C;AG3kBmC;EAAW,iBAAA;CH8kB9C;AG7kBmC;EAAW,iBAAA;CHglB9C;AG/kBmC;EAAW,iBAAA;CHklB9C;AGjlBmC;EAAW,iBAAA;CHolB9C;AGnlBmC;EAAW,iBAAA;CHslB9C;AGrlBmC;EAAW,iBAAA;CHwlB9C;AGvlBmC;EAAW,iBAAA;CH0lB9C;AGzlBmC;EAAW,iBAAA;CH4lB9C;AG3lBmC;EAAW,iBAAA;CH8lB9C;AG7lBmC;EAAW,iBAAA;CHgmB9C;AG/lBmC;EAAW,iBAAA;CHkmB9C;AGjmBmC;EAAW,iBAAA;CHomB9C;AGnmBmC;EAAW,iBAAA;CHsmB9C;AGrmBmC;EAAW,iBAAA;CHwmB9C;AGvmBmC;EAAW,iBAAA;CH0mB9C;AGzmBmC;EAAW,iBAAA;CH4mB9C;AG3mBmC;EAAW,iBAAA;CH8mB9C;AG7mBmC;EAAW,iBAAA;CHgnB9C;AG/mBmC;EAAW,iBAAA;CHknB9C;AGjnBmC;EAAW,iBAAA;CHonB9C;AGnnBmC;EAAW,iBAAA;CHsnB9C;AGrnBmC;EAAW,iBAAA;CHwnB9C;AGvnBmC;EAAW,iBAAA;CH0nB9C;AGznBmC;EAAW,iBAAA;CH4nB9C;AG3nBmC;EAAW,iBAAA;CH8nB9C;AG7nBmC;EAAW,iBAAA;CHgoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AGvoBmC;EAAW,iBAAA;CH0oB9C;AGzoBmC;EAAW,iBAAA;CH4oB9C;AG3oBmC;EAAW,iBAAA;CH8oB9C;AG7oBmC;EAAW,iBAAA;CHgpB9C;AG/oBmC;EAAW,iBAAA;CHkpB9C;AGjpBmC;EAAW,iBAAA;CHopB9C;AGnpBmC;EAAW,iBAAA;CHspB9C;AGrpBmC;EAAW,iBAAA;CHwpB9C;AGvpBmC;EAAW,iBAAA;CH0pB9C;AGzpBmC;EAAW,iBAAA;CH4pB9C;AG3pBmC;EAAW,iBAAA;CH8pB9C;AG7pBmC;EAAW,iBAAA;CHgqB9C;AG/pBmC;EAAW,iBAAA;CHkqB9C;AGjqBmC;EAAW,iBAAA;CHoqB9C;AGnqBmC;EAAW,iBAAA;CHsqB9C;AGrqBmC;EAAW,iBAAA;CHwqB9C;AGvqBmC;EAAW,iBAAA;CH0qB9C;AGzqBmC;EAAW,iBAAA;CH4qB9C;AG3qBmC;EAAW,iBAAA;CH8qB9C;AG7qBmC;EAAW,iBAAA;CHgrB9C;AG/qBmC;EAAW,iBAAA;CHkrB9C;AGjrBmC;EAAW,iBAAA;CHorB9C;AGnrBmC;EAAW,iBAAA;CHsrB9C;AGrrBmC;EAAW,iBAAA;CHwrB9C;AGvrBmC;EAAW,iBAAA;CH0rB9C;AGzrBmC;EAAW,iBAAA;CH4rB9C;AG3rBmC;EAAW,iBAAA;CH8rB9C;AG7rBmC;EAAW,iBAAA;CHgsB9C;AG/rBmC;EAAW,iBAAA;CHksB9C;AGjsBmC;EAAW,iBAAA;CHosB9C;AGnsBmC;EAAW,iBAAA;CHssB9C;AGrsBmC;EAAW,iBAAA;CHwsB9C;AGvsBmC;EAAW,iBAAA;CH0sB9C;AGzsBmC;EAAW,iBAAA;CH4sB9C;AG3sBmC;EAAW,iBAAA;CH8sB9C;AG7sBmC;EAAW,iBAAA;CHgtB9C;AG/sBmC;EAAW,iBAAA;CHktB9C;AGjtBmC;EAAW,iBAAA;CHotB9C;AGntBmC;EAAW,iBAAA;CHstB9C;AGrtBmC;EAAW,iBAAA;CHwtB9C;AGvtBmC;EAAW,iBAAA;CH0tB9C;AGztBmC;EAAW,iBAAA;CH4tB9C;AG3tBmC;EAAW,iBAAA;CH8tB9C;AG7tBmC;EAAW,iBAAA;CHguB9C;AG/tBmC;EAAW,iBAAA;CHkuB9C;AGjuBmC;EAAW,iBAAA;CHouB9C;AGnuBmC;EAAW,iBAAA;CHsuB9C;AGruBmC;EAAW,iBAAA;CHwuB9C;AGvuBmC;EAAW,iBAAA;CH0uB9C;AGzuBmC;EAAW,iBAAA;CH4uB9C;AG3uBmC;EAAW,iBAAA;CH8uB9C;AG7uBmC;EAAW,iBAAA;CHgvB9C;AIthCD;ECgEE,+BAAA;EACG,4BAAA;EACK,uBAAA;CLy9BT;AIxhCD;;EC6DE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL+9BT;AIthCD;EACE,gBAAA;EACA,8CAAA;CJwhCD;AIrhCD;EACE,4DAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,uBAAA;CJuhCD;AInhCD;;;;EAIE,qBAAA;EACA,mBAAA;EACA,qBAAA;CJqhCD;AI/gCD;EACE,eAAA;EACA,sBAAA;CJihCD;AI/gCC;;EAEE,eAAA;EACA,2BAAA;CJihCH;AI9gCC;EEnDA,2CAAA;EACA,qBAAA;CNokCD;AIvgCD;EACE,UAAA;CJygCD;AIngCD;EACE,uBAAA;CJqgCD;AIjgCD;;;;;EGvEE,eAAA;EACA,gBAAA;EACA,aAAA;CP+kCD;AIrgCD;EACE,mBAAA;CJugCD;AIjgCD;EACE,aAAA;EACA,wBAAA;EACA,uBAAA;EACA,uBAAA;EACA,mBAAA;EC6FA,yCAAA;EACK,oCAAA;EACG,iCAAA;EEvLR,sBAAA;EACA,gBAAA;EACA,aAAA;CP+lCD;AIjgCD;EACE,mBAAA;CJmgCD;AI7/BD;EACE,iBAAA;EACA,oBAAA;EACA,UAAA;EACA,8BAAA;CJ+/BD;AIv/BD;EACE,mBAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,WAAA;EACA,iBAAA;EACA,uBAAA;EACA,UAAA;CJy/BD;AIj/BC;;EAEE,iBAAA;EACA,YAAA;EACA,aAAA;EACA,UAAA;EACA,kBAAA;EACA,WAAA;CJm/BH;AIx+BD;EACE,gBAAA;CJ0+BD;AQjoCD;;;;;;;;;;;;EAEE,qBAAA;EACA,iBAAA;EACA,iBAAA;EACA,eAAA;CR6oCD;AQlpCD;;;;;;;;;;;;;;;;;;;;;;;;EASI,oBAAA;EACA,eAAA;EACA,eAAA;CRmqCH;AQ/pCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRoqCD;AQxqCD;;;;;;;;;;;;EAQI,eAAA;CR8qCH;AQ3qCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRgrCD;AQprCD;;;;;;;;;;;;EAQI,eAAA;CR0rCH;AQtrCD;;EAAU,gBAAA;CR0rCT;AQzrCD;;EAAU,gBAAA;CR6rCT;AQ5rCD;;EAAU,gBAAA;CRgsCT;AQ/rCD;;EAAU,gBAAA;CRmsCT;AQlsCD;;EAAU,gBAAA;CRssCT;AQrsCD;;EAAU,gBAAA;CRysCT;AQnsCD;EACE,iBAAA;CRqsCD;AQlsCD;EACE,oBAAA;EACA,gBAAA;EACA,iBAAA;EACA,iBAAA;CRosCD;AQ/rCD;EAwOA;IA1OI,gBAAA;GRqsCD;CACF;AQ7rCD;;EAEE,eAAA;CR+rCD;AQ5rCD;;EAEE,0BAAA;EACA,cAAA;CR8rCD;AQ1rCD;EAAuB,iBAAA;CR6rCtB;AQ5rCD;EAAuB,kBAAA;CR+rCtB;AQ9rCD;EAAuB,mBAAA;CRisCtB;AQhsCD;EAAuB,oBAAA;CRmsCtB;AQlsCD;EAAuB,oBAAA;CRqsCtB;AQlsCD;EAAuB,0BAAA;CRqsCtB;AQpsCD;EAAuB,0BAAA;CRusCtB;AQtsCD;EAAuB,2BAAA;CRysCtB;AQtsCD;EACE,eAAA;CRwsCD;AQtsCD;ECrGE,eAAA;CT8yCD;AS7yCC;;EAEE,eAAA;CT+yCH;AQ1sCD;ECxGE,eAAA;CTqzCD;ASpzCC;;EAEE,eAAA;CTszCH;AQ9sCD;EC3GE,eAAA;CT4zCD;AS3zCC;;EAEE,eAAA;CT6zCH;AQltCD;EC9GE,eAAA;CTm0CD;ASl0CC;;EAEE,eAAA;CTo0CH;AQttCD;ECjHE,eAAA;CT00CD;ASz0CC;;EAEE,eAAA;CT20CH;AQttCD;EAGE,YAAA;EE3HA,0BAAA;CVk1CD;AUj1CC;;EAEE,0BAAA;CVm1CH;AQxtCD;EE9HE,0BAAA;CVy1CD;AUx1CC;;EAEE,0BAAA;CV01CH;AQ5tCD;EEjIE,0BAAA;CVg2CD;AU/1CC;;EAEE,0BAAA;CVi2CH;AQhuCD;EEpIE,0BAAA;CVu2CD;AUt2CC;;EAEE,0BAAA;CVw2CH;AQpuCD;EEvIE,0BAAA;CV82CD;AU72CC;;EAEE,0BAAA;CV+2CH;AQnuCD;EACE,oBAAA;EACA,oBAAA;EACA,iCAAA;CRquCD;AQ7tCD;;EAEE,cAAA;EACA,oBAAA;CR+tCD;AQluCD;;;;EAMI,iBAAA;CRkuCH;AQ3tCD;EACE,gBAAA;EACA,iBAAA;CR6tCD;AQztCD;EALE,gBAAA;EACA,iBAAA;EAMA,kBAAA;CR4tCD;AQ9tCD;EAKI,sBAAA;EACA,kBAAA;EACA,mBAAA;CR4tCH;AQvtCD;EACE,cAAA;EACA,oBAAA;CRytCD;AQvtCD;;EAEE,wBAAA;CRytCD;AQvtCD;EACE,kBAAA;CRytCD;AQvtCD;EACE,eAAA;CRytCD;AQhsCD;EA6EA;IAvFM,YAAA;IACA,aAAA;IACA,YAAA;IACA,kBAAA;IGtNJ,iBAAA;IACA,wBAAA;IACA,oBAAA;GXq6CC;EQ7nCH;IAhFM,mBAAA;GRgtCH;CACF;AQvsCD;;EAGE,aAAA;EACA,kCAAA;CRwsCD;AQtsCD;EACE,eAAA;EA9IqB,0BAAA;CRu1CtB;AQpsCD;EACE,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,+BAAA;CRssCD;AQjsCG;;;EACE,iBAAA;CRqsCL;AQ/sCD;;;EAmBI,eAAA;EACA,eAAA;EACA,wBAAA;EACA,eAAA;CRisCH;AQ/rCG;;;EACE,uBAAA;CRmsCL;AQ3rCD;;EAEE,oBAAA;EACA,gBAAA;EACA,gCAAA;EACA,eAAA;EACA,kBAAA;CR6rCD;AQvrCG;;;;;;EAAW,YAAA;CR+rCd;AQ9rCG;;;;;;EACE,uBAAA;CRqsCL;AQ/rCD;EACE,oBAAA;EACA,mBAAA;EACA,wBAAA;CRisCD;AYv+CD;;;;EAIE,+DAAA;CZy+CD;AYr+CD;EACE,iBAAA;EACA,eAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;CZu+CD;AYn+CD;EACE,iBAAA;EACA,eAAA;EACA,YAAA;EACA,uBAAA;EACA,mBAAA;EACA,uDAAA;UAAA,+CAAA;CZq+CD;AY3+CD;EASI,WAAA;EACA,gBAAA;EACA,kBAAA;EACA,yBAAA;UAAA,iBAAA;CZq+CH;AYh+CD;EACE,eAAA;EACA,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,sBAAA;EACA,sBAAA;EACA,eAAA;EACA,0BAAA;EACA,uBAAA;EACA,mBAAA;CZk+CD;AY7+CD;EAeI,WAAA;EACA,mBAAA;EACA,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,iBAAA;CZi+CH;AY59CD;EACE,kBAAA;EACA,mBAAA;CZ89CD;AaxhDD;ECHE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;Cd8hDD;AaxhDC;EAqEF;IAvEI,aAAA;Gb8hDD;CACF;Aa1hDC;EAkEF;IApEI,aAAA;GbgiDD;CACF;Aa5hDD;EA+DA;IAjEI,cAAA;GbkiDD;CACF;AazhDD;ECvBE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;CdmjDD;AathDD;ECvBE,mBAAA;EACA,oBAAA;CdgjDD;AehjDG;EACE,mBAAA;EAEA,gBAAA;EAEA,mBAAA;EACA,oBAAA;CfgjDL;AehiDG;EACE,YAAA;CfkiDL;Ae3hDC;EACE,YAAA;Cf6hDH;Ae9hDC;EACE,oBAAA;CfgiDH;AejiDC;EACE,oBAAA;CfmiDH;AepiDC;EACE,WAAA;CfsiDH;AeviDC;EACE,oBAAA;CfyiDH;Ae1iDC;EACE,oBAAA;Cf4iDH;Ae7iDC;EACE,WAAA;Cf+iDH;AehjDC;EACE,oBAAA;CfkjDH;AenjDC;EACE,oBAAA;CfqjDH;AetjDC;EACE,WAAA;CfwjDH;AezjDC;EACE,oBAAA;Cf2jDH;Ae5jDC;EACE,mBAAA;Cf8jDH;AehjDC;EACE,YAAA;CfkjDH;AenjDC;EACE,oBAAA;CfqjDH;AetjDC;EACE,oBAAA;CfwjDH;AezjDC;EACE,WAAA;Cf2jDH;Ae5jDC;EACE,oBAAA;Cf8jDH;Ae/jDC;EACE,oBAAA;CfikDH;AelkDC;EACE,WAAA;CfokDH;AerkDC;EACE,oBAAA;CfukDH;AexkDC;EACE,oBAAA;Cf0kDH;Ae3kDC;EACE,WAAA;Cf6kDH;Ae9kDC;EACE,oBAAA;CfglDH;AejlDC;EACE,mBAAA;CfmlDH;Ae/kDC;EACE,YAAA;CfilDH;AejmDC;EACE,WAAA;CfmmDH;AepmDC;EACE,mBAAA;CfsmDH;AevmDC;EACE,mBAAA;CfymDH;Ae1mDC;EACE,UAAA;Cf4mDH;Ae7mDC;EACE,mBAAA;Cf+mDH;AehnDC;EACE,mBAAA;CfknDH;AennDC;EACE,UAAA;CfqnDH;AetnDC;EACE,mBAAA;CfwnDH;AeznDC;EACE,mBAAA;Cf2nDH;Ae5nDC;EACE,UAAA;Cf8nDH;Ae/nDC;EACE,mBAAA;CfioDH;AeloDC;EACE,kBAAA;CfooDH;AehoDC;EACE,WAAA;CfkoDH;AepnDC;EACE,kBAAA;CfsnDH;AevnDC;EACE,0BAAA;CfynDH;Ae1nDC;EACE,0BAAA;Cf4nDH;Ae7nDC;EACE,iBAAA;Cf+nDH;AehoDC;EACE,0BAAA;CfkoDH;AenoDC;EACE,0BAAA;CfqoDH;AetoDC;EACE,iBAAA;CfwoDH;AezoDC;EACE,0BAAA;Cf2oDH;Ae5oDC;EACE,0BAAA;Cf8oDH;Ae/oDC;EACE,iBAAA;CfipDH;AelpDC;EACE,0BAAA;CfopDH;AerpDC;EACE,yBAAA;CfupDH;AexpDC;EACE,gBAAA;Cf0pDH;Aa1pDD;EElCI;IACE,YAAA;Gf+rDH;EexrDD;IACE,YAAA;Gf0rDD;Ee3rDD;IACE,oBAAA;Gf6rDD;Ee9rDD;IACE,oBAAA;GfgsDD;EejsDD;IACE,WAAA;GfmsDD;EepsDD;IACE,oBAAA;GfssDD;EevsDD;IACE,oBAAA;GfysDD;Ee1sDD;IACE,WAAA;Gf4sDD;Ee7sDD;IACE,oBAAA;Gf+sDD;EehtDD;IACE,oBAAA;GfktDD;EentDD;IACE,WAAA;GfqtDD;EettDD;IACE,oBAAA;GfwtDD;EeztDD;IACE,mBAAA;Gf2tDD;Ee7sDD;IACE,YAAA;Gf+sDD;EehtDD;IACE,oBAAA;GfktDD;EentDD;IACE,oBAAA;GfqtDD;EettDD;IACE,WAAA;GfwtDD;EeztDD;IACE,oBAAA;Gf2tDD;Ee5tDD;IACE,oBAAA;Gf8tDD;Ee/tDD;IACE,WAAA;GfiuDD;EeluDD;IACE,oBAAA;GfouDD;EeruDD;IACE,oBAAA;GfuuDD;EexuDD;IACE,WAAA;Gf0uDD;Ee3uDD;IACE,oBAAA;Gf6uDD;Ee9uDD;IACE,mBAAA;GfgvDD;Ee5uDD;IACE,YAAA;Gf8uDD;Ee9vDD;IACE,WAAA;GfgwDD;EejwDD;IACE,mBAAA;GfmwDD;EepwDD;IACE,mBAAA;GfswDD;EevwDD;IACE,UAAA;GfywDD;Ee1wDD;IACE,mBAAA;Gf4wDD;Ee7wDD;IACE,mBAAA;Gf+wDD;EehxDD;IACE,UAAA;GfkxDD;EenxDD;IACE,mBAAA;GfqxDD;EetxDD;IACE,mBAAA;GfwxDD;EezxDD;IACE,UAAA;Gf2xDD;Ee5xDD;IACE,mBAAA;Gf8xDD;Ee/xDD;IACE,kBAAA;GfiyDD;Ee7xDD;IACE,WAAA;Gf+xDD;EejxDD;IACE,kBAAA;GfmxDD;EepxDD;IACE,0BAAA;GfsxDD;EevxDD;IACE,0BAAA;GfyxDD;Ee1xDD;IACE,iBAAA;Gf4xDD;Ee7xDD;IACE,0BAAA;Gf+xDD;EehyDD;IACE,0BAAA;GfkyDD;EenyDD;IACE,iBAAA;GfqyDD;EetyDD;IACE,0BAAA;GfwyDD;EezyDD;IACE,0BAAA;Gf2yDD;Ee5yDD;IACE,iBAAA;Gf8yDD;Ee/yDD;IACE,0BAAA;GfizDD;EelzDD;IACE,yBAAA;GfozDD;EerzDD;IACE,gBAAA;GfuzDD;CACF;Aa/yDD;EE3CI;IACE,YAAA;Gf61DH;Eet1DD;IACE,YAAA;Gfw1DD;Eez1DD;IACE,oBAAA;Gf21DD;Ee51DD;IACE,oBAAA;Gf81DD;Ee/1DD;IACE,WAAA;Gfi2DD;Eel2DD;IACE,oBAAA;Gfo2DD;Eer2DD;IACE,oBAAA;Gfu2DD;Eex2DD;IACE,WAAA;Gf02DD;Ee32DD;IACE,oBAAA;Gf62DD;Ee92DD;IACE,oBAAA;Gfg3DD;Eej3DD;IACE,WAAA;Gfm3DD;Eep3DD;IACE,oBAAA;Gfs3DD;Eev3DD;IACE,mBAAA;Gfy3DD;Ee32DD;IACE,YAAA;Gf62DD;Ee92DD;IACE,oBAAA;Gfg3DD;Eej3DD;IACE,oBAAA;Gfm3DD;Eep3DD;IACE,WAAA;Gfs3DD;Eev3DD;IACE,oBAAA;Gfy3DD;Ee13DD;IACE,oBAAA;Gf43DD;Ee73DD;IACE,WAAA;Gf+3DD;Eeh4DD;IACE,oBAAA;Gfk4DD;Een4DD;IACE,oBAAA;Gfq4DD;Eet4DD;IACE,WAAA;Gfw4DD;Eez4DD;IACE,oBAAA;Gf24DD;Ee54DD;IACE,mBAAA;Gf84DD;Ee14DD;IACE,YAAA;Gf44DD;Ee55DD;IACE,WAAA;Gf85DD;Ee/5DD;IACE,mBAAA;Gfi6DD;Eel6DD;IACE,mBAAA;Gfo6DD;Eer6DD;IACE,UAAA;Gfu6DD;Eex6DD;IACE,mBAAA;Gf06DD;Ee36DD;IACE,mBAAA;Gf66DD;Ee96DD;IACE,UAAA;Gfg7DD;Eej7DD;IACE,mBAAA;Gfm7DD;Eep7DD;IACE,mBAAA;Gfs7DD;Eev7DD;IACE,UAAA;Gfy7DD;Ee17DD;IACE,mBAAA;Gf47DD;Ee77DD;IACE,kBAAA;Gf+7DD;Ee37DD;IACE,WAAA;Gf67DD;Ee/6DD;IACE,kBAAA;Gfi7DD;Eel7DD;IACE,0BAAA;Gfo7DD;Eer7DD;IACE,0BAAA;Gfu7DD;Eex7DD;IACE,iBAAA;Gf07DD;Ee37DD;IACE,0BAAA;Gf67DD;Ee97DD;IACE,0BAAA;Gfg8DD;Eej8DD;IACE,iBAAA;Gfm8DD;Eep8DD;IACE,0BAAA;Gfs8DD;Eev8DD;IACE,0BAAA;Gfy8DD;Ee18DD;IACE,iBAAA;Gf48DD;Ee78DD;IACE,0BAAA;Gf+8DD;Eeh9DD;IACE,yBAAA;Gfk9DD;Een9DD;IACE,gBAAA;Gfq9DD;CACF;Aa18DD;EE9CI;IACE,YAAA;Gf2/DH;Eep/DD;IACE,YAAA;Gfs/DD;Eev/DD;IACE,oBAAA;Gfy/DD;Ee1/DD;IACE,oBAAA;Gf4/DD;Ee7/DD;IACE,WAAA;Gf+/DD;EehgED;IACE,oBAAA;GfkgED;EengED;IACE,oBAAA;GfqgED;EetgED;IACE,WAAA;GfwgED;EezgED;IACE,oBAAA;Gf2gED;Ee5gED;IACE,oBAAA;Gf8gED;Ee/gED;IACE,WAAA;GfihED;EelhED;IACE,oBAAA;GfohED;EerhED;IACE,mBAAA;GfuhED;EezgED;IACE,YAAA;Gf2gED;Ee5gED;IACE,oBAAA;Gf8gED;Ee/gED;IACE,oBAAA;GfihED;EelhED;IACE,WAAA;GfohED;EerhED;IACE,oBAAA;GfuhED;EexhED;IACE,oBAAA;Gf0hED;Ee3hED;IACE,WAAA;Gf6hED;Ee9hED;IACE,oBAAA;GfgiED;EejiED;IACE,oBAAA;GfmiED;EepiED;IACE,WAAA;GfsiED;EeviED;IACE,oBAAA;GfyiED;Ee1iED;IACE,mBAAA;Gf4iED;EexiED;IACE,YAAA;Gf0iED;Ee1jED;IACE,WAAA;Gf4jED;Ee7jED;IACE,mBAAA;Gf+jED;EehkED;IACE,mBAAA;GfkkED;EenkED;IACE,UAAA;GfqkED;EetkED;IACE,mBAAA;GfwkED;EezkED;IACE,mBAAA;Gf2kED;Ee5kED;IACE,UAAA;Gf8kED;Ee/kED;IACE,mBAAA;GfilED;EellED;IACE,mBAAA;GfolED;EerlED;IACE,UAAA;GfulED;EexlED;IACE,mBAAA;Gf0lED;Ee3lED;IACE,kBAAA;Gf6lED;EezlED;IACE,WAAA;Gf2lED;Ee7kED;IACE,kBAAA;Gf+kED;EehlED;IACE,0BAAA;GfklED;EenlED;IACE,0BAAA;GfqlED;EetlED;IACE,iBAAA;GfwlED;EezlED;IACE,0BAAA;Gf2lED;Ee5lED;IACE,0BAAA;Gf8lED;Ee/lED;IACE,iBAAA;GfimED;EelmED;IACE,0BAAA;GfomED;EermED;IACE,0BAAA;GfumED;EexmED;IACE,iBAAA;Gf0mED;Ee3mED;IACE,0BAAA;Gf6mED;Ee9mED;IACE,yBAAA;GfgnED;EejnED;IACE,gBAAA;GfmnED;CACF;AgBvrED;EACE,8BAAA;ChByrED;AgBvrED;EACE,iBAAA;EACA,oBAAA;EACA,eAAA;EACA,iBAAA;ChByrED;AgBvrED;EACE,iBAAA;ChByrED;AgBnrED;EACE,YAAA;EACA,gBAAA;EACA,oBAAA;ChBqrED;AgBxrED;;;;;;EAWQ,aAAA;EACA,wBAAA;EACA,oBAAA;EACA,2BAAA;ChBqrEP;AgBnsED;EAoBI,uBAAA;EACA,8BAAA;ChBkrEH;AgBvsED;;;;;;EA8BQ,cAAA;ChBirEP;AgB/sED;EAoCI,2BAAA;ChB8qEH;AgBltED;EAyCI,uBAAA;ChB4qEH;AgBrqED;;;;;;EAOQ,aAAA;ChBsqEP;AgB3pED;EACE,uBAAA;ChB6pED;AgB9pED;;;;;;EAQQ,uBAAA;ChB8pEP;AgBtqED;;EAeM,yBAAA;ChB2pEL;AgBjpED;EAEI,0BAAA;ChBkpEH;AgBzoED;EAEI,0BAAA;ChB0oEH;AgBjoED;EACE,iBAAA;EACA,YAAA;EACA,sBAAA;ChBmoED;AgB9nEG;;EACE,iBAAA;EACA,YAAA;EACA,oBAAA;ChBioEL;AiB7wEC;;;;;;;;;;;;EAOI,0BAAA;CjBoxEL;AiB9wEC;;;;;EAMI,0BAAA;CjB+wEL;AiBlyEC;;;;;;;;;;;;EAOI,0BAAA;CjByyEL;AiBnyEC;;;;;EAMI,0BAAA;CjBoyEL;AiBvzEC;;;;;;;;;;;;EAOI,0BAAA;CjB8zEL;AiBxzEC;;;;;EAMI,0BAAA;CjByzEL;AiB50EC;;;;;;;;;;;;EAOI,0BAAA;CjBm1EL;AiB70EC;;;;;EAMI,0BAAA;CjB80EL;AiBj2EC;;;;;;;;;;;;EAOI,0BAAA;CjBw2EL;AiBl2EC;;;;;EAMI,0BAAA;CjBm2EL;AgBjtED;EACE,iBAAA;EACA,kBAAA;ChBmtED;AgBtpED;EACA;IA3DI,YAAA;IACA,oBAAA;IACA,mBAAA;IACA,6CAAA;IACA,uBAAA;GhBotED;EgB7pEH;IAnDM,iBAAA;GhBmtEH;EgBhqEH;;;;;;IA1CY,oBAAA;GhBktET;EgBxqEH;IAlCM,UAAA;GhB6sEH;EgB3qEH;;;;;;IAzBY,eAAA;GhB4sET;EgBnrEH;;;;;;IArBY,gBAAA;GhBgtET;EgB3rEH;;;;IARY,iBAAA;GhBysET;CACF;AkBn6ED;EACE,WAAA;EACA,UAAA;EACA,UAAA;EAIA,aAAA;ClBk6ED;AkB/5ED;EACE,eAAA;EACA,YAAA;EACA,WAAA;EACA,oBAAA;EACA,gBAAA;EACA,qBAAA;EACA,eAAA;EACA,UAAA;EACA,iCAAA;ClBi6ED;AkB95ED;EACE,sBAAA;EACA,gBAAA;EACA,mBAAA;EACA,kBAAA;ClBg6ED;AkBr5ED;Eb4BE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL43ET;AkBr5ED;;EAEE,gBAAA;EACA,mBAAA;EACA,oBAAA;ClBu5ED;AkBp5ED;EACE,eAAA;ClBs5ED;AkBl5ED;EACE,eAAA;EACA,YAAA;ClBo5ED;AkBh5ED;;EAEE,aAAA;ClBk5ED;AkB94ED;;;EZrEE,2CAAA;EACA,qBAAA;CNw9ED;AkB74ED;EACE,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;ClB+4ED;AkBr3ED;EACE,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,uBAAA;EACA,uBAAA;EACA,uBAAA;EACA,mBAAA;EbxDA,yDAAA;EACQ,iDAAA;EAyHR,uFAAA;EACK,0EAAA;EACG,uEAAA;CLwzET;AmBh8EC;EACE,sBAAA;EACA,WAAA;EdUF,uFAAA;EACQ,+EAAA;CLy7ET;AKx5EC;EACE,YAAA;EACA,WAAA;CL05EH;AKx5EC;EAA0B,YAAA;CL25E3B;AK15EC;EAAgC,YAAA;CL65EjC;AkBj4EC;EACE,UAAA;EACA,8BAAA;ClBm4EH;AkB33EC;;;EAGE,0BAAA;EACA,WAAA;ClB63EH;AkB13EC;;EAEE,oBAAA;ClB43EH;AkBx3EC;EACE,aAAA;ClB03EH;AkB92ED;EACE,yBAAA;ClBg3ED;AkBx0ED;EAtBI;;;;IACE,kBAAA;GlBo2EH;EkBj2EC;;;;;;;;IAEE,kBAAA;GlBy2EH;EkBt2EC;;;;;;;;IAEE,kBAAA;GlB82EH;CACF;AkBp2ED;EACE,oBAAA;ClBs2ED;AkB91ED;;EAEE,mBAAA;EACA,eAAA;EACA,iBAAA;EACA,oBAAA;ClBg2ED;AkBr2ED;;EAQI,iBAAA;EACA,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,gBAAA;ClBi2EH;AkB91ED;;;;EAIE,mBAAA;EACA,mBAAA;EACA,mBAAA;ClBg2ED;AkB71ED;;EAEE,iBAAA;ClB+1ED;AkB31ED;;EAEE,mBAAA;EACA,sBAAA;EACA,mBAAA;EACA,iBAAA;EACA,uBAAA;EACA,oBAAA;EACA,gBAAA;ClB61ED;AkB31ED;;EAEE,cAAA;EACA,kBAAA;ClB61ED;AkBp1EC;;;;;;EAGE,oBAAA;ClBy1EH;AkBn1EC;;;;EAEE,oBAAA;ClBu1EH;AkBj1EC;;;;EAGI,oBAAA;ClBo1EL;AkBz0ED;EAEE,iBAAA;EACA,oBAAA;EAEA,iBAAA;EACA,iBAAA;ClBy0ED;AkBv0EC;;EAEE,gBAAA;EACA,iBAAA;ClBy0EH;AkB5zED;ECnQE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnBkkFD;AmBhkFC;EACE,aAAA;EACA,kBAAA;CnBkkFH;AmB/jFC;;EAEE,aAAA;CnBikFH;AkBx0ED;EAEI,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;ClBy0EH;AkB/0ED;EASI,aAAA;EACA,kBAAA;ClBy0EH;AkBn1ED;;EAcI,aAAA;ClBy0EH;AkBv1ED;EAiBI,aAAA;EACA,iBAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;ClBy0EH;AkBr0ED;EC/RE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBumFD;AmBrmFC;EACE,aAAA;EACA,kBAAA;CnBumFH;AmBpmFC;;EAEE,aAAA;CnBsmFH;AkBj1ED;EAEI,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;ClBk1EH;AkBx1ED;EASI,aAAA;EACA,kBAAA;ClBk1EH;AkB51ED;;EAcI,aAAA;ClBk1EH;AkBh2ED;EAiBI,aAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;ClBk1EH;AkBz0ED;EAEE,mBAAA;ClB00ED;AkB50ED;EAMI,sBAAA;ClBy0EH;AkBr0ED;EACE,mBAAA;EACA,OAAA;EACA,SAAA;EACA,WAAA;EACA,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,mBAAA;EACA,qBAAA;ClBu0ED;AkBr0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClBu0ED;AkBr0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClBu0ED;AkBn0ED;;;;;;;;;;EC1ZI,eAAA;CnByuFH;AkB/0ED;ECtZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CL0rFT;AmBxuFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL+rFT;AkBz1ED;EC5YI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBwuFH;AkB91ED;ECtYI,eAAA;CnBuuFH;AkB91ED;;;;;;;;;;EC7ZI,eAAA;CnBuwFH;AkB12ED;ECzZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLwtFT;AmBtwFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL6tFT;AkBp3ED;EC/YI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBswFH;AkBz3ED;ECzYI,eAAA;CnBqwFH;AkBz3ED;;;;;;;;;;EChaI,eAAA;CnBqyFH;AkBr4ED;EC5ZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLsvFT;AmBpyFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL2vFT;AkB/4ED;EClZI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBoyFH;AkBp5ED;EC5YI,eAAA;CnBmyFH;AkBh5EC;EACE,UAAA;ClBk5EH;AkBh5EC;EACE,OAAA;ClBk5EH;AkBx4ED;EACE,eAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;ClB04ED;AkBvzED;EAwEA;IAtIM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlBy3EH;EkBrvEH;IA/HM,sBAAA;IACA,YAAA;IACA,uBAAA;GlBu3EH;EkB1vEH;IAxHM,sBAAA;GlBq3EH;EkB7vEH;IApHM,sBAAA;IACA,uBAAA;GlBo3EH;EkBjwEH;;;IA9GQ,YAAA;GlBo3EL;EkBtwEH;IAxGM,YAAA;GlBi3EH;EkBzwEH;IApGM,iBAAA;IACA,uBAAA;GlBg3EH;EkB7wEH;;IA5FM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlB62EH;EkBpxEH;;IAtFQ,gBAAA;GlB82EL;EkBxxEH;;IAjFM,mBAAA;IACA,eAAA;GlB62EH;EkB7xEH;IA3EM,OAAA;GlB22EH;CACF;AkBj2ED;;;;EASI,cAAA;EACA,iBAAA;EACA,iBAAA;ClB81EH;AkBz2ED;;EAiBI,iBAAA;ClB41EH;AkB72ED;EJthBE,mBAAA;EACA,oBAAA;Cds4FD;AkB10EC;EAyBF;IAnCM,kBAAA;IACA,iBAAA;IACA,iBAAA;GlBw1EH;CACF;AkBx3ED;EAwCI,YAAA;ClBm1EH;AkBr0EC;EAUF;IAdQ,kBAAA;IACA,gBAAA;GlB60EL;CACF;AkBn0EC;EAEF;IANQ,iBAAA;IACA,gBAAA;GlB20EL;CACF;AoBp6FD;EACE,sBAAA;EACA,iBAAA;EACA,oBAAA;EACA,mBAAA;EACA,uBAAA;EACA,+BAAA;MAAA,2BAAA;EACA,gBAAA;EACA,uBAAA;EACA,8BAAA;EACA,oBAAA;EC0CA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,mBAAA;EhB+JA,0BAAA;EACG,uBAAA;EACC,sBAAA;EACI,kBAAA;CL+tFT;AoBv6FG;;;;;;EdnBF,2CAAA;EACA,qBAAA;CNk8FD;AoB16FC;;;EAGE,YAAA;EACA,sBAAA;CpB46FH;AoBz6FC;;EAEE,WAAA;EACA,uBAAA;Ef2BF,yDAAA;EACQ,iDAAA;CLi5FT;AoBz6FC;;;EAGE,oBAAA;EE7CF,cAAA;EAGA,0BAAA;EjB8DA,yBAAA;EACQ,iBAAA;CL05FT;AoBz6FG;;EAEE,qBAAA;CpB26FL;AoBl6FD;EC3DE,YAAA;EACA,uBAAA;EACA,mBAAA;CrBg+FD;AqB99FC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBg+FP;AqB99FC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBg+FP;AqB99FC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBg+FP;AqB99FG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBs+FT;AqBn+FC;;;EAGE,uBAAA;CrBq+FH;AqBh+FG;;;;;;;;;EAGE,uBAAA;EACI,mBAAA;CrBw+FT;AoBv9FD;ECZI,YAAA;EACA,uBAAA;CrBs+FH;AoBx9FD;EC9DE,YAAA;EACA,0BAAA;EACA,sBAAA;CrByhGD;AqBvhGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrByhGP;AqBvhGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrByhGP;AqBvhGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrByhGP;AqBvhGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB+hGT;AqB5hGC;;;EAGE,uBAAA;CrB8hGH;AqBzhGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrBiiGT;AoB7gGD;ECfI,eAAA;EACA,uBAAA;CrB+hGH;AoB7gGD;EClEE,YAAA;EACA,0BAAA;EACA,sBAAA;CrBklGD;AqBhlGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBklGP;AqBhlGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBklGP;AqBhlGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBklGP;AqBhlGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBwlGT;AqBrlGC;;;EAGE,uBAAA;CrBulGH;AqBllGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrB0lGT;AoBlkGD;ECnBI,eAAA;EACA,uBAAA;CrBwlGH;AoBlkGD;ECtEE,YAAA;EACA,0BAAA;EACA,sBAAA;CrB2oGD;AqBzoGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB2oGP;AqBzoGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB2oGP;AqBzoGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB2oGP;AqBzoGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBipGT;AqB9oGC;;;EAGE,uBAAA;CrBgpGH;AqB3oGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrBmpGT;AoBvnGD;ECvBI,eAAA;EACA,uBAAA;CrBipGH;AoBvnGD;EC1EE,YAAA;EACA,0BAAA;EACA,sBAAA;CrBosGD;AqBlsGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBosGP;AqBlsGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBosGP;AqBlsGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBosGP;AqBlsGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB0sGT;AqBvsGC;;;EAGE,uBAAA;CrBysGH;AqBpsGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrB4sGT;AoB5qGD;EC3BI,eAAA;EACA,uBAAA;CrB0sGH;AoB5qGD;EC9EE,YAAA;EACA,0BAAA;EACA,sBAAA;CrB6vGD;AqB3vGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB6vGP;AqB3vGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB6vGP;AqB3vGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB6vGP;AqB3vGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBmwGT;AqBhwGC;;;EAGE,uBAAA;CrBkwGH;AqB7vGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrBqwGT;AoBjuGD;EC/BI,eAAA;EACA,uBAAA;CrBmwGH;AoB5tGD;EACE,eAAA;EACA,oBAAA;EACA,iBAAA;CpB8tGD;AoB5tGC;;;;;EAKE,8BAAA;EfnCF,yBAAA;EACQ,iBAAA;CLkwGT;AoB7tGC;;;;EAIE,0BAAA;CpB+tGH;AoB7tGC;;EAEE,eAAA;EACA,2BAAA;EACA,8BAAA;CpB+tGH;AoB3tGG;;;;EAEE,eAAA;EACA,sBAAA;CpB+tGL;AoBttGD;;ECxEE,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CrBkyGD;AoBztGD;;EC5EE,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrByyGD;AoB5tGD;;EChFE,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrBgzGD;AoB3tGD;EACE,eAAA;EACA,YAAA;CpB6tGD;AoBztGD;EACE,gBAAA;CpB2tGD;AoBptGC;;;EACE,YAAA;CpBwtGH;AuBl3GD;EACE,WAAA;ElBoLA,yCAAA;EACK,oCAAA;EACG,iCAAA;CLisGT;AuBr3GC;EACE,WAAA;CvBu3GH;AuBn3GD;EACE,cAAA;CvBq3GD;AuBn3GC;EAAY,eAAA;CvBs3Gb;AuBr3GC;EAAY,mBAAA;CvBw3Gb;AuBv3GC;EAAY,yBAAA;CvB03Gb;AuBv3GD;EACE,mBAAA;EACA,UAAA;EACA,iBAAA;ElBuKA,gDAAA;EACQ,2CAAA;KAAA,wCAAA;EAOR,mCAAA;EACQ,8BAAA;KAAA,2BAAA;EAGR,yCAAA;EACQ,oCAAA;KAAA,iCAAA;CL2sGT;AwBr5GD;EACE,sBAAA;EACA,SAAA;EACA,UAAA;EACA,iBAAA;EACA,uBAAA;EACA,uBAAA;EACA,yBAAA;EACA,oCAAA;EACA,mCAAA;CxBu5GD;AwBn5GD;;EAEE,mBAAA;CxBq5GD;AwBj5GD;EACE,WAAA;CxBm5GD;AwB/4GD;EACE,mBAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,YAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,uBAAA;EACA,uBAAA;EACA,sCAAA;EACA,mBAAA;EnBsBA,oDAAA;EACQ,4CAAA;EmBrBR,qCAAA;UAAA,6BAAA;CxBk5GD;AwB74GC;EACE,SAAA;EACA,WAAA;CxB+4GH;AwBx6GD;ECzBE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBo8GD;AwB96GD;EAmCI,eAAA;EACA,kBAAA;EACA,YAAA;EACA,oBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxB84GH;AwBx4GC;;EAEE,sBAAA;EACA,eAAA;EACA,0BAAA;CxB04GH;AwBp4GC;;;EAGE,YAAA;EACA,sBAAA;EACA,WAAA;EACA,0BAAA;CxBs4GH;AwB73GC;;;EAGE,eAAA;CxB+3GH;AwB33GC;;EAEE,sBAAA;EACA,8BAAA;EACA,uBAAA;EE3GF,oEAAA;EF6GE,oBAAA;CxB63GH;AwBx3GD;EAGI,eAAA;CxBw3GH;AwB33GD;EAQI,WAAA;CxBs3GH;AwB92GD;EACE,WAAA;EACA,SAAA;CxBg3GD;AwBx2GD;EACE,QAAA;EACA,YAAA;CxB02GD;AwBt2GD;EACE,eAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxBw2GD;AwBp2GD;EACE,gBAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;EACA,OAAA;EACA,aAAA;CxBs2GD;AwBl2GD;EACE,SAAA;EACA,WAAA;CxBo2GD;AwB51GD;;EAII,cAAA;EACA,0BAAA;EACA,4BAAA;EACA,YAAA;CxB41GH;AwBn2GD;;EAWI,UAAA;EACA,aAAA;EACA,mBAAA;CxB41GH;AwBv0GD;EAXE;IApEA,WAAA;IACA,SAAA;GxB05GC;EwBv1GD;IA1DA,QAAA;IACA,YAAA;GxBo5GC;CACF;A2BpiHD;;EAEE,mBAAA;EACA,sBAAA;EACA,uBAAA;C3BsiHD;A2B1iHD;;EAMI,mBAAA;EACA,YAAA;C3BwiHH;A2BtiHG;;;;;;;;EAIE,WAAA;C3B4iHL;A2BtiHD;;;;EAKI,kBAAA;C3BuiHH;A2BliHD;EACE,kBAAA;C3BoiHD;A2BriHD;;;EAOI,YAAA;C3BmiHH;A2B1iHD;;;EAYI,iBAAA;C3BmiHH;A2B/hHD;EACE,iBAAA;C3BiiHD;A2B7hHD;EACE,eAAA;C3B+hHD;A2B9hHC;EClDA,8BAAA;EACG,2BAAA;C5BmlHJ;A2B7hHD;;EC/CE,6BAAA;EACG,0BAAA;C5BglHJ;A2B5hHD;EACE,YAAA;C3B8hHD;A2B5hHD;EACE,iBAAA;C3B8hHD;A2B5hHD;;ECnEE,8BAAA;EACG,2BAAA;C5BmmHJ;A2B3hHD;ECjEE,6BAAA;EACG,0BAAA;C5B+lHJ;A2B1hHD;;EAEE,WAAA;C3B4hHD;A2B3gHD;EACE,kBAAA;EACA,mBAAA;C3B6gHD;A2B3gHD;EACE,mBAAA;EACA,oBAAA;C3B6gHD;A2BxgHD;EtB/CE,yDAAA;EACQ,iDAAA;CL0jHT;A2BxgHC;EtBnDA,yBAAA;EACQ,iBAAA;CL8jHT;A2BrgHD;EACE,eAAA;C3BugHD;A2BpgHD;EACE,wBAAA;EACA,uBAAA;C3BsgHD;A2BngHD;EACE,wBAAA;C3BqgHD;A2B9/GD;;;EAII,eAAA;EACA,YAAA;EACA,YAAA;EACA,gBAAA;C3B+/GH;A2BtgHD;EAcM,YAAA;C3B2/GL;A2BzgHD;;;;EAsBI,iBAAA;EACA,eAAA;C3By/GH;A2Bp/GC;EACE,iBAAA;C3Bs/GH;A2Bp/GC;EC3KA,6BAAA;EACC,4BAAA;EAOD,8BAAA;EACC,6BAAA;C5B4pHF;A2Bt/GC;EC/KA,2BAAA;EACC,0BAAA;EAOD,gCAAA;EACC,+BAAA;C5BkqHF;A2Bv/GD;EACE,iBAAA;C3By/GD;A2Bv/GD;;EC/KE,8BAAA;EACC,6BAAA;C5B0qHF;A2Bt/GD;EC7LE,2BAAA;EACC,0BAAA;C5BsrHF;A2Bl/GD;EACE,eAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;C3Bo/GD;A2Bx/GD;;EAOI,YAAA;EACA,oBAAA;EACA,UAAA;C3Bq/GH;A2B9/GD;EAYI,YAAA;C3Bq/GH;A2BjgHD;EAgBI,WAAA;C3Bo/GH;A2Bn+GD;;;;EAKM,mBAAA;EACA,uBAAA;EACA,qBAAA;C3Bo+GL;A6B9sHD;EACE,mBAAA;EACA,eAAA;EACA,0BAAA;C7BgtHD;A6B7sHC;EACE,YAAA;EACA,gBAAA;EACA,iBAAA;C7B+sHH;A6BxtHD;EAeI,mBAAA;EACA,WAAA;EAKA,YAAA;EAEA,YAAA;EACA,iBAAA;C7BusHH;A6BrsHG;EACE,WAAA;C7BusHL;A6B7rHD;;;EV0BE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBwqHD;AmBtqHC;;;EACE,aAAA;EACA,kBAAA;CnB0qHH;AmBvqHC;;;;;;EAEE,aAAA;CnB6qHH;A6B/sHD;;;EVqBE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnB+rHD;AmB7rHC;;;EACE,aAAA;EACA,kBAAA;CnBisHH;AmB9rHC;;;;;;EAEE,aAAA;CnBosHH;A6B7tHD;;;EAGE,oBAAA;C7B+tHD;A6B7tHC;;;EACE,iBAAA;C7BiuHH;A6B7tHD;;EAEE,UAAA;EACA,oBAAA;EACA,uBAAA;C7B+tHD;A6B1tHD;EACE,kBAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;EACA,uBAAA;EACA,mBAAA;C7B4tHD;A6BztHC;EACE,kBAAA;EACA,gBAAA;EACA,mBAAA;C7B2tHH;A6BztHC;EACE,mBAAA;EACA,gBAAA;EACA,mBAAA;C7B2tHH;A6B/uHD;;EA0BI,cAAA;C7BytHH;A6BptHD;;;;;;;EDpGE,8BAAA;EACG,2BAAA;C5Bi0HJ;A6BrtHD;EACE,gBAAA;C7ButHD;A6BrtHD;;;;;;;EDxGE,6BAAA;EACG,0BAAA;C5Bs0HJ;A6BttHD;EACE,eAAA;C7BwtHD;A6BntHD;EACE,mBAAA;EAGA,aAAA;EACA,oBAAA;C7BmtHD;A6BxtHD;EAUI,mBAAA;C7BitHH;A6B3tHD;EAYM,kBAAA;C7BktHL;A6B/sHG;;;EAGE,WAAA;C7BitHL;A6B5sHC;;EAGI,mBAAA;C7B6sHL;A6B1sHC;;EAGI,WAAA;EACA,kBAAA;C7B2sHL;A8B12HD;EACE,iBAAA;EACA,gBAAA;EACA,iBAAA;C9B42HD;A8B/2HD;EAOI,mBAAA;EACA,eAAA;C9B22HH;A8Bn3HD;EAWM,mBAAA;EACA,eAAA;EACA,mBAAA;C9B22HL;A8B12HK;;EAEE,sBAAA;EACA,0BAAA;C9B42HP;A8Bv2HG;EACE,eAAA;C9By2HL;A8Bv2HK;;EAEE,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,oBAAA;C9By2HP;A8Bl2HG;;;EAGE,0BAAA;EACA,sBAAA;C9Bo2HL;A8B74HD;ELHE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBm5HD;A8Bn5HD;EA0DI,gBAAA;C9B41HH;A8Bn1HD;EACE,8BAAA;C9Bq1HD;A8Bt1HD;EAGI,YAAA;EAEA,oBAAA;C9Bq1HH;A8B11HD;EASM,kBAAA;EACA,wBAAA;EACA,8BAAA;EACA,2BAAA;C9Bo1HL;A8Bn1HK;EACE,mCAAA;C9Bq1HP;A8B/0HK;;;EAGE,eAAA;EACA,uBAAA;EACA,uBAAA;EACA,iCAAA;EACA,gBAAA;C9Bi1HP;A8B50HC;EAqDA,YAAA;EA8BA,iBAAA;C9B6vHD;A8Bh1HC;EAwDE,YAAA;C9B2xHH;A8Bn1HC;EA0DI,mBAAA;EACA,mBAAA;C9B4xHL;A8Bv1HC;EAgEE,UAAA;EACA,WAAA;C9B0xHH;A8B9wHD;EA0DA;IAjEM,oBAAA;IACA,UAAA;G9ByxHH;E8BztHH;IA9DQ,iBAAA;G9B0xHL;CACF;A8Bp2HC;EAuFE,gBAAA;EACA,mBAAA;C9BgxHH;A8Bx2HC;;;EA8FE,uBAAA;C9B+wHH;A8BjwHD;EA2BA;IApCM,8BAAA;IACA,2BAAA;G9B8wHH;E8B3uHH;;;IA9BM,0BAAA;G9B8wHH;CACF;A8B/2HD;EAEI,YAAA;C9Bg3HH;A8Bl3HD;EAMM,mBAAA;C9B+2HL;A8Br3HD;EASM,iBAAA;C9B+2HL;A8B12HK;;;EAGE,YAAA;EACA,0BAAA;C9B42HP;A8Bp2HD;EAEI,YAAA;C9Bq2HH;A8Bv2HD;EAIM,gBAAA;EACA,eAAA;C9Bs2HL;A8B11HD;EACE,YAAA;C9B41HD;A8B71HD;EAII,YAAA;C9B41HH;A8Bh2HD;EAMM,mBAAA;EACA,mBAAA;C9B61HL;A8Bp2HD;EAYI,UAAA;EACA,WAAA;C9B21HH;A8B/0HD;EA0DA;IAjEM,oBAAA;IACA,UAAA;G9B01HH;E8B1xHH;IA9DQ,iBAAA;G9B21HL;CACF;A8Bn1HD;EACE,iBAAA;C9Bq1HD;A8Bt1HD;EAKI,gBAAA;EACA,mBAAA;C9Bo1HH;A8B11HD;;;EAYI,uBAAA;C9Bm1HH;A8Br0HD;EA2BA;IApCM,8BAAA;IACA,2BAAA;G9Bk1HH;E8B/yHH;;;IA9BM,0BAAA;G9Bk1HH;CACF;A8Bz0HD;EAEI,cAAA;C9B00HH;A8B50HD;EAKI,eAAA;C9B00HH;A8Bj0HD;EAEE,iBAAA;EF3OA,2BAAA;EACC,0BAAA;C5B8iIF;A+BxiID;EACE,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,8BAAA;C/B0iID;A+BliID;EA8nBA;IAhoBI,mBAAA;G/BwiID;CACF;A+BzhID;EAgnBA;IAlnBI,YAAA;G/B+hID;CACF;A+BjhID;EACE,oBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,2DAAA;UAAA,mDAAA;EAEA,kCAAA;C/BkhID;A+BhhIC;EACE,iBAAA;C/BkhIH;A+Bt/HD;EA6jBA;IArlBI,YAAA;IACA,cAAA;IACA,yBAAA;YAAA,iBAAA;G/BkhID;E+BhhIC;IACE,0BAAA;IACA,wBAAA;IACA,kBAAA;IACA,6BAAA;G/BkhIH;E+B/gIC;IACE,oBAAA;G/BihIH;E+B5gIC;;;IAGE,gBAAA;IACA,iBAAA;G/B8gIH;CACF;A+B1gID;;EAGI,kBAAA;C/B2gIH;A+BtgIC;EAmjBF;;IArjBM,kBAAA;G/B6gIH;CACF;A+BpgID;;;;EAII,oBAAA;EACA,mBAAA;C/BsgIH;A+BhgIC;EAgiBF;;;;IAniBM,gBAAA;IACA,eAAA;G/B0gIH;CACF;A+B9/HD;EACE,cAAA;EACA,sBAAA;C/BggID;A+B3/HD;EA8gBA;IAhhBI,iBAAA;G/BigID;CACF;A+B7/HD;;EAEE,gBAAA;EACA,SAAA;EACA,QAAA;EACA,cAAA;C/B+/HD;A+Bz/HD;EAggBA;;IAlgBI,iBAAA;G/BggID;CACF;A+B9/HD;EACE,OAAA;EACA,sBAAA;C/BggID;A+B9/HD;EACE,UAAA;EACA,iBAAA;EACA,sBAAA;C/BggID;A+B1/HD;EACE,YAAA;EACA,mBAAA;EACA,gBAAA;EACA,kBAAA;EACA,aAAA;C/B4/HD;A+B1/HC;;EAEE,sBAAA;C/B4/HH;A+BrgID;EAaI,eAAA;C/B2/HH;A+Bl/HD;EALI;;IAEE,mBAAA;G/B0/HH;CACF;A+Bh/HD;EACE,mBAAA;EACA,aAAA;EACA,mBAAA;EACA,kBAAA;EC9LA,gBAAA;EACA,mBAAA;ED+LA,8BAAA;EACA,uBAAA;EACA,8BAAA;EACA,mBAAA;C/Bm/HD;A+B/+HC;EACE,WAAA;C/Bi/HH;A+B//HD;EAmBI,eAAA;EACA,YAAA;EACA,YAAA;EACA,mBAAA;C/B++HH;A+BrgID;EAyBI,gBAAA;C/B++HH;A+Bz+HD;EAqbA;IAvbI,cAAA;G/B++HD;CACF;A+Bt+HD;EACE,oBAAA;C/Bw+HD;A+Bz+HD;EAII,kBAAA;EACA,qBAAA;EACA,kBAAA;C/Bw+HH;A+B58HC;EA2YF;IAjaM,iBAAA;IACA,YAAA;IACA,YAAA;IACA,cAAA;IACA,8BAAA;IACA,UAAA;IACA,yBAAA;YAAA,iBAAA;G/Bs+HH;E+B3kHH;;IAxZQ,2BAAA;G/Bu+HL;E+B/kHH;IArZQ,kBAAA;G/Bu+HL;E+Bt+HK;;IAEE,uBAAA;G/Bw+HP;CACF;A+Bt9HD;EA+XA;IA1YI,YAAA;IACA,UAAA;G/Bq+HD;E+B5lHH;IAtYM,YAAA;G/Bq+HH;E+B/lHH;IApYQ,kBAAA;IACA,qBAAA;G/Bs+HL;CACF;A+B39HD;EACE,mBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,qCAAA;E1B9NA,6FAAA;EACQ,qFAAA;E2B/DR,gBAAA;EACA,mBAAA;ChC4vID;AkBtuHD;EAwEA;IAtIM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlBwyHH;EkBpqHH;IA/HM,sBAAA;IACA,YAAA;IACA,uBAAA;GlBsyHH;EkBzqHH;IAxHM,sBAAA;GlBoyHH;EkB5qHH;IApHM,sBAAA;IACA,uBAAA;GlBmyHH;EkBhrHH;;;IA9GQ,YAAA;GlBmyHL;EkBrrHH;IAxGM,YAAA;GlBgyHH;EkBxrHH;IApGM,iBAAA;IACA,uBAAA;GlB+xHH;EkB5rHH;;IA5FM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlB4xHH;EkBnsHH;;IAtFQ,gBAAA;GlB6xHL;EkBvsHH;;IAjFM,mBAAA;IACA,eAAA;GlB4xHH;EkB5sHH;IA3EM,OAAA;GlB0xHH;CACF;A+BpgIC;EAmWF;IAzWM,mBAAA;G/B8gIH;E+B5gIG;IACE,iBAAA;G/B8gIL;CACF;A+B7/HD;EAoVA;IA5VI,YAAA;IACA,UAAA;IACA,eAAA;IACA,gBAAA;IACA,eAAA;IACA,kBAAA;I1BzPF,yBAAA;IACQ,iBAAA;GLmwIP;CACF;A+BngID;EACE,cAAA;EHpUA,2BAAA;EACC,0BAAA;C5B00IF;A+BngID;EACE,iBAAA;EHzUA,6BAAA;EACC,4BAAA;EAOD,8BAAA;EACC,6BAAA;C5By0IF;A+B//HD;EChVE,gBAAA;EACA,mBAAA;ChCk1ID;A+BhgIC;ECnVA,iBAAA;EACA,oBAAA;ChCs1ID;A+BjgIC;ECtVA,iBAAA;EACA,oBAAA;ChC01ID;A+B3/HD;EChWE,iBAAA;EACA,oBAAA;ChC81ID;A+Bv/HD;EAsSA;IA1SI,YAAA;IACA,kBAAA;IACA,mBAAA;G/B+/HD;CACF;A+Bl+HD;EAhBE;IExWA,uBAAA;GjC81IC;E+Br/HD;IE5WA,wBAAA;IF8WE,oBAAA;G/Bu/HD;E+Bz/HD;IAKI,gBAAA;G/Bu/HH;CACF;A+B9+HD;EACE,0BAAA;EACA,sBAAA;C/Bg/HD;A+Bl/HD;EAKI,YAAA;C/Bg/HH;A+B/+HG;;EAEE,eAAA;EACA,8BAAA;C/Bi/HL;A+B1/HD;EAcI,YAAA;C/B++HH;A+B7/HD;EAmBM,YAAA;C/B6+HL;A+B3+HK;;EAEE,YAAA;EACA,8BAAA;C/B6+HP;A+Bz+HK;;;EAGE,YAAA;EACA,0BAAA;C/B2+HP;A+Bv+HK;;;EAGE,YAAA;EACA,8BAAA;C/By+HP;A+BjhID;EA8CI,mBAAA;C/Bs+HH;A+Br+HG;;EAEE,uBAAA;C/Bu+HL;A+BxhID;EAoDM,uBAAA;C/Bu+HL;A+B3hID;;EA0DI,sBAAA;C/Bq+HH;A+B99HK;;;EAGE,0BAAA;EACA,YAAA;C/Bg+HP;A+B/7HC;EAoKF;IA7LU,YAAA;G/B49HP;E+B39HO;;IAEE,YAAA;IACA,8BAAA;G/B69HT;E+Bz9HO;;;IAGE,YAAA;IACA,0BAAA;G/B29HT;E+Bv9HO;;;IAGE,YAAA;IACA,8BAAA;G/By9HT;CACF;A+B3jID;EA8GI,YAAA;C/Bg9HH;A+B/8HG;EACE,YAAA;C/Bi9HL;A+BjkID;EAqHI,YAAA;C/B+8HH;A+B98HG;;EAEE,YAAA;C/Bg9HL;A+B58HK;;;;EAEE,YAAA;C/Bg9HP;A+Bx8HD;EACE,uBAAA;EACA,sBAAA;C/B08HD;A+B58HD;EAKI,eAAA;C/B08HH;A+Bz8HG;;EAEE,YAAA;EACA,8BAAA;C/B28HL;A+Bp9HD;EAcI,eAAA;C/By8HH;A+Bv9HD;EAmBM,eAAA;C/Bu8HL;A+Br8HK;;EAEE,YAAA;EACA,8BAAA;C/Bu8HP;A+Bn8HK;;;EAGE,YAAA;EACA,0BAAA;C/Bq8HP;A+Bj8HK;;;EAGE,YAAA;EACA,8BAAA;C/Bm8HP;A+B3+HD;EA+CI,mBAAA;C/B+7HH;A+B97HG;;EAEE,uBAAA;C/Bg8HL;A+Bl/HD;EAqDM,uBAAA;C/Bg8HL;A+Br/HD;;EA2DI,sBAAA;C/B87HH;A+Bx7HK;;;EAGE,0BAAA;EACA,YAAA;C/B07HP;A+Bn5HC;EAwBF;IAvDU,sBAAA;G/Bs7HP;E+B/3HH;IApDU,0BAAA;G/Bs7HP;E+Bl4HH;IAjDU,eAAA;G/Bs7HP;E+Br7HO;;IAEE,YAAA;IACA,8BAAA;G/Bu7HT;E+Bn7HO;;;IAGE,YAAA;IACA,0BAAA;G/Bq7HT;E+Bj7HO;;;IAGE,YAAA;IACA,8BAAA;G/Bm7HT;CACF;A+B3hID;EA+GI,eAAA;C/B+6HH;A+B96HG;EACE,YAAA;C/Bg7HL;A+BjiID;EAsHI,eAAA;C/B86HH;A+B76HG;;EAEE,YAAA;C/B+6HL;A+B36HK;;;;EAEE,YAAA;C/B+6HP;AkCzjJD;EACE,kBAAA;EACA,oBAAA;EACA,iBAAA;EACA,0BAAA;EACA,mBAAA;ClC2jJD;AkChkJD;EAQI,sBAAA;ClC2jJH;AkCnkJD;EAWM,kBAAA;EACA,eAAA;EACA,YAAA;ClC2jJL;AkCxkJD;EAkBI,eAAA;ClCyjJH;AmC7kJD;EACE,sBAAA;EACA,gBAAA;EACA,eAAA;EACA,mBAAA;CnC+kJD;AmCnlJD;EAOI,gBAAA;CnC+kJH;AmCtlJD;;EAUM,mBAAA;EACA,YAAA;EACA,kBAAA;EACA,wBAAA;EACA,sBAAA;EACA,eAAA;EACA,uBAAA;EACA,uBAAA;EACA,kBAAA;CnCglJL;AmC9kJG;;EAGI,eAAA;EPXN,+BAAA;EACG,4BAAA;C5B2lJJ;AmC7kJG;;EPvBF,gCAAA;EACG,6BAAA;C5BwmJJ;AmCxkJG;;;;EAEE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;CnC4kJL;AmCtkJG;;;;;;EAGE,WAAA;EACA,YAAA;EACA,0BAAA;EACA,sBAAA;EACA,gBAAA;CnC2kJL;AmCloJD;;;;;;EAkEM,eAAA;EACA,uBAAA;EACA,mBAAA;EACA,oBAAA;CnCwkJL;AmC/jJD;;EC3EM,mBAAA;EACA,gBAAA;EACA,uBAAA;CpC8oJL;AoC5oJG;;ERKF,+BAAA;EACG,4BAAA;C5B2oJJ;AoC3oJG;;ERTF,gCAAA;EACG,6BAAA;C5BwpJJ;AmC1kJD;;EChFM,kBAAA;EACA,gBAAA;EACA,iBAAA;CpC8pJL;AoC5pJG;;ERKF,+BAAA;EACG,4BAAA;C5B2pJJ;AoC3pJG;;ERTF,gCAAA;EACG,6BAAA;C5BwqJJ;AqC3qJD;EACE,gBAAA;EACA,eAAA;EACA,iBAAA;EACA,mBAAA;CrC6qJD;AqCjrJD;EAOI,gBAAA;CrC6qJH;AqCprJD;;EAUM,sBAAA;EACA,kBAAA;EACA,uBAAA;EACA,uBAAA;EACA,oBAAA;CrC8qJL;AqC5rJD;;EAmBM,sBAAA;EACA,0BAAA;CrC6qJL;AqCjsJD;;EA2BM,aAAA;CrC0qJL;AqCrsJD;;EAkCM,YAAA;CrCuqJL;AqCzsJD;;;;EA2CM,eAAA;EACA,uBAAA;EACA,oBAAA;CrCoqJL;AsCltJD;EACE,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,kBAAA;EACA,eAAA;EACA,YAAA;EACA,mBAAA;EACA,oBAAA;EACA,yBAAA;EACA,qBAAA;CtCotJD;AsChtJG;;EAEE,YAAA;EACA,sBAAA;EACA,gBAAA;CtCktJL;AsC7sJC;EACE,cAAA;CtC+sJH;AsC3sJC;EACE,mBAAA;EACA,UAAA;CtC6sJH;AsCtsJD;ECtCE,0BAAA;CvC+uJD;AuC5uJG;;EAEE,0BAAA;CvC8uJL;AsCzsJD;EC1CE,0BAAA;CvCsvJD;AuCnvJG;;EAEE,0BAAA;CvCqvJL;AsC5sJD;EC9CE,0BAAA;CvC6vJD;AuC1vJG;;EAEE,0BAAA;CvC4vJL;AsC/sJD;EClDE,0BAAA;CvCowJD;AuCjwJG;;EAEE,0BAAA;CvCmwJL;AsCltJD;ECtDE,0BAAA;CvC2wJD;AuCxwJG;;EAEE,0BAAA;CvC0wJL;AsCrtJD;EC1DE,0BAAA;CvCkxJD;AuC/wJG;;EAEE,0BAAA;CvCixJL;AwCnxJD;EACE,sBAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,kBAAA;EACA,YAAA;EACA,eAAA;EACA,uBAAA;EACA,oBAAA;EACA,mBAAA;EACA,0BAAA;EACA,oBAAA;CxCqxJD;AwClxJC;EACE,cAAA;CxCoxJH;AwChxJC;EACE,mBAAA;EACA,UAAA;CxCkxJH;AwC/wJC;;EAEE,OAAA;EACA,iBAAA;CxCixJH;AwC5wJG;;EAEE,YAAA;EACA,sBAAA;EACA,gBAAA;CxC8wJL;AwCzwJC;;EAEE,eAAA;EACA,uBAAA;CxC2wJH;AwCxwJC;EACE,aAAA;CxC0wJH;AwCvwJC;EACE,kBAAA;CxCywJH;AwCtwJC;EACE,iBAAA;CxCwwJH;AyCl0JD;EACE,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,eAAA;EACA,0BAAA;CzCo0JD;AyCz0JD;;EASI,eAAA;CzCo0JH;AyC70JD;EAaI,oBAAA;EACA,gBAAA;EACA,iBAAA;CzCm0JH;AyCl1JD;EAmBI,0BAAA;CzCk0JH;AyC/zJC;;EAEE,mBAAA;EACA,mBAAA;EACA,oBAAA;CzCi0JH;AyC31JD;EA8BI,gBAAA;CzCg0JH;AyC9yJD;EACA;IAfI,kBAAA;IACA,qBAAA;GzCg0JD;EyC9zJC;;IAEE,mBAAA;IACA,oBAAA;GzCg0JH;EyCvzJH;;IAJM,gBAAA;GzC+zJH;CACF;A0C52JD;EACE,eAAA;EACA,aAAA;EACA,oBAAA;EACA,wBAAA;EACA,uBAAA;EACA,uBAAA;EACA,mBAAA;ErCiLA,4CAAA;EACK,uCAAA;EACG,oCAAA;CL8rJT;A0Cx3JD;;EAaI,kBAAA;EACA,mBAAA;C1C+2JH;A0C32JC;;;EAGE,sBAAA;C1C62JH;A0Cl4JD;EA0BI,aAAA;EACA,eAAA;C1C22JH;A2Cp4JD;EACE,cAAA;EACA,oBAAA;EACA,8BAAA;EACA,mBAAA;C3Cs4JD;A2C14JD;EAQI,cAAA;EAEA,eAAA;C3Co4JH;A2C94JD;EAeI,kBAAA;C3Ck4JH;A2Cj5JD;;EAqBI,iBAAA;C3Cg4JH;A2Cr5JD;EAyBI,gBAAA;C3C+3JH;A2Cv3JD;;EAEE,oBAAA;C3Cy3JD;A2C33JD;;EAMI,mBAAA;EACA,UAAA;EACA,aAAA;EACA,eAAA;C3Cy3JH;A2Cj3JD;ECvDE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C26JD;A2Ct3JD;EClDI,0BAAA;C5C26JH;A2Cz3JD;EC/CI,eAAA;C5C26JH;A2Cx3JD;EC3DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Cs7JD;A2C73JD;ECtDI,0BAAA;C5Cs7JH;A2Ch4JD;ECnDI,eAAA;C5Cs7JH;A2C/3JD;EC/DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Ci8JD;A2Cp4JD;EC1DI,0BAAA;C5Ci8JH;A2Cv4JD;ECvDI,eAAA;C5Ci8JH;A2Ct4JD;ECnEE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C48JD;A2C34JD;EC9DI,0BAAA;C5C48JH;A2C94JD;EC3DI,eAAA;C5C48JH;A6C98JD;EACE;IAAQ,4BAAA;G7Ci9JP;E6Ch9JD;IAAQ,yBAAA;G7Cm9JP;CACF;A6Ch9JD;EACE;IAAQ,4BAAA;G7Cm9JP;E6Cl9JD;IAAQ,yBAAA;G7Cq9JP;CACF;A6Cx9JD;EACE;IAAQ,4BAAA;G7Cm9JP;E6Cl9JD;IAAQ,yBAAA;G7Cq9JP;CACF;A6C98JD;EACE,iBAAA;EACA,aAAA;EACA,oBAAA;EACA,0BAAA;EACA,mBAAA;ExCsCA,uDAAA;EACQ,+CAAA;CL26JT;A6C78JD;EACE,YAAA;EACA,UAAA;EACA,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,YAAA;EACA,mBAAA;EACA,0BAAA;ExCyBA,uDAAA;EACQ,+CAAA;EAyHR,oCAAA;EACK,+BAAA;EACG,4BAAA;CL+zJT;A6C18JD;;ECCI,8MAAA;EACA,yMAAA;EACA,sMAAA;EDAF,mCAAA;UAAA,2BAAA;C7C88JD;A6Cv8JD;;ExC5CE,2DAAA;EACK,sDAAA;EACG,mDAAA;CLu/JT;A6Cp8JD;EErEE,0BAAA;C/C4gKD;A+CzgKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C49JH;A6Cx8JD;EEzEE,0BAAA;C/CohKD;A+CjhKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9Co+JH;A6C58JD;EE7EE,0BAAA;C/C4hKD;A+CzhKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C4+JH;A6Ch9JD;EEjFE,0BAAA;C/CoiKD;A+CjiKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9Co/JH;AgD5iKD;EAEE,iBAAA;ChD6iKD;AgD3iKC;EACE,cAAA;ChD6iKH;AgDziKD;;EAEE,QAAA;EACA,iBAAA;ChD2iKD;AgDxiKD;EACE,eAAA;ChD0iKD;AgDviKD;EACE,eAAA;ChDyiKD;AgDtiKC;EACE,gBAAA;ChDwiKH;AgDpiKD;;EAEE,mBAAA;ChDsiKD;AgDniKD;;EAEE,oBAAA;ChDqiKD;AgDliKD;;;EAGE,oBAAA;EACA,oBAAA;ChDoiKD;AgDjiKD;EACE,uBAAA;ChDmiKD;AgDhiKD;EACE,uBAAA;ChDkiKD;AgD9hKD;EACE,cAAA;EACA,mBAAA;ChDgiKD;AgD1hKD;EACE,gBAAA;EACA,iBAAA;ChD4hKD;AiDnlKD;EAEE,oBAAA;EACA,gBAAA;CjDolKD;AiD5kKD;EACE,mBAAA;EACA,eAAA;EACA,mBAAA;EAEA,oBAAA;EACA,uBAAA;EACA,uBAAA;CjD6kKD;AiD1kKC;ErB3BA,6BAAA;EACC,4BAAA;C5BwmKF;AiD3kKC;EACE,iBAAA;ErBvBF,gCAAA;EACC,+BAAA;C5BqmKF;AiDpkKD;;EAEE,YAAA;CjDskKD;AiDxkKD;;EAKI,YAAA;CjDukKH;AiDnkKC;;;;EAEE,sBAAA;EACA,YAAA;EACA,0BAAA;CjDukKH;AiDnkKD;EACE,YAAA;EACA,iBAAA;CjDqkKD;AiDhkKC;;;EAGE,0BAAA;EACA,eAAA;EACA,oBAAA;CjDkkKH;AiDvkKC;;;EASI,eAAA;CjDmkKL;AiD5kKC;;;EAYI,eAAA;CjDqkKL;AiDhkKC;;;EAGE,WAAA;EACA,YAAA;EACA,0BAAA;EACA,sBAAA;CjDkkKH;AiDxkKC;;;;;;;;;EAYI,eAAA;CjDukKL;AiDnlKC;;;EAeI,eAAA;CjDykKL;AkD3qKC;EACE,eAAA;EACA,0BAAA;ClD6qKH;AkD3qKG;;EAEE,eAAA;ClD6qKL;AkD/qKG;;EAKI,eAAA;ClD8qKP;AkD3qKK;;;;EAEE,eAAA;EACA,0BAAA;ClD+qKP;AkD7qKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDkrKP;AkDxsKC;EACE,eAAA;EACA,0BAAA;ClD0sKH;AkDxsKG;;EAEE,eAAA;ClD0sKL;AkD5sKG;;EAKI,eAAA;ClD2sKP;AkDxsKK;;;;EAEE,eAAA;EACA,0BAAA;ClD4sKP;AkD1sKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD+sKP;AkDruKC;EACE,eAAA;EACA,0BAAA;ClDuuKH;AkDruKG;;EAEE,eAAA;ClDuuKL;AkDzuKG;;EAKI,eAAA;ClDwuKP;AkDruKK;;;;EAEE,eAAA;EACA,0BAAA;ClDyuKP;AkDvuKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD4uKP;AkDlwKC;EACE,eAAA;EACA,0BAAA;ClDowKH;AkDlwKG;;EAEE,eAAA;ClDowKL;AkDtwKG;;EAKI,eAAA;ClDqwKP;AkDlwKK;;;;EAEE,eAAA;EACA,0BAAA;ClDswKP;AkDpwKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDywKP;AiDxqKD;EACE,cAAA;EACA,mBAAA;CjD0qKD;AiDxqKD;EACE,iBAAA;EACA,iBAAA;CjD0qKD;AmDpyKD;EACE,oBAAA;EACA,uBAAA;EACA,8BAAA;EACA,mBAAA;E9C0DA,kDAAA;EACQ,0CAAA;CL6uKT;AmDnyKD;EACE,cAAA;CnDqyKD;AmDhyKD;EACE,mBAAA;EACA,qCAAA;EvBpBA,6BAAA;EACC,4BAAA;C5BuzKF;AmDtyKD;EAMI,eAAA;CnDmyKH;AmD9xKD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,eAAA;CnDgyKD;AmDpyKD;;;;;EAWI,eAAA;CnDgyKH;AmD3xKD;EACE,mBAAA;EACA,0BAAA;EACA,2BAAA;EvBxCA,gCAAA;EACC,+BAAA;C5Bs0KF;AmDrxKD;;EAGI,iBAAA;CnDsxKH;AmDzxKD;;EAMM,oBAAA;EACA,iBAAA;CnDuxKL;AmDnxKG;;EAEI,cAAA;EvBvEN,6BAAA;EACC,4BAAA;C5B61KF;AmDjxKG;;EAEI,iBAAA;EvBvEN,gCAAA;EACC,+BAAA;C5B21KF;AmD1yKD;EvB1DE,2BAAA;EACC,0BAAA;C5Bu2KF;AmD7wKD;EAEI,oBAAA;CnD8wKH;AmD3wKD;EACE,oBAAA;CnD6wKD;AmDrwKD;;;EAII,iBAAA;CnDswKH;AmD1wKD;;;EAOM,mBAAA;EACA,oBAAA;CnDwwKL;AmDhxKD;;EvBzGE,6BAAA;EACC,4BAAA;C5B63KF;AmDrxKD;;;;EAmBQ,4BAAA;EACA,6BAAA;CnDwwKP;AmD5xKD;;;;;;;;EAwBU,4BAAA;CnD8wKT;AmDtyKD;;;;;;;;EA4BU,6BAAA;CnDoxKT;AmDhzKD;;EvBjGE,gCAAA;EACC,+BAAA;C5Bq5KF;AmDrzKD;;;;EAyCQ,+BAAA;EACA,gCAAA;CnDkxKP;AmD5zKD;;;;;;;;EA8CU,+BAAA;CnDwxKT;AmDt0KD;;;;;;;;EAkDU,gCAAA;CnD8xKT;AmDh1KD;;;;EA2DI,2BAAA;CnD2xKH;AmDt1KD;;EA+DI,cAAA;CnD2xKH;AmD11KD;;EAmEI,UAAA;CnD2xKH;AmD91KD;;;;;;;;;;;;EA0EU,eAAA;CnDkyKT;AmD52KD;;;;;;;;;;;;EA8EU,gBAAA;CnD4yKT;AmD13KD;;;;;;;;EAuFU,iBAAA;CnD6yKT;AmDp4KD;;;;;;;;EAgGU,iBAAA;CnD8yKT;AmD94KD;EAsGI,UAAA;EACA,iBAAA;CnD2yKH;AmDjyKD;EACE,oBAAA;CnDmyKD;AmDpyKD;EAKI,iBAAA;EACA,mBAAA;CnDkyKH;AmDxyKD;EASM,gBAAA;CnDkyKL;AmD3yKD;EAcI,iBAAA;CnDgyKH;AmD9yKD;;EAkBM,2BAAA;CnDgyKL;AmDlzKD;EAuBI,cAAA;CnD8xKH;AmDrzKD;EAyBM,8BAAA;CnD+xKL;AmDxxKD;EC1PE,mBAAA;CpDqhLD;AoDnhLC;EACE,eAAA;EACA,0BAAA;EACA,mBAAA;CpDqhLH;AoDxhLC;EAMI,uBAAA;CpDqhLL;AoD3hLC;EASI,eAAA;EACA,0BAAA;CpDqhLL;AoDlhLC;EAEI,0BAAA;CpDmhLL;AmDvyKD;EC7PE,sBAAA;CpDuiLD;AoDriLC;EACE,YAAA;EACA,0BAAA;EACA,sBAAA;CpDuiLH;AoD1iLC;EAMI,0BAAA;CpDuiLL;AoD7iLC;EASI,eAAA;EACA,uBAAA;CpDuiLL;AoDpiLC;EAEI,6BAAA;CpDqiLL;AmDtzKD;EChQE,sBAAA;CpDyjLD;AoDvjLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDyjLH;AoD5jLC;EAMI,0BAAA;CpDyjLL;AoD/jLC;EASI,eAAA;EACA,0BAAA;CpDyjLL;AoDtjLC;EAEI,6BAAA;CpDujLL;AmDr0KD;ECnQE,sBAAA;CpD2kLD;AoDzkLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD2kLH;AoD9kLC;EAMI,0BAAA;CpD2kLL;AoDjlLC;EASI,eAAA;EACA,0BAAA;CpD2kLL;AoDxkLC;EAEI,6BAAA;CpDykLL;AmDp1KD;ECtQE,sBAAA;CpD6lLD;AoD3lLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD6lLH;AoDhmLC;EAMI,0BAAA;CpD6lLL;AoDnmLC;EASI,eAAA;EACA,0BAAA;CpD6lLL;AoD1lLC;EAEI,6BAAA;CpD2lLL;AmDn2KD;ECzQE,sBAAA;CpD+mLD;AoD7mLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD+mLH;AoDlnLC;EAMI,0BAAA;CpD+mLL;AoDrnLC;EASI,eAAA;EACA,0BAAA;CpD+mLL;AoD5mLC;EAEI,6BAAA;CpD6mLL;AqD7nLD;EACE,mBAAA;EACA,eAAA;EACA,UAAA;EACA,WAAA;EACA,iBAAA;CrD+nLD;AqDpoLD;;;;;EAYI,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,aAAA;EACA,YAAA;EACA,UAAA;CrD+nLH;AqD1nLD;EACE,uBAAA;CrD4nLD;AqDxnLD;EACE,oBAAA;CrD0nLD;AsDrpLD;EACE,iBAAA;EACA,cAAA;EACA,oBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;EjDwDA,wDAAA;EACQ,gDAAA;CLgmLT;AsD/pLD;EASI,mBAAA;EACA,kCAAA;CtDypLH;AsDppLD;EACE,cAAA;EACA,mBAAA;CtDspLD;AsDppLD;EACE,aAAA;EACA,mBAAA;CtDspLD;AuD5qLD;EACE,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,YAAA;EACA,0BAAA;EjCRA,aAAA;EAGA,0BAAA;CtBqrLD;AuD7qLC;;EAEE,YAAA;EACA,sBAAA;EACA,gBAAA;EjCfF,aAAA;EAGA,0BAAA;CtB6rLD;AuDzqLC;EACE,WAAA;EACA,gBAAA;EACA,wBAAA;EACA,UAAA;EACA,yBAAA;CvD2qLH;AwDhsLD;EACE,iBAAA;CxDksLD;AwD9rLD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,kCAAA;EAIA,WAAA;CxD6rLD;AwD1rLC;EnD+GA,sCAAA;EACI,kCAAA;EACC,iCAAA;EACG,8BAAA;EAkER,oDAAA;EAEK,0CAAA;EACG,oCAAA;CL6gLT;AwDhsLC;EnD2GA,mCAAA;EACI,+BAAA;EACC,8BAAA;EACG,2BAAA;CLwlLT;AwDpsLD;EACE,mBAAA;EACA,iBAAA;CxDssLD;AwDlsLD;EACE,mBAAA;EACA,YAAA;EACA,aAAA;CxDosLD;AwDhsLD;EACE,mBAAA;EACA,uBAAA;EACA,uBAAA;EACA,qCAAA;EACA,mBAAA;EnDaA,iDAAA;EACQ,yCAAA;EmDZR,qCAAA;UAAA,6BAAA;EAEA,WAAA;CxDksLD;AwD9rLD;EACE,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,uBAAA;CxDgsLD;AwD9rLC;ElCrEA,WAAA;EAGA,yBAAA;CtBowLD;AwDjsLC;ElCtEA,aAAA;EAGA,0BAAA;CtBwwLD;AwDhsLD;EACE,cAAA;EACA,iCAAA;CxDksLD;AwD9rLD;EACE,iBAAA;CxDgsLD;AwD5rLD;EACE,UAAA;EACA,wBAAA;CxD8rLD;AwDzrLD;EACE,mBAAA;EACA,cAAA;CxD2rLD;AwDvrLD;EACE,cAAA;EACA,kBAAA;EACA,8BAAA;CxDyrLD;AwD5rLD;EAQI,iBAAA;EACA,iBAAA;CxDurLH;AwDhsLD;EAaI,kBAAA;CxDsrLH;AwDnsLD;EAiBI,eAAA;CxDqrLH;AwDhrLD;EACE,mBAAA;EACA,aAAA;EACA,YAAA;EACA,aAAA;EACA,iBAAA;CxDkrLD;AwDhqLD;EAZE;IACE,aAAA;IACA,kBAAA;GxD+qLD;EwD7qLD;InDvEA,kDAAA;IACQ,0CAAA;GLuvLP;EwD5qLD;IAAY,aAAA;GxD+qLX;CACF;AwD1qLD;EAFE;IAAY,aAAA;GxDgrLX;CACF;AyD/zLD;EACE,mBAAA;EACA,cAAA;EACA,eAAA;ECRA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;EDHA,gBAAA;EnCVA,WAAA;EAGA,yBAAA;CtBs1LD;AyD30LC;EnCdA,aAAA;EAGA,0BAAA;CtB01LD;AyD90LC;EAAW,iBAAA;EAAmB,eAAA;CzDk1L/B;AyDj1LC;EAAW,iBAAA;EAAmB,eAAA;CzDq1L/B;AyDp1LC;EAAW,gBAAA;EAAmB,eAAA;CzDw1L/B;AyDv1LC;EAAW,kBAAA;EAAmB,eAAA;CzD21L/B;AyDv1LD;EACE,iBAAA;EACA,iBAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,mBAAA;CzDy1LD;AyDr1LD;EACE,mBAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;CzDu1LD;AyDn1LC;EACE,UAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,uBAAA;CzDq1LH;AyDn1LC;EACE,UAAA;EACA,WAAA;EACA,oBAAA;EACA,wBAAA;EACA,uBAAA;CzDq1LH;AyDn1LC;EACE,UAAA;EACA,UAAA;EACA,oBAAA;EACA,wBAAA;EACA,uBAAA;CzDq1LH;AyDn1LC;EACE,SAAA;EACA,QAAA;EACA,iBAAA;EACA,4BAAA;EACA,yBAAA;CzDq1LH;AyDn1LC;EACE,SAAA;EACA,SAAA;EACA,iBAAA;EACA,4BAAA;EACA,wBAAA;CzDq1LH;AyDn1LC;EACE,OAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,0BAAA;CzDq1LH;AyDn1LC;EACE,OAAA;EACA,WAAA;EACA,iBAAA;EACA,wBAAA;EACA,0BAAA;CzDq1LH;AyDn1LC;EACE,OAAA;EACA,UAAA;EACA,iBAAA;EACA,wBAAA;EACA,0BAAA;CzDq1LH;A2Dl7LD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,iBAAA;EACA,aAAA;EDXA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;ECAA,gBAAA;EAEA,uBAAA;EACA,qCAAA;UAAA,6BAAA;EACA,uBAAA;EACA,qCAAA;EACA,mBAAA;EtD8CA,kDAAA;EACQ,0CAAA;CLk5LT;A2D77LC;EAAY,kBAAA;C3Dg8Lb;A2D/7LC;EAAY,kBAAA;C3Dk8Lb;A2Dj8LC;EAAY,iBAAA;C3Do8Lb;A2Dn8LC;EAAY,mBAAA;C3Ds8Lb;A2Dn8LD;EACE,UAAA;EACA,kBAAA;EACA,gBAAA;EACA,0BAAA;EACA,iCAAA;EACA,2BAAA;C3Dq8LD;A2Dl8LD;EACE,kBAAA;C3Do8LD;A2D57LC;;EAEE,mBAAA;EACA,eAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;C3D87LH;A2D37LD;EACE,mBAAA;C3D67LD;A2D37LD;EACE,mBAAA;EACA,YAAA;C3D67LD;A2Dz7LC;EACE,UAAA;EACA,mBAAA;EACA,uBAAA;EACA,0BAAA;EACA,sCAAA;EACA,cAAA;C3D27LH;A2D17LG;EACE,aAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,uBAAA;C3D47LL;A2Dz7LC;EACE,SAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,4BAAA;EACA,wCAAA;C3D27LH;A2D17LG;EACE,aAAA;EACA,UAAA;EACA,cAAA;EACA,qBAAA;EACA,yBAAA;C3D47LL;A2Dz7LC;EACE,UAAA;EACA,mBAAA;EACA,oBAAA;EACA,6BAAA;EACA,yCAAA;EACA,WAAA;C3D27LH;A2D17LG;EACE,aAAA;EACA,SAAA;EACA,mBAAA;EACA,oBAAA;EACA,0BAAA;C3D47LL;A2Dx7LC;EACE,SAAA;EACA,aAAA;EACA,kBAAA;EACA,sBAAA;EACA,2BAAA;EACA,uCAAA;C3D07LH;A2Dz7LG;EACE,aAAA;EACA,WAAA;EACA,sBAAA;EACA,wBAAA;EACA,cAAA;C3D27LL;A4DpjMD;EACE,mBAAA;C5DsjMD;A4DnjMD;EACE,mBAAA;EACA,iBAAA;EACA,YAAA;C5DqjMD;A4DxjMD;EAMI,cAAA;EACA,mBAAA;EvD6KF,0CAAA;EACK,qCAAA;EACG,kCAAA;CLy4LT;A4D/jMD;;EAcM,eAAA;C5DqjML;A4D3hMC;EA4NF;IvD3DE,uDAAA;IAEK,6CAAA;IACG,uCAAA;IA7JR,oCAAA;IAEQ,4BAAA;IA+GR,4BAAA;IAEQ,oBAAA;GL86LP;E4DzjMG;;IvDmHJ,2CAAA;IACQ,mCAAA;IuDjHF,QAAA;G5D4jML;E4D1jMG;;IvD8GJ,4CAAA;IACQ,oCAAA;IuD5GF,QAAA;G5D6jML;E4D3jMG;;;IvDyGJ,wCAAA;IACQ,gCAAA;IuDtGF,QAAA;G5D8jML;CACF;A4DpmMD;;;EA6CI,eAAA;C5D4jMH;A4DzmMD;EAiDI,QAAA;C5D2jMH;A4D5mMD;;EAsDI,mBAAA;EACA,OAAA;EACA,YAAA;C5D0jMH;A4DlnMD;EA4DI,WAAA;C5DyjMH;A4DrnMD;EA+DI,YAAA;C5DyjMH;A4DxnMD;;EAmEI,QAAA;C5DyjMH;A4D5nMD;EAuEI,YAAA;C5DwjMH;A4D/nMD;EA0EI,WAAA;C5DwjMH;A4DhjMD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EtC9FA,aAAA;EAGA,0BAAA;EsC6FA,gBAAA;EACA,YAAA;EACA,mBAAA;EACA,0CAAA;EACA,mCAAA;C5DmjMD;A4D9iMC;EdnGE,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9CopMH;A4DljMC;EACE,WAAA;EACA,SAAA;EdxGA,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9C6pMH;A4DpjMC;;EAEE,WAAA;EACA,YAAA;EACA,sBAAA;EtCvHF,aAAA;EAGA,0BAAA;CtB4qMD;A4DtlMD;;;;EAuCI,mBAAA;EACA,SAAA;EACA,kBAAA;EACA,WAAA;EACA,sBAAA;C5DqjMH;A4DhmMD;;EA+CI,UAAA;EACA,mBAAA;C5DqjMH;A4DrmMD;;EAoDI,WAAA;EACA,oBAAA;C5DqjMH;A4D1mMD;;EAyDI,YAAA;EACA,aAAA;EACA,eAAA;EACA,mBAAA;C5DqjMH;A4DhjMG;EACE,iBAAA;C5DkjML;A4D9iMG;EACE,iBAAA;C5DgjML;A4DtiMD;EACE,mBAAA;EACA,aAAA;EACA,UAAA;EACA,YAAA;EACA,WAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;C5DwiMD;A4DjjMD;EAYI,sBAAA;EACA,YAAA;EACA,aAAA;EACA,YAAA;EACA,oBAAA;EACA,uBAAA;EACA,oBAAA;EACA,gBAAA;EAWA,0BAAA;EACA,mCAAA;C5D8hMH;A4D7jMD;EAkCI,UAAA;EACA,YAAA;EACA,aAAA;EACA,uBAAA;C5D8hMH;A4DvhMD;EACE,mBAAA;EACA,UAAA;EACA,WAAA;EACA,aAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,YAAA;EACA,mBAAA;EACA,0CAAA;C5DyhMD;A4DxhMC;EACE,kBAAA;C5D0hMH;A4Dj/LD;EAhCE;;;;IAKI,YAAA;IACA,aAAA;IACA,kBAAA;IACA,gBAAA;G5DmhMH;E4D3hMD;;IAYI,mBAAA;G5DmhMH;E4D/hMD;;IAgBI,oBAAA;G5DmhMH;E4D9gMD;IACE,UAAA;IACA,WAAA;IACA,qBAAA;G5DghMD;E4D5gMD;IACE,aAAA;G5D8gMD;CACF;A6D7wMC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEE,aAAA;EACA,eAAA;C7D6yMH;A6D3yMC;;;;;;;;;;;;;;;;EACE,YAAA;C7D4zMH;AiCp0MD;E6BRE,eAAA;EACA,kBAAA;EACA,mBAAA;C9D+0MD;AiCt0MD;EACE,wBAAA;CjCw0MD;AiCt0MD;EACE,uBAAA;CjCw0MD;AiCh0MD;EACE,yBAAA;CjCk0MD;AiCh0MD;EACE,0BAAA;CjCk0MD;AiCh0MD;EACE,mBAAA;CjCk0MD;AiCh0MD;E8BzBE,YAAA;EACA,mBAAA;EACA,kBAAA;EACA,8BAAA;EACA,UAAA;C/D41MD;AiC9zMD;EACE,yBAAA;CjCg0MD;AiCzzMD;EACE,gBAAA;CjC2zMD;AgE51MD;EACE,oBAAA;ChE81MD;AgEx1MD;;;;ECdE,yBAAA;CjE42MD;AgEv1MD;;;;;;;;;;;;EAYE,yBAAA;ChEy1MD;AgEl1MD;EA6IA;IC7LE,0BAAA;GjEs4MC;EiEr4MD;IAAU,0BAAA;GjEw4MT;EiEv4MD;IAAU,8BAAA;GjE04MT;EiEz4MD;;IACU,+BAAA;GjE44MT;CACF;AgE51MD;EAwIA;IA1II,0BAAA;GhEk2MD;CACF;AgE51MD;EAmIA;IArII,2BAAA;GhEk2MD;CACF;AgE51MD;EA8HA;IAhII,iCAAA;GhEk2MD;CACF;AgE31MD;EAwHA;IC7LE,0BAAA;GjEo6MC;EiEn6MD;IAAU,0BAAA;GjEs6MT;EiEr6MD;IAAU,8BAAA;GjEw6MT;EiEv6MD;;IACU,+BAAA;GjE06MT;CACF;AgEr2MD;EAmHA;IArHI,0BAAA;GhE22MD;CACF;AgEr2MD;EA8GA;IAhHI,2BAAA;GhE22MD;CACF;AgEr2MD;EAyGA;IA3GI,iCAAA;GhE22MD;CACF;AgEp2MD;EAmGA;IC7LE,0BAAA;GjEk8MC;EiEj8MD;IAAU,0BAAA;GjEo8MT;EiEn8MD;IAAU,8BAAA;GjEs8MT;EiEr8MD;;IACU,+BAAA;GjEw8MT;CACF;AgE92MD;EA8FA;IAhGI,0BAAA;GhEo3MD;CACF;AgE92MD;EAyFA;IA3FI,2BAAA;GhEo3MD;CACF;AgE92MD;EAoFA;IAtFI,iCAAA;GhEo3MD;CACF;AgE72MD;EA8EA;IC7LE,0BAAA;GjEg+MC;EiE/9MD;IAAU,0BAAA;GjEk+MT;EiEj+MD;IAAU,8BAAA;GjEo+MT;EiEn+MD;;IACU,+BAAA;GjEs+MT;CACF;AgEv3MD;EAyEA;IA3EI,0BAAA;GhE63MD;CACF;AgEv3MD;EAoEA;IAtEI,2BAAA;GhE63MD;CACF;AgEv3MD;EA+DA;IAjEI,iCAAA;GhE63MD;CACF;AgEt3MD;EAyDA;ICrLE,yBAAA;GjEs/MC;CACF;AgEt3MD;EAoDA;ICrLE,yBAAA;GjE2/MC;CACF;AgEt3MD;EA+CA;ICrLE,yBAAA;GjEggNC;CACF;AgEt3MD;EA0CA;ICrLE,yBAAA;GjEqgNC;CACF;AgEn3MD;ECnJE,yBAAA;CjEygND;AgEh3MD;EA4BA;IC7LE,0BAAA;GjEqhNC;EiEphND;IAAU,0BAAA;GjEuhNT;EiEthND;IAAU,8BAAA;GjEyhNT;EiExhND;;IACU,+BAAA;GjE2hNT;CACF;AgE93MD;EACE,yBAAA;ChEg4MD;AgE33MD;EAqBA;IAvBI,0BAAA;GhEi4MD;CACF;AgE/3MD;EACE,yBAAA;ChEi4MD;AgE53MD;EAcA;IAhBI,2BAAA;GhEk4MD;CACF;AgEh4MD;EACE,yBAAA;ChEk4MD;AgE73MD;EAOA;IATI,iCAAA;GhEm4MD;CACF;AgE53MD;EACA;ICrLE,yBAAA;GjEojNC;CACF", + "file": "bootstrap.css", + "sourcesContent": [ + "/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\nhtml {\n font-family: sans-serif;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%;\n}\nbody {\n margin: 0;\n}\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block;\n vertical-align: baseline;\n}\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n[hidden],\ntemplate {\n display: none;\n}\na {\n background-color: transparent;\n}\na:active,\na:hover {\n outline: 0;\n}\nabbr[title] {\n border-bottom: 1px dotted;\n}\nb,\nstrong {\n font-weight: bold;\n}\ndfn {\n font-style: italic;\n}\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\nmark {\n background: #ff0;\n color: #000;\n}\nsmall {\n font-size: 80%;\n}\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsup {\n top: -0.5em;\n}\nsub {\n bottom: -0.25em;\n}\nimg {\n border: 0;\n}\nsvg:not(:root) {\n overflow: hidden;\n}\nfigure {\n margin: 1em 40px;\n}\nhr {\n box-sizing: content-box;\n height: 0;\n}\npre {\n overflow: auto;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit;\n font: inherit;\n margin: 0;\n}\nbutton {\n overflow: visible;\n}\nbutton,\nselect {\n text-transform: none;\n}\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button;\n cursor: pointer;\n}\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\ninput {\n line-height: normal;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box;\n padding: 0;\n}\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: textfield;\n box-sizing: content-box;\n}\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\nlegend {\n border: 0;\n padding: 0;\n}\ntextarea {\n overflow: auto;\n}\noptgroup {\n font-weight: bold;\n}\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\ntd,\nth {\n padding: 0;\n}\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important;\n box-shadow: none !important;\n text-shadow: none !important;\n }\n a,\n a:visited {\n text-decoration: underline;\n }\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n img {\n max-width: 100% !important;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n .navbar {\n display: none;\n }\n .btn > .caret,\n .dropup > .btn > .caret {\n border-top-color: #000 !important;\n }\n .label {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #ddd !important;\n }\n}\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('../fonts/glyphicons-halflings-regular.eot');\n src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\n}\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n.glyphicon-asterisk:before {\n content: \"\\002a\";\n}\n.glyphicon-plus:before {\n content: \"\\002b\";\n}\n.glyphicon-euro:before,\n.glyphicon-eur:before {\n content: \"\\20ac\";\n}\n.glyphicon-minus:before {\n content: \"\\2212\";\n}\n.glyphicon-cloud:before {\n content: \"\\2601\";\n}\n.glyphicon-envelope:before {\n content: \"\\2709\";\n}\n.glyphicon-pencil:before {\n content: \"\\270f\";\n}\n.glyphicon-glass:before {\n content: \"\\e001\";\n}\n.glyphicon-music:before {\n content: \"\\e002\";\n}\n.glyphicon-search:before {\n content: \"\\e003\";\n}\n.glyphicon-heart:before {\n content: \"\\e005\";\n}\n.glyphicon-star:before {\n content: \"\\e006\";\n}\n.glyphicon-star-empty:before {\n content: \"\\e007\";\n}\n.glyphicon-user:before {\n content: \"\\e008\";\n}\n.glyphicon-film:before {\n content: \"\\e009\";\n}\n.glyphicon-th-large:before {\n content: \"\\e010\";\n}\n.glyphicon-th:before {\n content: \"\\e011\";\n}\n.glyphicon-th-list:before {\n content: \"\\e012\";\n}\n.glyphicon-ok:before {\n content: \"\\e013\";\n}\n.glyphicon-remove:before {\n content: \"\\e014\";\n}\n.glyphicon-zoom-in:before {\n content: \"\\e015\";\n}\n.glyphicon-zoom-out:before {\n content: \"\\e016\";\n}\n.glyphicon-off:before {\n content: \"\\e017\";\n}\n.glyphicon-signal:before {\n content: \"\\e018\";\n}\n.glyphicon-cog:before {\n content: \"\\e019\";\n}\n.glyphicon-trash:before {\n content: \"\\e020\";\n}\n.glyphicon-home:before {\n content: \"\\e021\";\n}\n.glyphicon-file:before {\n content: \"\\e022\";\n}\n.glyphicon-time:before {\n content: \"\\e023\";\n}\n.glyphicon-road:before {\n content: \"\\e024\";\n}\n.glyphicon-download-alt:before {\n content: \"\\e025\";\n}\n.glyphicon-download:before {\n content: \"\\e026\";\n}\n.glyphicon-upload:before {\n content: \"\\e027\";\n}\n.glyphicon-inbox:before {\n content: \"\\e028\";\n}\n.glyphicon-play-circle:before {\n content: \"\\e029\";\n}\n.glyphicon-repeat:before {\n content: \"\\e030\";\n}\n.glyphicon-refresh:before {\n content: \"\\e031\";\n}\n.glyphicon-list-alt:before {\n content: \"\\e032\";\n}\n.glyphicon-lock:before {\n content: \"\\e033\";\n}\n.glyphicon-flag:before {\n content: \"\\e034\";\n}\n.glyphicon-headphones:before {\n content: \"\\e035\";\n}\n.glyphicon-volume-off:before {\n content: \"\\e036\";\n}\n.glyphicon-volume-down:before {\n content: \"\\e037\";\n}\n.glyphicon-volume-up:before {\n content: \"\\e038\";\n}\n.glyphicon-qrcode:before {\n content: \"\\e039\";\n}\n.glyphicon-barcode:before {\n content: \"\\e040\";\n}\n.glyphicon-tag:before {\n content: \"\\e041\";\n}\n.glyphicon-tags:before {\n content: \"\\e042\";\n}\n.glyphicon-book:before {\n content: \"\\e043\";\n}\n.glyphicon-bookmark:before {\n content: \"\\e044\";\n}\n.glyphicon-print:before {\n content: \"\\e045\";\n}\n.glyphicon-camera:before {\n content: \"\\e046\";\n}\n.glyphicon-font:before {\n content: \"\\e047\";\n}\n.glyphicon-bold:before {\n content: \"\\e048\";\n}\n.glyphicon-italic:before {\n content: \"\\e049\";\n}\n.glyphicon-text-height:before {\n content: \"\\e050\";\n}\n.glyphicon-text-width:before {\n content: \"\\e051\";\n}\n.glyphicon-align-left:before {\n content: \"\\e052\";\n}\n.glyphicon-align-center:before {\n content: \"\\e053\";\n}\n.glyphicon-align-right:before {\n content: \"\\e054\";\n}\n.glyphicon-align-justify:before {\n content: \"\\e055\";\n}\n.glyphicon-list:before {\n content: \"\\e056\";\n}\n.glyphicon-indent-left:before {\n content: \"\\e057\";\n}\n.glyphicon-indent-right:before {\n content: \"\\e058\";\n}\n.glyphicon-facetime-video:before {\n content: \"\\e059\";\n}\n.glyphicon-picture:before {\n content: \"\\e060\";\n}\n.glyphicon-map-marker:before {\n content: \"\\e062\";\n}\n.glyphicon-adjust:before {\n content: \"\\e063\";\n}\n.glyphicon-tint:before {\n content: \"\\e064\";\n}\n.glyphicon-edit:before {\n content: \"\\e065\";\n}\n.glyphicon-share:before {\n content: \"\\e066\";\n}\n.glyphicon-check:before {\n content: \"\\e067\";\n}\n.glyphicon-move:before {\n content: \"\\e068\";\n}\n.glyphicon-step-backward:before {\n content: \"\\e069\";\n}\n.glyphicon-fast-backward:before {\n content: \"\\e070\";\n}\n.glyphicon-backward:before {\n content: \"\\e071\";\n}\n.glyphicon-play:before {\n content: \"\\e072\";\n}\n.glyphicon-pause:before {\n content: \"\\e073\";\n}\n.glyphicon-stop:before {\n content: \"\\e074\";\n}\n.glyphicon-forward:before {\n content: \"\\e075\";\n}\n.glyphicon-fast-forward:before {\n content: \"\\e076\";\n}\n.glyphicon-step-forward:before {\n content: \"\\e077\";\n}\n.glyphicon-eject:before {\n content: \"\\e078\";\n}\n.glyphicon-chevron-left:before {\n content: \"\\e079\";\n}\n.glyphicon-chevron-right:before {\n content: \"\\e080\";\n}\n.glyphicon-plus-sign:before {\n content: \"\\e081\";\n}\n.glyphicon-minus-sign:before {\n content: \"\\e082\";\n}\n.glyphicon-remove-sign:before {\n content: \"\\e083\";\n}\n.glyphicon-ok-sign:before {\n content: \"\\e084\";\n}\n.glyphicon-question-sign:before {\n content: \"\\e085\";\n}\n.glyphicon-info-sign:before {\n content: \"\\e086\";\n}\n.glyphicon-screenshot:before {\n content: \"\\e087\";\n}\n.glyphicon-remove-circle:before {\n content: \"\\e088\";\n}\n.glyphicon-ok-circle:before {\n content: \"\\e089\";\n}\n.glyphicon-ban-circle:before {\n content: \"\\e090\";\n}\n.glyphicon-arrow-left:before {\n content: \"\\e091\";\n}\n.glyphicon-arrow-right:before {\n content: \"\\e092\";\n}\n.glyphicon-arrow-up:before {\n content: \"\\e093\";\n}\n.glyphicon-arrow-down:before {\n content: \"\\e094\";\n}\n.glyphicon-share-alt:before {\n content: \"\\e095\";\n}\n.glyphicon-resize-full:before {\n content: \"\\e096\";\n}\n.glyphicon-resize-small:before {\n content: \"\\e097\";\n}\n.glyphicon-exclamation-sign:before {\n content: \"\\e101\";\n}\n.glyphicon-gift:before {\n content: \"\\e102\";\n}\n.glyphicon-leaf:before {\n content: \"\\e103\";\n}\n.glyphicon-fire:before {\n content: \"\\e104\";\n}\n.glyphicon-eye-open:before {\n content: \"\\e105\";\n}\n.glyphicon-eye-close:before {\n content: \"\\e106\";\n}\n.glyphicon-warning-sign:before {\n content: \"\\e107\";\n}\n.glyphicon-plane:before {\n content: \"\\e108\";\n}\n.glyphicon-calendar:before {\n content: \"\\e109\";\n}\n.glyphicon-random:before {\n content: \"\\e110\";\n}\n.glyphicon-comment:before {\n content: \"\\e111\";\n}\n.glyphicon-magnet:before {\n content: \"\\e112\";\n}\n.glyphicon-chevron-up:before {\n content: \"\\e113\";\n}\n.glyphicon-chevron-down:before {\n content: \"\\e114\";\n}\n.glyphicon-retweet:before {\n content: \"\\e115\";\n}\n.glyphicon-shopping-cart:before {\n content: \"\\e116\";\n}\n.glyphicon-folder-close:before {\n content: \"\\e117\";\n}\n.glyphicon-folder-open:before {\n content: \"\\e118\";\n}\n.glyphicon-resize-vertical:before {\n content: \"\\e119\";\n}\n.glyphicon-resize-horizontal:before {\n content: \"\\e120\";\n}\n.glyphicon-hdd:before {\n content: \"\\e121\";\n}\n.glyphicon-bullhorn:before {\n content: \"\\e122\";\n}\n.glyphicon-bell:before {\n content: \"\\e123\";\n}\n.glyphicon-certificate:before {\n content: \"\\e124\";\n}\n.glyphicon-thumbs-up:before {\n content: \"\\e125\";\n}\n.glyphicon-thumbs-down:before {\n content: \"\\e126\";\n}\n.glyphicon-hand-right:before {\n content: \"\\e127\";\n}\n.glyphicon-hand-left:before {\n content: \"\\e128\";\n}\n.glyphicon-hand-up:before {\n content: \"\\e129\";\n}\n.glyphicon-hand-down:before {\n content: \"\\e130\";\n}\n.glyphicon-circle-arrow-right:before {\n content: \"\\e131\";\n}\n.glyphicon-circle-arrow-left:before {\n content: \"\\e132\";\n}\n.glyphicon-circle-arrow-up:before {\n content: \"\\e133\";\n}\n.glyphicon-circle-arrow-down:before {\n content: \"\\e134\";\n}\n.glyphicon-globe:before {\n content: \"\\e135\";\n}\n.glyphicon-wrench:before {\n content: \"\\e136\";\n}\n.glyphicon-tasks:before {\n content: \"\\e137\";\n}\n.glyphicon-filter:before {\n content: \"\\e138\";\n}\n.glyphicon-briefcase:before {\n content: \"\\e139\";\n}\n.glyphicon-fullscreen:before {\n content: \"\\e140\";\n}\n.glyphicon-dashboard:before {\n content: \"\\e141\";\n}\n.glyphicon-paperclip:before {\n content: \"\\e142\";\n}\n.glyphicon-heart-empty:before {\n content: \"\\e143\";\n}\n.glyphicon-link:before {\n content: \"\\e144\";\n}\n.glyphicon-phone:before {\n content: \"\\e145\";\n}\n.glyphicon-pushpin:before {\n content: \"\\e146\";\n}\n.glyphicon-usd:before {\n content: \"\\e148\";\n}\n.glyphicon-gbp:before {\n content: \"\\e149\";\n}\n.glyphicon-sort:before {\n content: \"\\e150\";\n}\n.glyphicon-sort-by-alphabet:before {\n content: \"\\e151\";\n}\n.glyphicon-sort-by-alphabet-alt:before {\n content: \"\\e152\";\n}\n.glyphicon-sort-by-order:before {\n content: \"\\e153\";\n}\n.glyphicon-sort-by-order-alt:before {\n content: \"\\e154\";\n}\n.glyphicon-sort-by-attributes:before {\n content: \"\\e155\";\n}\n.glyphicon-sort-by-attributes-alt:before {\n content: \"\\e156\";\n}\n.glyphicon-unchecked:before {\n content: \"\\e157\";\n}\n.glyphicon-expand:before {\n content: \"\\e158\";\n}\n.glyphicon-collapse-down:before {\n content: \"\\e159\";\n}\n.glyphicon-collapse-up:before {\n content: \"\\e160\";\n}\n.glyphicon-log-in:before {\n content: \"\\e161\";\n}\n.glyphicon-flash:before {\n content: \"\\e162\";\n}\n.glyphicon-log-out:before {\n content: \"\\e163\";\n}\n.glyphicon-new-window:before {\n content: \"\\e164\";\n}\n.glyphicon-record:before {\n content: \"\\e165\";\n}\n.glyphicon-save:before {\n content: \"\\e166\";\n}\n.glyphicon-open:before {\n content: \"\\e167\";\n}\n.glyphicon-saved:before {\n content: \"\\e168\";\n}\n.glyphicon-import:before {\n content: \"\\e169\";\n}\n.glyphicon-export:before {\n content: \"\\e170\";\n}\n.glyphicon-send:before {\n content: \"\\e171\";\n}\n.glyphicon-floppy-disk:before {\n content: \"\\e172\";\n}\n.glyphicon-floppy-saved:before {\n content: \"\\e173\";\n}\n.glyphicon-floppy-remove:before {\n content: \"\\e174\";\n}\n.glyphicon-floppy-save:before {\n content: \"\\e175\";\n}\n.glyphicon-floppy-open:before {\n content: \"\\e176\";\n}\n.glyphicon-credit-card:before {\n content: \"\\e177\";\n}\n.glyphicon-transfer:before {\n content: \"\\e178\";\n}\n.glyphicon-cutlery:before {\n content: \"\\e179\";\n}\n.glyphicon-header:before {\n content: \"\\e180\";\n}\n.glyphicon-compressed:before {\n content: \"\\e181\";\n}\n.glyphicon-earphone:before {\n content: \"\\e182\";\n}\n.glyphicon-phone-alt:before {\n content: \"\\e183\";\n}\n.glyphicon-tower:before {\n content: \"\\e184\";\n}\n.glyphicon-stats:before {\n content: \"\\e185\";\n}\n.glyphicon-sd-video:before {\n content: \"\\e186\";\n}\n.glyphicon-hd-video:before {\n content: \"\\e187\";\n}\n.glyphicon-subtitles:before {\n content: \"\\e188\";\n}\n.glyphicon-sound-stereo:before {\n content: \"\\e189\";\n}\n.glyphicon-sound-dolby:before {\n content: \"\\e190\";\n}\n.glyphicon-sound-5-1:before {\n content: \"\\e191\";\n}\n.glyphicon-sound-6-1:before {\n content: \"\\e192\";\n}\n.glyphicon-sound-7-1:before {\n content: \"\\e193\";\n}\n.glyphicon-copyright-mark:before {\n content: \"\\e194\";\n}\n.glyphicon-registration-mark:before {\n content: \"\\e195\";\n}\n.glyphicon-cloud-download:before {\n content: \"\\e197\";\n}\n.glyphicon-cloud-upload:before {\n content: \"\\e198\";\n}\n.glyphicon-tree-conifer:before {\n content: \"\\e199\";\n}\n.glyphicon-tree-deciduous:before {\n content: \"\\e200\";\n}\n.glyphicon-cd:before {\n content: \"\\e201\";\n}\n.glyphicon-save-file:before {\n content: \"\\e202\";\n}\n.glyphicon-open-file:before {\n content: \"\\e203\";\n}\n.glyphicon-level-up:before {\n content: \"\\e204\";\n}\n.glyphicon-copy:before {\n content: \"\\e205\";\n}\n.glyphicon-paste:before {\n content: \"\\e206\";\n}\n.glyphicon-alert:before {\n content: \"\\e209\";\n}\n.glyphicon-equalizer:before {\n content: \"\\e210\";\n}\n.glyphicon-king:before {\n content: \"\\e211\";\n}\n.glyphicon-queen:before {\n content: \"\\e212\";\n}\n.glyphicon-pawn:before {\n content: \"\\e213\";\n}\n.glyphicon-bishop:before {\n content: \"\\e214\";\n}\n.glyphicon-knight:before {\n content: \"\\e215\";\n}\n.glyphicon-baby-formula:before {\n content: \"\\e216\";\n}\n.glyphicon-tent:before {\n content: \"\\26fa\";\n}\n.glyphicon-blackboard:before {\n content: \"\\e218\";\n}\n.glyphicon-bed:before {\n content: \"\\e219\";\n}\n.glyphicon-apple:before {\n content: \"\\f8ff\";\n}\n.glyphicon-erase:before {\n content: \"\\e221\";\n}\n.glyphicon-hourglass:before {\n content: \"\\231b\";\n}\n.glyphicon-lamp:before {\n content: \"\\e223\";\n}\n.glyphicon-duplicate:before {\n content: \"\\e224\";\n}\n.glyphicon-piggy-bank:before {\n content: \"\\e225\";\n}\n.glyphicon-scissors:before {\n content: \"\\e226\";\n}\n.glyphicon-bitcoin:before {\n content: \"\\e227\";\n}\n.glyphicon-btc:before {\n content: \"\\e227\";\n}\n.glyphicon-xbt:before {\n content: \"\\e227\";\n}\n.glyphicon-yen:before {\n content: \"\\00a5\";\n}\n.glyphicon-jpy:before {\n content: \"\\00a5\";\n}\n.glyphicon-ruble:before {\n content: \"\\20bd\";\n}\n.glyphicon-rub:before {\n content: \"\\20bd\";\n}\n.glyphicon-scale:before {\n content: \"\\e230\";\n}\n.glyphicon-ice-lolly:before {\n content: \"\\e231\";\n}\n.glyphicon-ice-lolly-tasted:before {\n content: \"\\e232\";\n}\n.glyphicon-education:before {\n content: \"\\e233\";\n}\n.glyphicon-option-horizontal:before {\n content: \"\\e234\";\n}\n.glyphicon-option-vertical:before {\n content: \"\\e235\";\n}\n.glyphicon-menu-hamburger:before {\n content: \"\\e236\";\n}\n.glyphicon-modal-window:before {\n content: \"\\e237\";\n}\n.glyphicon-oil:before {\n content: \"\\e238\";\n}\n.glyphicon-grain:before {\n content: \"\\e239\";\n}\n.glyphicon-sunglasses:before {\n content: \"\\e240\";\n}\n.glyphicon-text-size:before {\n content: \"\\e241\";\n}\n.glyphicon-text-color:before {\n content: \"\\e242\";\n}\n.glyphicon-text-background:before {\n content: \"\\e243\";\n}\n.glyphicon-object-align-top:before {\n content: \"\\e244\";\n}\n.glyphicon-object-align-bottom:before {\n content: \"\\e245\";\n}\n.glyphicon-object-align-horizontal:before {\n content: \"\\e246\";\n}\n.glyphicon-object-align-left:before {\n content: \"\\e247\";\n}\n.glyphicon-object-align-vertical:before {\n content: \"\\e248\";\n}\n.glyphicon-object-align-right:before {\n content: \"\\e249\";\n}\n.glyphicon-triangle-right:before {\n content: \"\\e250\";\n}\n.glyphicon-triangle-left:before {\n content: \"\\e251\";\n}\n.glyphicon-triangle-bottom:before {\n content: \"\\e252\";\n}\n.glyphicon-triangle-top:before {\n content: \"\\e253\";\n}\n.glyphicon-console:before {\n content: \"\\e254\";\n}\n.glyphicon-superscript:before {\n content: \"\\e255\";\n}\n.glyphicon-subscript:before {\n content: \"\\e256\";\n}\n.glyphicon-menu-left:before {\n content: \"\\e257\";\n}\n.glyphicon-menu-right:before {\n content: \"\\e258\";\n}\n.glyphicon-menu-down:before {\n content: \"\\e259\";\n}\n.glyphicon-menu-up:before {\n content: \"\\e260\";\n}\n* {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n*:before,\n*:after {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-size: 14px;\n line-height: 1.42857143;\n color: #333333;\n background-color: #fff;\n}\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\na {\n color: #337ab7;\n text-decoration: none;\n}\na:hover,\na:focus {\n color: #23527c;\n text-decoration: underline;\n}\na:focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\nfigure {\n margin: 0;\n}\nimg {\n vertical-align: middle;\n}\n.img-responsive,\n.thumbnail > img,\n.thumbnail a > img,\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n display: block;\n max-width: 100%;\n height: auto;\n}\n.img-rounded {\n border-radius: 6px;\n}\n.img-thumbnail {\n padding: 4px;\n line-height: 1.42857143;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 4px;\n -webkit-transition: all 0.2s ease-in-out;\n -o-transition: all 0.2s ease-in-out;\n transition: all 0.2s ease-in-out;\n display: inline-block;\n max-width: 100%;\n height: auto;\n}\n.img-circle {\n border-radius: 50%;\n}\nhr {\n margin-top: 20px;\n margin-bottom: 20px;\n border: 0;\n border-top: 1px solid #eeeeee;\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n}\n[role=\"button\"] {\n cursor: pointer;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n font-family: inherit;\n font-weight: 500;\n line-height: 1.1;\n color: inherit;\n}\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n font-weight: normal;\n line-height: 1;\n color: #777777;\n}\nh1,\n.h1,\nh2,\n.h2,\nh3,\n.h3 {\n margin-top: 20px;\n margin-bottom: 10px;\n}\nh1 small,\n.h1 small,\nh2 small,\n.h2 small,\nh3 small,\n.h3 small,\nh1 .small,\n.h1 .small,\nh2 .small,\n.h2 .small,\nh3 .small,\n.h3 .small {\n font-size: 65%;\n}\nh4,\n.h4,\nh5,\n.h5,\nh6,\n.h6 {\n margin-top: 10px;\n margin-bottom: 10px;\n}\nh4 small,\n.h4 small,\nh5 small,\n.h5 small,\nh6 small,\n.h6 small,\nh4 .small,\n.h4 .small,\nh5 .small,\n.h5 .small,\nh6 .small,\n.h6 .small {\n font-size: 75%;\n}\nh1,\n.h1 {\n font-size: 36px;\n}\nh2,\n.h2 {\n font-size: 30px;\n}\nh3,\n.h3 {\n font-size: 24px;\n}\nh4,\n.h4 {\n font-size: 18px;\n}\nh5,\n.h5 {\n font-size: 14px;\n}\nh6,\n.h6 {\n font-size: 12px;\n}\np {\n margin: 0 0 10px;\n}\n.lead {\n margin-bottom: 20px;\n font-size: 16px;\n font-weight: 300;\n line-height: 1.4;\n}\n@media (min-width: 768px) {\n .lead {\n font-size: 21px;\n }\n}\nsmall,\n.small {\n font-size: 85%;\n}\nmark,\n.mark {\n background-color: #fcf8e3;\n padding: .2em;\n}\n.text-left {\n text-align: left;\n}\n.text-right {\n text-align: right;\n}\n.text-center {\n text-align: center;\n}\n.text-justify {\n text-align: justify;\n}\n.text-nowrap {\n white-space: nowrap;\n}\n.text-lowercase {\n text-transform: lowercase;\n}\n.text-uppercase {\n text-transform: uppercase;\n}\n.text-capitalize {\n text-transform: capitalize;\n}\n.text-muted {\n color: #777777;\n}\n.text-primary {\n color: #337ab7;\n}\na.text-primary:hover,\na.text-primary:focus {\n color: #286090;\n}\n.text-success {\n color: #3c763d;\n}\na.text-success:hover,\na.text-success:focus {\n color: #2b542c;\n}\n.text-info {\n color: #31708f;\n}\na.text-info:hover,\na.text-info:focus {\n color: #245269;\n}\n.text-warning {\n color: #8a6d3b;\n}\na.text-warning:hover,\na.text-warning:focus {\n color: #66512c;\n}\n.text-danger {\n color: #a94442;\n}\na.text-danger:hover,\na.text-danger:focus {\n color: #843534;\n}\n.bg-primary {\n color: #fff;\n background-color: #337ab7;\n}\na.bg-primary:hover,\na.bg-primary:focus {\n background-color: #286090;\n}\n.bg-success {\n background-color: #dff0d8;\n}\na.bg-success:hover,\na.bg-success:focus {\n background-color: #c1e2b3;\n}\n.bg-info {\n background-color: #d9edf7;\n}\na.bg-info:hover,\na.bg-info:focus {\n background-color: #afd9ee;\n}\n.bg-warning {\n background-color: #fcf8e3;\n}\na.bg-warning:hover,\na.bg-warning:focus {\n background-color: #f7ecb5;\n}\n.bg-danger {\n background-color: #f2dede;\n}\na.bg-danger:hover,\na.bg-danger:focus {\n background-color: #e4b9b9;\n}\n.page-header {\n padding-bottom: 9px;\n margin: 40px 0 20px;\n border-bottom: 1px solid #eeeeee;\n}\nul,\nol {\n margin-top: 0;\n margin-bottom: 10px;\n}\nul ul,\nol ul,\nul ol,\nol ol {\n margin-bottom: 0;\n}\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n.list-inline {\n padding-left: 0;\n list-style: none;\n margin-left: -5px;\n}\n.list-inline > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n}\ndl {\n margin-top: 0;\n margin-bottom: 20px;\n}\ndt,\ndd {\n line-height: 1.42857143;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0;\n}\n@media (min-width: 768px) {\n .dl-horizontal dt {\n float: left;\n width: 160px;\n clear: left;\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .dl-horizontal dd {\n margin-left: 180px;\n }\n}\nabbr[title],\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted #777777;\n}\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\nblockquote {\n padding: 10px 20px;\n margin: 0 0 20px;\n font-size: 17.5px;\n border-left: 5px solid #eeeeee;\n}\nblockquote p:last-child,\nblockquote ul:last-child,\nblockquote ol:last-child {\n margin-bottom: 0;\n}\nblockquote footer,\nblockquote small,\nblockquote .small {\n display: block;\n font-size: 80%;\n line-height: 1.42857143;\n color: #777777;\n}\nblockquote footer:before,\nblockquote small:before,\nblockquote .small:before {\n content: '\\2014 \\00A0';\n}\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid #eeeeee;\n border-left: 0;\n text-align: right;\n}\n.blockquote-reverse footer:before,\nblockquote.pull-right footer:before,\n.blockquote-reverse small:before,\nblockquote.pull-right small:before,\n.blockquote-reverse .small:before,\nblockquote.pull-right .small:before {\n content: '';\n}\n.blockquote-reverse footer:after,\nblockquote.pull-right footer:after,\n.blockquote-reverse small:after,\nblockquote.pull-right small:after,\n.blockquote-reverse .small:after,\nblockquote.pull-right .small:after {\n content: '\\00A0 \\2014';\n}\naddress {\n margin-bottom: 20px;\n font-style: normal;\n line-height: 1.42857143;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: #c7254e;\n background-color: #f9f2f4;\n border-radius: 4px;\n}\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: #fff;\n background-color: #333;\n border-radius: 3px;\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n}\npre {\n display: block;\n padding: 9.5px;\n margin: 0 0 10px;\n font-size: 13px;\n line-height: 1.42857143;\n word-break: break-all;\n word-wrap: break-word;\n color: #333333;\n background-color: #f5f5f5;\n border: 1px solid #ccc;\n border-radius: 4px;\n}\npre code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n}\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n.container {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n@media (min-width: 768px) {\n .container {\n width: 750px;\n }\n}\n@media (min-width: 992px) {\n .container {\n width: 970px;\n }\n}\n@media (min-width: 1200px) {\n .container {\n width: 1170px;\n }\n}\n.container-fluid {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n.row {\n margin-left: -15px;\n margin-right: -15px;\n}\n.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\n position: relative;\n min-height: 1px;\n padding-left: 15px;\n padding-right: 15px;\n}\n.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\n float: left;\n}\n.col-xs-12 {\n width: 100%;\n}\n.col-xs-11 {\n width: 91.66666667%;\n}\n.col-xs-10 {\n width: 83.33333333%;\n}\n.col-xs-9 {\n width: 75%;\n}\n.col-xs-8 {\n width: 66.66666667%;\n}\n.col-xs-7 {\n width: 58.33333333%;\n}\n.col-xs-6 {\n width: 50%;\n}\n.col-xs-5 {\n width: 41.66666667%;\n}\n.col-xs-4 {\n width: 33.33333333%;\n}\n.col-xs-3 {\n width: 25%;\n}\n.col-xs-2 {\n width: 16.66666667%;\n}\n.col-xs-1 {\n width: 8.33333333%;\n}\n.col-xs-pull-12 {\n right: 100%;\n}\n.col-xs-pull-11 {\n right: 91.66666667%;\n}\n.col-xs-pull-10 {\n right: 83.33333333%;\n}\n.col-xs-pull-9 {\n right: 75%;\n}\n.col-xs-pull-8 {\n right: 66.66666667%;\n}\n.col-xs-pull-7 {\n right: 58.33333333%;\n}\n.col-xs-pull-6 {\n right: 50%;\n}\n.col-xs-pull-5 {\n right: 41.66666667%;\n}\n.col-xs-pull-4 {\n right: 33.33333333%;\n}\n.col-xs-pull-3 {\n right: 25%;\n}\n.col-xs-pull-2 {\n right: 16.66666667%;\n}\n.col-xs-pull-1 {\n right: 8.33333333%;\n}\n.col-xs-pull-0 {\n right: auto;\n}\n.col-xs-push-12 {\n left: 100%;\n}\n.col-xs-push-11 {\n left: 91.66666667%;\n}\n.col-xs-push-10 {\n left: 83.33333333%;\n}\n.col-xs-push-9 {\n left: 75%;\n}\n.col-xs-push-8 {\n left: 66.66666667%;\n}\n.col-xs-push-7 {\n left: 58.33333333%;\n}\n.col-xs-push-6 {\n left: 50%;\n}\n.col-xs-push-5 {\n left: 41.66666667%;\n}\n.col-xs-push-4 {\n left: 33.33333333%;\n}\n.col-xs-push-3 {\n left: 25%;\n}\n.col-xs-push-2 {\n left: 16.66666667%;\n}\n.col-xs-push-1 {\n left: 8.33333333%;\n}\n.col-xs-push-0 {\n left: auto;\n}\n.col-xs-offset-12 {\n margin-left: 100%;\n}\n.col-xs-offset-11 {\n margin-left: 91.66666667%;\n}\n.col-xs-offset-10 {\n margin-left: 83.33333333%;\n}\n.col-xs-offset-9 {\n margin-left: 75%;\n}\n.col-xs-offset-8 {\n margin-left: 66.66666667%;\n}\n.col-xs-offset-7 {\n margin-left: 58.33333333%;\n}\n.col-xs-offset-6 {\n margin-left: 50%;\n}\n.col-xs-offset-5 {\n margin-left: 41.66666667%;\n}\n.col-xs-offset-4 {\n margin-left: 33.33333333%;\n}\n.col-xs-offset-3 {\n margin-left: 25%;\n}\n.col-xs-offset-2 {\n margin-left: 16.66666667%;\n}\n.col-xs-offset-1 {\n margin-left: 8.33333333%;\n}\n.col-xs-offset-0 {\n margin-left: 0%;\n}\n@media (min-width: 768px) {\n .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {\n float: left;\n }\n .col-sm-12 {\n width: 100%;\n }\n .col-sm-11 {\n width: 91.66666667%;\n }\n .col-sm-10 {\n width: 83.33333333%;\n }\n .col-sm-9 {\n width: 75%;\n }\n .col-sm-8 {\n width: 66.66666667%;\n }\n .col-sm-7 {\n width: 58.33333333%;\n }\n .col-sm-6 {\n width: 50%;\n }\n .col-sm-5 {\n width: 41.66666667%;\n }\n .col-sm-4 {\n width: 33.33333333%;\n }\n .col-sm-3 {\n width: 25%;\n }\n .col-sm-2 {\n width: 16.66666667%;\n }\n .col-sm-1 {\n width: 8.33333333%;\n }\n .col-sm-pull-12 {\n right: 100%;\n }\n .col-sm-pull-11 {\n right: 91.66666667%;\n }\n .col-sm-pull-10 {\n right: 83.33333333%;\n }\n .col-sm-pull-9 {\n right: 75%;\n }\n .col-sm-pull-8 {\n right: 66.66666667%;\n }\n .col-sm-pull-7 {\n right: 58.33333333%;\n }\n .col-sm-pull-6 {\n right: 50%;\n }\n .col-sm-pull-5 {\n right: 41.66666667%;\n }\n .col-sm-pull-4 {\n right: 33.33333333%;\n }\n .col-sm-pull-3 {\n right: 25%;\n }\n .col-sm-pull-2 {\n right: 16.66666667%;\n }\n .col-sm-pull-1 {\n right: 8.33333333%;\n }\n .col-sm-pull-0 {\n right: auto;\n }\n .col-sm-push-12 {\n left: 100%;\n }\n .col-sm-push-11 {\n left: 91.66666667%;\n }\n .col-sm-push-10 {\n left: 83.33333333%;\n }\n .col-sm-push-9 {\n left: 75%;\n }\n .col-sm-push-8 {\n left: 66.66666667%;\n }\n .col-sm-push-7 {\n left: 58.33333333%;\n }\n .col-sm-push-6 {\n left: 50%;\n }\n .col-sm-push-5 {\n left: 41.66666667%;\n }\n .col-sm-push-4 {\n left: 33.33333333%;\n }\n .col-sm-push-3 {\n left: 25%;\n }\n .col-sm-push-2 {\n left: 16.66666667%;\n }\n .col-sm-push-1 {\n left: 8.33333333%;\n }\n .col-sm-push-0 {\n left: auto;\n }\n .col-sm-offset-12 {\n margin-left: 100%;\n }\n .col-sm-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-sm-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-sm-offset-9 {\n margin-left: 75%;\n }\n .col-sm-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-sm-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-sm-offset-6 {\n margin-left: 50%;\n }\n .col-sm-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-sm-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-sm-offset-3 {\n margin-left: 25%;\n }\n .col-sm-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-sm-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-sm-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 992px) {\n .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {\n float: left;\n }\n .col-md-12 {\n width: 100%;\n }\n .col-md-11 {\n width: 91.66666667%;\n }\n .col-md-10 {\n width: 83.33333333%;\n }\n .col-md-9 {\n width: 75%;\n }\n .col-md-8 {\n width: 66.66666667%;\n }\n .col-md-7 {\n width: 58.33333333%;\n }\n .col-md-6 {\n width: 50%;\n }\n .col-md-5 {\n width: 41.66666667%;\n }\n .col-md-4 {\n width: 33.33333333%;\n }\n .col-md-3 {\n width: 25%;\n }\n .col-md-2 {\n width: 16.66666667%;\n }\n .col-md-1 {\n width: 8.33333333%;\n }\n .col-md-pull-12 {\n right: 100%;\n }\n .col-md-pull-11 {\n right: 91.66666667%;\n }\n .col-md-pull-10 {\n right: 83.33333333%;\n }\n .col-md-pull-9 {\n right: 75%;\n }\n .col-md-pull-8 {\n right: 66.66666667%;\n }\n .col-md-pull-7 {\n right: 58.33333333%;\n }\n .col-md-pull-6 {\n right: 50%;\n }\n .col-md-pull-5 {\n right: 41.66666667%;\n }\n .col-md-pull-4 {\n right: 33.33333333%;\n }\n .col-md-pull-3 {\n right: 25%;\n }\n .col-md-pull-2 {\n right: 16.66666667%;\n }\n .col-md-pull-1 {\n right: 8.33333333%;\n }\n .col-md-pull-0 {\n right: auto;\n }\n .col-md-push-12 {\n left: 100%;\n }\n .col-md-push-11 {\n left: 91.66666667%;\n }\n .col-md-push-10 {\n left: 83.33333333%;\n }\n .col-md-push-9 {\n left: 75%;\n }\n .col-md-push-8 {\n left: 66.66666667%;\n }\n .col-md-push-7 {\n left: 58.33333333%;\n }\n .col-md-push-6 {\n left: 50%;\n }\n .col-md-push-5 {\n left: 41.66666667%;\n }\n .col-md-push-4 {\n left: 33.33333333%;\n }\n .col-md-push-3 {\n left: 25%;\n }\n .col-md-push-2 {\n left: 16.66666667%;\n }\n .col-md-push-1 {\n left: 8.33333333%;\n }\n .col-md-push-0 {\n left: auto;\n }\n .col-md-offset-12 {\n margin-left: 100%;\n }\n .col-md-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-md-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-md-offset-9 {\n margin-left: 75%;\n }\n .col-md-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-md-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-md-offset-6 {\n margin-left: 50%;\n }\n .col-md-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-md-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-md-offset-3 {\n margin-left: 25%;\n }\n .col-md-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-md-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-md-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 1200px) {\n .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {\n float: left;\n }\n .col-lg-12 {\n width: 100%;\n }\n .col-lg-11 {\n width: 91.66666667%;\n }\n .col-lg-10 {\n width: 83.33333333%;\n }\n .col-lg-9 {\n width: 75%;\n }\n .col-lg-8 {\n width: 66.66666667%;\n }\n .col-lg-7 {\n width: 58.33333333%;\n }\n .col-lg-6 {\n width: 50%;\n }\n .col-lg-5 {\n width: 41.66666667%;\n }\n .col-lg-4 {\n width: 33.33333333%;\n }\n .col-lg-3 {\n width: 25%;\n }\n .col-lg-2 {\n width: 16.66666667%;\n }\n .col-lg-1 {\n width: 8.33333333%;\n }\n .col-lg-pull-12 {\n right: 100%;\n }\n .col-lg-pull-11 {\n right: 91.66666667%;\n }\n .col-lg-pull-10 {\n right: 83.33333333%;\n }\n .col-lg-pull-9 {\n right: 75%;\n }\n .col-lg-pull-8 {\n right: 66.66666667%;\n }\n .col-lg-pull-7 {\n right: 58.33333333%;\n }\n .col-lg-pull-6 {\n right: 50%;\n }\n .col-lg-pull-5 {\n right: 41.66666667%;\n }\n .col-lg-pull-4 {\n right: 33.33333333%;\n }\n .col-lg-pull-3 {\n right: 25%;\n }\n .col-lg-pull-2 {\n right: 16.66666667%;\n }\n .col-lg-pull-1 {\n right: 8.33333333%;\n }\n .col-lg-pull-0 {\n right: auto;\n }\n .col-lg-push-12 {\n left: 100%;\n }\n .col-lg-push-11 {\n left: 91.66666667%;\n }\n .col-lg-push-10 {\n left: 83.33333333%;\n }\n .col-lg-push-9 {\n left: 75%;\n }\n .col-lg-push-8 {\n left: 66.66666667%;\n }\n .col-lg-push-7 {\n left: 58.33333333%;\n }\n .col-lg-push-6 {\n left: 50%;\n }\n .col-lg-push-5 {\n left: 41.66666667%;\n }\n .col-lg-push-4 {\n left: 33.33333333%;\n }\n .col-lg-push-3 {\n left: 25%;\n }\n .col-lg-push-2 {\n left: 16.66666667%;\n }\n .col-lg-push-1 {\n left: 8.33333333%;\n }\n .col-lg-push-0 {\n left: auto;\n }\n .col-lg-offset-12 {\n margin-left: 100%;\n }\n .col-lg-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-lg-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-lg-offset-9 {\n margin-left: 75%;\n }\n .col-lg-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-lg-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-lg-offset-6 {\n margin-left: 50%;\n }\n .col-lg-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-lg-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-lg-offset-3 {\n margin-left: 25%;\n }\n .col-lg-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-lg-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-lg-offset-0 {\n margin-left: 0%;\n }\n}\ntable {\n background-color: transparent;\n}\ncaption {\n padding-top: 8px;\n padding-bottom: 8px;\n color: #777777;\n text-align: left;\n}\nth {\n text-align: left;\n}\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n padding: 8px;\n line-height: 1.42857143;\n vertical-align: top;\n border-top: 1px solid #ddd;\n}\n.table > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid #ddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n border-top: 0;\n}\n.table > tbody + tbody {\n border-top: 2px solid #ddd;\n}\n.table .table {\n background-color: #fff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n padding: 5px;\n}\n.table-bordered {\n border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n border-bottom-width: 2px;\n}\n.table-striped > tbody > tr:nth-of-type(odd) {\n background-color: #f9f9f9;\n}\n.table-hover > tbody > tr:hover {\n background-color: #f5f5f5;\n}\ntable col[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-column;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-cell;\n}\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n background-color: #f5f5f5;\n}\n.table-hover > tbody > tr > td.active:hover,\n.table-hover > tbody > tr > th.active:hover,\n.table-hover > tbody > tr.active:hover > td,\n.table-hover > tbody > tr:hover > .active,\n.table-hover > tbody > tr.active:hover > th {\n background-color: #e8e8e8;\n}\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n background-color: #dff0d8;\n}\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td,\n.table-hover > tbody > tr:hover > .success,\n.table-hover > tbody > tr.success:hover > th {\n background-color: #d0e9c6;\n}\n.table > thead > tr > td.info,\n.table > tbody > tr > td.info,\n.table > tfoot > tr > td.info,\n.table > thead > tr > th.info,\n.table > tbody > tr > th.info,\n.table > tfoot > tr > th.info,\n.table > thead > tr.info > td,\n.table > tbody > tr.info > td,\n.table > tfoot > tr.info > td,\n.table > thead > tr.info > th,\n.table > tbody > tr.info > th,\n.table > tfoot > tr.info > th {\n background-color: #d9edf7;\n}\n.table-hover > tbody > tr > td.info:hover,\n.table-hover > tbody > tr > th.info:hover,\n.table-hover > tbody > tr.info:hover > td,\n.table-hover > tbody > tr:hover > .info,\n.table-hover > tbody > tr.info:hover > th {\n background-color: #c4e3f3;\n}\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n background-color: #fcf8e3;\n}\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td,\n.table-hover > tbody > tr:hover > .warning,\n.table-hover > tbody > tr.warning:hover > th {\n background-color: #faf2cc;\n}\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n background-color: #f2dede;\n}\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td,\n.table-hover > tbody > tr:hover > .danger,\n.table-hover > tbody > tr.danger:hover > th {\n background-color: #ebcccc;\n}\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%;\n}\n@media screen and (max-width: 767px) {\n .table-responsive {\n width: 100%;\n margin-bottom: 15px;\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid #ddd;\n }\n .table-responsive > .table {\n margin-bottom: 0;\n }\n .table-responsive > .table > thead > tr > th,\n .table-responsive > .table > tbody > tr > th,\n .table-responsive > .table > tfoot > tr > th,\n .table-responsive > .table > thead > tr > td,\n .table-responsive > .table > tbody > tr > td,\n .table-responsive > .table > tfoot > tr > td {\n white-space: nowrap;\n }\n .table-responsive > .table-bordered {\n border: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:first-child,\n .table-responsive > .table-bordered > tbody > tr > th:first-child,\n .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n .table-responsive > .table-bordered > thead > tr > td:first-child,\n .table-responsive > .table-bordered > tbody > tr > td:first-child,\n .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:last-child,\n .table-responsive > .table-bordered > tbody > tr > th:last-child,\n .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n .table-responsive > .table-bordered > thead > tr > td:last-child,\n .table-responsive > .table-bordered > tbody > tr > td:last-child,\n .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n }\n .table-responsive > .table-bordered > tbody > tr:last-child > th,\n .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n .table-responsive > .table-bordered > tbody > tr:last-child > td,\n .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n border-bottom: 0;\n }\n}\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n min-width: 0;\n}\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: 20px;\n font-size: 21px;\n line-height: inherit;\n color: #333333;\n border: 0;\n border-bottom: 1px solid #e5e5e5;\n}\nlabel {\n display: inline-block;\n max-width: 100%;\n margin-bottom: 5px;\n font-weight: bold;\n}\ninput[type=\"search\"] {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9;\n line-height: normal;\n}\ninput[type=\"file\"] {\n display: block;\n}\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\nselect[multiple],\nselect[size] {\n height: auto;\n}\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\noutput {\n display: block;\n padding-top: 7px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n}\n.form-control {\n display: block;\n width: 100%;\n height: 34px;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n background-color: #fff;\n background-image: none;\n border: 1px solid #ccc;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n}\n.form-control:focus {\n border-color: #66afe9;\n outline: 0;\n -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n.form-control::-moz-placeholder {\n color: #999;\n opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n color: #999;\n}\n.form-control::-webkit-input-placeholder {\n color: #999;\n}\n.form-control::-ms-expand {\n border: 0;\n background-color: transparent;\n}\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n background-color: #eeeeee;\n opacity: 1;\n}\n.form-control[disabled],\nfieldset[disabled] .form-control {\n cursor: not-allowed;\n}\ntextarea.form-control {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"].form-control,\n input[type=\"time\"].form-control,\n input[type=\"datetime-local\"].form-control,\n input[type=\"month\"].form-control {\n line-height: 34px;\n }\n input[type=\"date\"].input-sm,\n input[type=\"time\"].input-sm,\n input[type=\"datetime-local\"].input-sm,\n input[type=\"month\"].input-sm,\n .input-group-sm input[type=\"date\"],\n .input-group-sm input[type=\"time\"],\n .input-group-sm input[type=\"datetime-local\"],\n .input-group-sm input[type=\"month\"] {\n line-height: 30px;\n }\n input[type=\"date\"].input-lg,\n input[type=\"time\"].input-lg,\n input[type=\"datetime-local\"].input-lg,\n input[type=\"month\"].input-lg,\n .input-group-lg input[type=\"date\"],\n .input-group-lg input[type=\"time\"],\n .input-group-lg input[type=\"datetime-local\"],\n .input-group-lg input[type=\"month\"] {\n line-height: 46px;\n }\n}\n.form-group {\n margin-bottom: 15px;\n}\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.radio label,\n.checkbox label {\n min-height: 20px;\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px;\n}\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px;\n}\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"].disabled,\ninput[type=\"checkbox\"].disabled,\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"] {\n cursor: not-allowed;\n}\n.radio-inline.disabled,\n.checkbox-inline.disabled,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox-inline {\n cursor: not-allowed;\n}\n.radio.disabled label,\n.checkbox.disabled label,\nfieldset[disabled] .radio label,\nfieldset[disabled] .checkbox label {\n cursor: not-allowed;\n}\n.form-control-static {\n padding-top: 7px;\n padding-bottom: 7px;\n margin-bottom: 0;\n min-height: 34px;\n}\n.form-control-static.input-lg,\n.form-control-static.input-sm {\n padding-left: 0;\n padding-right: 0;\n}\n.input-sm {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-sm {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-sm,\nselect[multiple].input-sm {\n height: auto;\n}\n.form-group-sm .form-control {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.form-group-sm select.form-control {\n height: 30px;\n line-height: 30px;\n}\n.form-group-sm textarea.form-control,\n.form-group-sm select[multiple].form-control {\n height: auto;\n}\n.form-group-sm .form-control-static {\n height: 30px;\n min-height: 32px;\n padding: 6px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.input-lg {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-lg {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-lg,\nselect[multiple].input-lg {\n height: auto;\n}\n.form-group-lg .form-control {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.form-group-lg select.form-control {\n height: 46px;\n line-height: 46px;\n}\n.form-group-lg textarea.form-control,\n.form-group-lg select[multiple].form-control {\n height: auto;\n}\n.form-group-lg .form-control-static {\n height: 46px;\n min-height: 38px;\n padding: 11px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.has-feedback {\n position: relative;\n}\n.has-feedback .form-control {\n padding-right: 42.5px;\n}\n.form-control-feedback {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n display: block;\n width: 34px;\n height: 34px;\n line-height: 34px;\n text-align: center;\n pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n width: 46px;\n height: 46px;\n line-height: 46px;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n width: 30px;\n height: 30px;\n line-height: 30px;\n}\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline,\n.has-success.radio label,\n.has-success.checkbox label,\n.has-success.radio-inline label,\n.has-success.checkbox-inline label {\n color: #3c763d;\n}\n.has-success .form-control {\n border-color: #3c763d;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-success .form-control:focus {\n border-color: #2b542c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n}\n.has-success .input-group-addon {\n color: #3c763d;\n border-color: #3c763d;\n background-color: #dff0d8;\n}\n.has-success .form-control-feedback {\n color: #3c763d;\n}\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline,\n.has-warning.radio label,\n.has-warning.checkbox label,\n.has-warning.radio-inline label,\n.has-warning.checkbox-inline label {\n color: #8a6d3b;\n}\n.has-warning .form-control {\n border-color: #8a6d3b;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-warning .form-control:focus {\n border-color: #66512c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n}\n.has-warning .input-group-addon {\n color: #8a6d3b;\n border-color: #8a6d3b;\n background-color: #fcf8e3;\n}\n.has-warning .form-control-feedback {\n color: #8a6d3b;\n}\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline,\n.has-error.radio label,\n.has-error.checkbox label,\n.has-error.radio-inline label,\n.has-error.checkbox-inline label {\n color: #a94442;\n}\n.has-error .form-control {\n border-color: #a94442;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-error .form-control:focus {\n border-color: #843534;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n.has-error .input-group-addon {\n color: #a94442;\n border-color: #a94442;\n background-color: #f2dede;\n}\n.has-error .form-control-feedback {\n color: #a94442;\n}\n.has-feedback label ~ .form-control-feedback {\n top: 25px;\n}\n.has-feedback label.sr-only ~ .form-control-feedback {\n top: 0;\n}\n.help-block {\n display: block;\n margin-top: 5px;\n margin-bottom: 10px;\n color: #737373;\n}\n@media (min-width: 768px) {\n .form-inline .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-static {\n display: inline-block;\n }\n .form-inline .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .form-inline .input-group .input-group-addon,\n .form-inline .input-group .input-group-btn,\n .form-inline .input-group .form-control {\n width: auto;\n }\n .form-inline .input-group > .form-control {\n width: 100%;\n }\n .form-inline .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio,\n .form-inline .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio label,\n .form-inline .checkbox label {\n padding-left: 0;\n }\n .form-inline .radio input[type=\"radio\"],\n .form-inline .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .form-inline .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n margin-top: 0;\n margin-bottom: 0;\n padding-top: 7px;\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n min-height: 27px;\n}\n.form-horizontal .form-group {\n margin-left: -15px;\n margin-right: -15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .control-label {\n text-align: right;\n margin-bottom: 0;\n padding-top: 7px;\n }\n}\n.form-horizontal .has-feedback .form-control-feedback {\n right: 15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-lg .control-label {\n padding-top: 11px;\n font-size: 18px;\n }\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-sm .control-label {\n padding-top: 6px;\n font-size: 12px;\n }\n}\n.btn {\n display: inline-block;\n margin-bottom: 0;\n font-weight: normal;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none;\n border: 1px solid transparent;\n white-space: nowrap;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n border-radius: 4px;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n.btn:focus,\n.btn:active:focus,\n.btn.active:focus,\n.btn.focus,\n.btn:active.focus,\n.btn.active.focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n color: #333;\n text-decoration: none;\n}\n.btn:active,\n.btn.active {\n outline: 0;\n background-image: none;\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n cursor: not-allowed;\n opacity: 0.65;\n filter: alpha(opacity=65);\n -webkit-box-shadow: none;\n box-shadow: none;\n}\na.btn.disabled,\nfieldset[disabled] a.btn {\n pointer-events: none;\n}\n.btn-default {\n color: #333;\n background-color: #fff;\n border-color: #ccc;\n}\n.btn-default:focus,\n.btn-default.focus {\n color: #333;\n background-color: #e6e6e6;\n border-color: #8c8c8c;\n}\n.btn-default:hover {\n color: #333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n color: #333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active:hover,\n.btn-default.active:hover,\n.open > .dropdown-toggle.btn-default:hover,\n.btn-default:active:focus,\n.btn-default.active:focus,\n.open > .dropdown-toggle.btn-default:focus,\n.btn-default:active.focus,\n.btn-default.active.focus,\n.open > .dropdown-toggle.btn-default.focus {\n color: #333;\n background-color: #d4d4d4;\n border-color: #8c8c8c;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n background-image: none;\n}\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus {\n background-color: #fff;\n border-color: #ccc;\n}\n.btn-default .badge {\n color: #fff;\n background-color: #333;\n}\n.btn-primary {\n color: #fff;\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary:focus,\n.btn-primary.focus {\n color: #fff;\n background-color: #286090;\n border-color: #122b40;\n}\n.btn-primary:hover {\n color: #fff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n color: #fff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active:hover,\n.btn-primary.active:hover,\n.open > .dropdown-toggle.btn-primary:hover,\n.btn-primary:active:focus,\n.btn-primary.active:focus,\n.open > .dropdown-toggle.btn-primary:focus,\n.btn-primary:active.focus,\n.btn-primary.active.focus,\n.open > .dropdown-toggle.btn-primary.focus {\n color: #fff;\n background-color: #204d74;\n border-color: #122b40;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n background-image: none;\n}\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus {\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.btn-success {\n color: #fff;\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success:focus,\n.btn-success.focus {\n color: #fff;\n background-color: #449d44;\n border-color: #255625;\n}\n.btn-success:hover {\n color: #fff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n color: #fff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active:hover,\n.btn-success.active:hover,\n.open > .dropdown-toggle.btn-success:hover,\n.btn-success:active:focus,\n.btn-success.active:focus,\n.open > .dropdown-toggle.btn-success:focus,\n.btn-success:active.focus,\n.btn-success.active.focus,\n.open > .dropdown-toggle.btn-success.focus {\n color: #fff;\n background-color: #398439;\n border-color: #255625;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n background-image: none;\n}\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus {\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success .badge {\n color: #5cb85c;\n background-color: #fff;\n}\n.btn-info {\n color: #fff;\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info:focus,\n.btn-info.focus {\n color: #fff;\n background-color: #31b0d5;\n border-color: #1b6d85;\n}\n.btn-info:hover {\n color: #fff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n color: #fff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active:hover,\n.btn-info.active:hover,\n.open > .dropdown-toggle.btn-info:hover,\n.btn-info:active:focus,\n.btn-info.active:focus,\n.open > .dropdown-toggle.btn-info:focus,\n.btn-info:active.focus,\n.btn-info.active.focus,\n.open > .dropdown-toggle.btn-info.focus {\n color: #fff;\n background-color: #269abc;\n border-color: #1b6d85;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n background-image: none;\n}\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus {\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info .badge {\n color: #5bc0de;\n background-color: #fff;\n}\n.btn-warning {\n color: #fff;\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning:focus,\n.btn-warning.focus {\n color: #fff;\n background-color: #ec971f;\n border-color: #985f0d;\n}\n.btn-warning:hover {\n color: #fff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n color: #fff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active:hover,\n.btn-warning.active:hover,\n.open > .dropdown-toggle.btn-warning:hover,\n.btn-warning:active:focus,\n.btn-warning.active:focus,\n.open > .dropdown-toggle.btn-warning:focus,\n.btn-warning:active.focus,\n.btn-warning.active.focus,\n.open > .dropdown-toggle.btn-warning.focus {\n color: #fff;\n background-color: #d58512;\n border-color: #985f0d;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n background-image: none;\n}\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus {\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning .badge {\n color: #f0ad4e;\n background-color: #fff;\n}\n.btn-danger {\n color: #fff;\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger:focus,\n.btn-danger.focus {\n color: #fff;\n background-color: #c9302c;\n border-color: #761c19;\n}\n.btn-danger:hover {\n color: #fff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n color: #fff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active:hover,\n.btn-danger.active:hover,\n.open > .dropdown-toggle.btn-danger:hover,\n.btn-danger:active:focus,\n.btn-danger.active:focus,\n.open > .dropdown-toggle.btn-danger:focus,\n.btn-danger:active.focus,\n.btn-danger.active.focus,\n.open > .dropdown-toggle.btn-danger.focus {\n color: #fff;\n background-color: #ac2925;\n border-color: #761c19;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n background-image: none;\n}\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus {\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger .badge {\n color: #d9534f;\n background-color: #fff;\n}\n.btn-link {\n color: #337ab7;\n font-weight: normal;\n border-radius: 0;\n}\n.btn-link,\n.btn-link:active,\n.btn-link.active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n background-color: transparent;\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n border-color: transparent;\n}\n.btn-link:hover,\n.btn-link:focus {\n color: #23527c;\n text-decoration: underline;\n background-color: transparent;\n}\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n color: #777777;\n text-decoration: none;\n}\n.btn-lg,\n.btn-group-lg > .btn {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.btn-sm,\n.btn-group-sm > .btn {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-xs,\n.btn-group-xs > .btn {\n padding: 1px 5px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-block {\n display: block;\n width: 100%;\n}\n.btn-block + .btn-block {\n margin-top: 5px;\n}\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n.fade {\n opacity: 0;\n -webkit-transition: opacity 0.15s linear;\n -o-transition: opacity 0.15s linear;\n transition: opacity 0.15s linear;\n}\n.fade.in {\n opacity: 1;\n}\n.collapse {\n display: none;\n}\n.collapse.in {\n display: block;\n}\ntr.collapse.in {\n display: table-row;\n}\ntbody.collapse.in {\n display: table-row-group;\n}\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n -webkit-transition-property: height, visibility;\n transition-property: height, visibility;\n -webkit-transition-duration: 0.35s;\n transition-duration: 0.35s;\n -webkit-transition-timing-function: ease;\n transition-timing-function: ease;\n}\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: 4px dashed;\n border-top: 4px solid \\9;\n border-right: 4px solid transparent;\n border-left: 4px solid transparent;\n}\n.dropup,\n.dropdown {\n position: relative;\n}\n.dropdown-toggle:focus {\n outline: 0;\n}\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0;\n list-style: none;\n font-size: 14px;\n text-align: left;\n background-color: #fff;\n border: 1px solid #ccc;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 4px;\n -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n background-clip: padding-box;\n}\n.dropdown-menu.pull-right {\n right: 0;\n left: auto;\n}\n.dropdown-menu .divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.dropdown-menu > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: 1.42857143;\n color: #333333;\n white-space: nowrap;\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n text-decoration: none;\n color: #262626;\n background-color: #f5f5f5;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n background-color: #337ab7;\n}\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n color: #777777;\n}\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n cursor: not-allowed;\n}\n.open > .dropdown-menu {\n display: block;\n}\n.open > a {\n outline: 0;\n}\n.dropdown-menu-right {\n left: auto;\n right: 0;\n}\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: 12px;\n line-height: 1.42857143;\n color: #777777;\n white-space: nowrap;\n}\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: 990;\n}\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n border-top: 0;\n border-bottom: 4px dashed;\n border-bottom: 4px solid \\9;\n content: \"\";\n}\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n}\n@media (min-width: 768px) {\n .navbar-right .dropdown-menu {\n left: auto;\n right: 0;\n }\n .navbar-right .dropdown-menu-left {\n left: 0;\n right: auto;\n }\n}\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n float: left;\n}\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n z-index: 2;\n}\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n margin-left: -1px;\n}\n.btn-toolbar {\n margin-left: -5px;\n}\n.btn-toolbar .btn,\n.btn-toolbar .btn-group,\n.btn-toolbar .input-group {\n float: left;\n}\n.btn-toolbar > .btn,\n.btn-toolbar > .btn-group,\n.btn-toolbar > .input-group {\n margin-left: 5px;\n}\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n.btn-group.open .dropdown-toggle {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-group.open .dropdown-toggle.btn-link {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn .caret {\n margin-left: 0;\n}\n.btn-lg .caret {\n border-width: 5px 5px 0;\n border-bottom-width: 0;\n}\n.dropup .btn-lg .caret {\n border-width: 0 5px 5px;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n.btn-group-vertical > .btn-group > .btn {\n float: none;\n}\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n}\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n}\n.btn-group-justified > .btn-group .btn {\n width: 100%;\n}\n.btn-group-justified > .btn-group .dropdown-menu {\n left: auto;\n}\n[data-toggle=\"buttons\"] > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn input[type=\"checkbox\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n.input-group {\n position: relative;\n display: table;\n border-collapse: separate;\n}\n.input-group[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n}\n.input-group .form-control {\n position: relative;\n z-index: 2;\n float: left;\n width: 100%;\n margin-bottom: 0;\n}\n.input-group .form-control:focus {\n z-index: 3;\n}\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn,\nselect[multiple].input-group-lg > .form-control,\nselect[multiple].input-group-lg > .input-group-addon,\nselect[multiple].input-group-lg > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn,\nselect[multiple].input-group-sm > .form-control,\nselect[multiple].input-group-sm > .input-group-addon,\nselect[multiple].input-group-sm > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n}\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle;\n}\n.input-group-addon {\n padding: 6px 12px;\n font-size: 14px;\n font-weight: normal;\n line-height: 1;\n color: #555555;\n text-align: center;\n background-color: #eeeeee;\n border: 1px solid #ccc;\n border-radius: 4px;\n}\n.input-group-addon.input-sm {\n padding: 5px 10px;\n font-size: 12px;\n border-radius: 3px;\n}\n.input-group-addon.input-lg {\n padding: 10px 16px;\n font-size: 18px;\n border-radius: 6px;\n}\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n margin-top: 0;\n}\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n.input-group-btn {\n position: relative;\n font-size: 0;\n white-space: nowrap;\n}\n.input-group-btn > .btn {\n position: relative;\n}\n.input-group-btn > .btn + .btn {\n margin-left: -1px;\n}\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:focus,\n.input-group-btn > .btn:active {\n z-index: 2;\n}\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group {\n margin-right: -1px;\n}\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group {\n z-index: 2;\n margin-left: -1px;\n}\n.nav {\n margin-bottom: 0;\n padding-left: 0;\n list-style: none;\n}\n.nav > li {\n position: relative;\n display: block;\n}\n.nav > li > a {\n position: relative;\n display: block;\n padding: 10px 15px;\n}\n.nav > li > a:hover,\n.nav > li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.nav > li.disabled > a {\n color: #777777;\n}\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n color: #777777;\n text-decoration: none;\n background-color: transparent;\n cursor: not-allowed;\n}\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n background-color: #eeeeee;\n border-color: #337ab7;\n}\n.nav .nav-divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.nav > li > a > img {\n max-width: none;\n}\n.nav-tabs {\n border-bottom: 1px solid #ddd;\n}\n.nav-tabs > li {\n float: left;\n margin-bottom: -1px;\n}\n.nav-tabs > li > a {\n margin-right: 2px;\n line-height: 1.42857143;\n border: 1px solid transparent;\n border-radius: 4px 4px 0 0;\n}\n.nav-tabs > li > a:hover {\n border-color: #eeeeee #eeeeee #ddd;\n}\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n color: #555555;\n background-color: #fff;\n border: 1px solid #ddd;\n border-bottom-color: transparent;\n cursor: default;\n}\n.nav-tabs.nav-justified {\n width: 100%;\n border-bottom: 0;\n}\n.nav-tabs.nav-justified > li {\n float: none;\n}\n.nav-tabs.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-tabs.nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs.nav-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li > a {\n border-bottom: 1px solid #ddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs.nav-justified > .active > a,\n .nav-tabs.nav-justified > .active > a:hover,\n .nav-tabs.nav-justified > .active > a:focus {\n border-bottom-color: #fff;\n }\n}\n.nav-pills > li {\n float: left;\n}\n.nav-pills > li > a {\n border-radius: 4px;\n}\n.nav-pills > li + li {\n margin-left: 2px;\n}\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n color: #fff;\n background-color: #337ab7;\n}\n.nav-stacked > li {\n float: none;\n}\n.nav-stacked > li + li {\n margin-top: 2px;\n margin-left: 0;\n}\n.nav-justified {\n width: 100%;\n}\n.nav-justified > li {\n float: none;\n}\n.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs-justified {\n border-bottom: 0;\n}\n.nav-tabs-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n .nav-tabs-justified > li > a {\n border-bottom: 1px solid #ddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs-justified > .active > a,\n .nav-tabs-justified > .active > a:hover,\n .nav-tabs-justified > .active > a:focus {\n border-bottom-color: #fff;\n }\n}\n.tab-content > .tab-pane {\n display: none;\n}\n.tab-content > .active {\n display: block;\n}\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar {\n position: relative;\n min-height: 50px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n}\n@media (min-width: 768px) {\n .navbar {\n border-radius: 4px;\n }\n}\n@media (min-width: 768px) {\n .navbar-header {\n float: left;\n }\n}\n.navbar-collapse {\n overflow-x: visible;\n padding-right: 15px;\n padding-left: 15px;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n -webkit-overflow-scrolling: touch;\n}\n.navbar-collapse.in {\n overflow-y: auto;\n}\n@media (min-width: 768px) {\n .navbar-collapse {\n width: auto;\n border-top: 0;\n box-shadow: none;\n }\n .navbar-collapse.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0;\n overflow: visible !important;\n }\n .navbar-collapse.in {\n overflow-y: visible;\n }\n .navbar-fixed-top .navbar-collapse,\n .navbar-static-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n padding-left: 0;\n padding-right: 0;\n }\n}\n.navbar-fixed-top .navbar-collapse,\n.navbar-fixed-bottom .navbar-collapse {\n max-height: 340px;\n}\n@media (max-device-width: 480px) and (orientation: landscape) {\n .navbar-fixed-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n max-height: 200px;\n }\n}\n.container > .navbar-header,\n.container-fluid > .navbar-header,\n.container > .navbar-collapse,\n.container-fluid > .navbar-collapse {\n margin-right: -15px;\n margin-left: -15px;\n}\n@media (min-width: 768px) {\n .container > .navbar-header,\n .container-fluid > .navbar-header,\n .container > .navbar-collapse,\n .container-fluid > .navbar-collapse {\n margin-right: 0;\n margin-left: 0;\n }\n}\n.navbar-static-top {\n z-index: 1000;\n border-width: 0 0 1px;\n}\n@media (min-width: 768px) {\n .navbar-static-top {\n border-radius: 0;\n }\n}\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n@media (min-width: 768px) {\n .navbar-fixed-top,\n .navbar-fixed-bottom {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0;\n border-width: 1px 0 0;\n}\n.navbar-brand {\n float: left;\n padding: 15px 15px;\n font-size: 18px;\n line-height: 20px;\n height: 50px;\n}\n.navbar-brand:hover,\n.navbar-brand:focus {\n text-decoration: none;\n}\n.navbar-brand > img {\n display: block;\n}\n@media (min-width: 768px) {\n .navbar > .container .navbar-brand,\n .navbar > .container-fluid .navbar-brand {\n margin-left: -15px;\n }\n}\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: 15px;\n padding: 9px 10px;\n margin-top: 8px;\n margin-bottom: 8px;\n background-color: transparent;\n background-image: none;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.navbar-toggle:focus {\n outline: 0;\n}\n.navbar-toggle .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n}\n.navbar-toggle .icon-bar + .icon-bar {\n margin-top: 4px;\n}\n@media (min-width: 768px) {\n .navbar-toggle {\n display: none;\n }\n}\n.navbar-nav {\n margin: 7.5px -15px;\n}\n.navbar-nav > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: 20px;\n}\n@media (max-width: 767px) {\n .navbar-nav .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n }\n .navbar-nav .open .dropdown-menu > li > a,\n .navbar-nav .open .dropdown-menu .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n .navbar-nav .open .dropdown-menu > li > a {\n line-height: 20px;\n }\n .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-nav .open .dropdown-menu > li > a:focus {\n background-image: none;\n }\n}\n@media (min-width: 768px) {\n .navbar-nav {\n float: left;\n margin: 0;\n }\n .navbar-nav > li {\n float: left;\n }\n .navbar-nav > li > a {\n padding-top: 15px;\n padding-bottom: 15px;\n }\n}\n.navbar-form {\n margin-left: -15px;\n margin-right: -15px;\n padding: 10px 15px;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n margin-top: 8px;\n margin-bottom: 8px;\n}\n@media (min-width: 768px) {\n .navbar-form .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .navbar-form .form-control-static {\n display: inline-block;\n }\n .navbar-form .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .navbar-form .input-group .input-group-addon,\n .navbar-form .input-group .input-group-btn,\n .navbar-form .input-group .form-control {\n width: auto;\n }\n .navbar-form .input-group > .form-control {\n width: 100%;\n }\n .navbar-form .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio,\n .navbar-form .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio label,\n .navbar-form .checkbox label {\n padding-left: 0;\n }\n .navbar-form .radio input[type=\"radio\"],\n .navbar-form .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .navbar-form .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n@media (max-width: 767px) {\n .navbar-form .form-group {\n margin-bottom: 5px;\n }\n .navbar-form .form-group:last-child {\n margin-bottom: 0;\n }\n}\n@media (min-width: 768px) {\n .navbar-form {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n -webkit-box-shadow: none;\n box-shadow: none;\n }\n}\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.navbar-btn {\n margin-top: 8px;\n margin-bottom: 8px;\n}\n.navbar-btn.btn-sm {\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.navbar-btn.btn-xs {\n margin-top: 14px;\n margin-bottom: 14px;\n}\n.navbar-text {\n margin-top: 15px;\n margin-bottom: 15px;\n}\n@media (min-width: 768px) {\n .navbar-text {\n float: left;\n margin-left: 15px;\n margin-right: 15px;\n }\n}\n@media (min-width: 768px) {\n .navbar-left {\n float: left !important;\n }\n .navbar-right {\n float: right !important;\n margin-right: -15px;\n }\n .navbar-right ~ .navbar-right {\n margin-right: 0;\n }\n}\n.navbar-default {\n background-color: #f8f8f8;\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-brand {\n color: #777;\n}\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n color: #5e5e5e;\n background-color: transparent;\n}\n.navbar-default .navbar-text {\n color: #777;\n}\n.navbar-default .navbar-nav > li > a {\n color: #777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n color: #333;\n background-color: transparent;\n}\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n color: #555;\n background-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n color: #ccc;\n background-color: transparent;\n}\n.navbar-default .navbar-toggle {\n border-color: #ddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n background-color: #ddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n background-color: #888;\n}\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n background-color: #e7e7e7;\n color: #555;\n}\n@media (max-width: 767px) {\n .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n color: #777;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #333;\n background-color: transparent;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #555;\n background-color: #e7e7e7;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #ccc;\n background-color: transparent;\n }\n}\n.navbar-default .navbar-link {\n color: #777;\n}\n.navbar-default .navbar-link:hover {\n color: #333;\n}\n.navbar-default .btn-link {\n color: #777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n color: #333;\n}\n.navbar-default .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-default .btn-link:hover,\n.navbar-default .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-default .btn-link:focus {\n color: #ccc;\n}\n.navbar-inverse {\n background-color: #222;\n border-color: #080808;\n}\n.navbar-inverse .navbar-brand {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n color: #fff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-text {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n color: #fff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n color: #fff;\n background-color: #080808;\n}\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n color: #444;\n background-color: transparent;\n}\n.navbar-inverse .navbar-toggle {\n border-color: #333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n background-color: #333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n background-color: #fff;\n}\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n border-color: #101010;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n background-color: #080808;\n color: #fff;\n}\n@media (max-width: 767px) {\n .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n border-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n color: #9d9d9d;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #fff;\n background-color: transparent;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #444;\n background-color: transparent;\n }\n}\n.navbar-inverse .navbar-link {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n color: #fff;\n}\n.navbar-inverse .btn-link {\n color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n color: #fff;\n}\n.navbar-inverse .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-inverse .btn-link:focus {\n color: #444;\n}\n.breadcrumb {\n padding: 8px 15px;\n margin-bottom: 20px;\n list-style: none;\n background-color: #f5f5f5;\n border-radius: 4px;\n}\n.breadcrumb > li {\n display: inline-block;\n}\n.breadcrumb > li + li:before {\n content: \"/\\00a0\";\n padding: 0 5px;\n color: #ccc;\n}\n.breadcrumb > .active {\n color: #777777;\n}\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: 20px 0;\n border-radius: 4px;\n}\n.pagination > li {\n display: inline;\n}\n.pagination > li > a,\n.pagination > li > span {\n position: relative;\n float: left;\n padding: 6px 12px;\n line-height: 1.42857143;\n text-decoration: none;\n color: #337ab7;\n background-color: #fff;\n border: 1px solid #ddd;\n margin-left: -1px;\n}\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n margin-left: 0;\n border-bottom-left-radius: 4px;\n border-top-left-radius: 4px;\n}\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n border-bottom-right-radius: 4px;\n border-top-right-radius: 4px;\n}\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n z-index: 2;\n color: #23527c;\n background-color: #eeeeee;\n border-color: #ddd;\n}\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n z-index: 3;\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n cursor: default;\n}\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n color: #777777;\n background-color: #fff;\n border-color: #ddd;\n cursor: not-allowed;\n}\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n border-bottom-left-radius: 6px;\n border-top-left-radius: 6px;\n}\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n border-bottom-right-radius: 6px;\n border-top-right-radius: 6px;\n}\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n border-bottom-left-radius: 3px;\n border-top-left-radius: 3px;\n}\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n border-bottom-right-radius: 3px;\n border-top-right-radius: 3px;\n}\n.pager {\n padding-left: 0;\n margin: 20px 0;\n list-style: none;\n text-align: center;\n}\n.pager li {\n display: inline;\n}\n.pager li > a,\n.pager li > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 15px;\n}\n.pager li > a:hover,\n.pager li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.pager .next > a,\n.pager .next > span {\n float: right;\n}\n.pager .previous > a,\n.pager .previous > span {\n float: left;\n}\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n color: #777777;\n background-color: #fff;\n cursor: not-allowed;\n}\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n}\na.label:hover,\na.label:focus {\n color: #fff;\n text-decoration: none;\n cursor: pointer;\n}\n.label:empty {\n display: none;\n}\n.btn .label {\n position: relative;\n top: -1px;\n}\n.label-default {\n background-color: #777777;\n}\n.label-default[href]:hover,\n.label-default[href]:focus {\n background-color: #5e5e5e;\n}\n.label-primary {\n background-color: #337ab7;\n}\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n background-color: #286090;\n}\n.label-success {\n background-color: #5cb85c;\n}\n.label-success[href]:hover,\n.label-success[href]:focus {\n background-color: #449d44;\n}\n.label-info {\n background-color: #5bc0de;\n}\n.label-info[href]:hover,\n.label-info[href]:focus {\n background-color: #31b0d5;\n}\n.label-warning {\n background-color: #f0ad4e;\n}\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n background-color: #ec971f;\n}\n.label-danger {\n background-color: #d9534f;\n}\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n background-color: #c9302c;\n}\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: 12px;\n font-weight: bold;\n color: #fff;\n line-height: 1;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: #777777;\n border-radius: 10px;\n}\n.badge:empty {\n display: none;\n}\n.btn .badge {\n position: relative;\n top: -1px;\n}\n.btn-xs .badge,\n.btn-group-xs > .btn .badge {\n top: 0;\n padding: 1px 5px;\n}\na.badge:hover,\na.badge:focus {\n color: #fff;\n text-decoration: none;\n cursor: pointer;\n}\n.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.list-group-item > .badge {\n float: right;\n}\n.list-group-item > .badge + .badge {\n margin-right: 5px;\n}\n.nav-pills > li > a > .badge {\n margin-left: 3px;\n}\n.jumbotron {\n padding-top: 30px;\n padding-bottom: 30px;\n margin-bottom: 30px;\n color: inherit;\n background-color: #eeeeee;\n}\n.jumbotron h1,\n.jumbotron .h1 {\n color: inherit;\n}\n.jumbotron p {\n margin-bottom: 15px;\n font-size: 21px;\n font-weight: 200;\n}\n.jumbotron > hr {\n border-top-color: #d5d5d5;\n}\n.container .jumbotron,\n.container-fluid .jumbotron {\n border-radius: 6px;\n padding-left: 15px;\n padding-right: 15px;\n}\n.jumbotron .container {\n max-width: 100%;\n}\n@media screen and (min-width: 768px) {\n .jumbotron {\n padding-top: 48px;\n padding-bottom: 48px;\n }\n .container .jumbotron,\n .container-fluid .jumbotron {\n padding-left: 60px;\n padding-right: 60px;\n }\n .jumbotron h1,\n .jumbotron .h1 {\n font-size: 63px;\n }\n}\n.thumbnail {\n display: block;\n padding: 4px;\n margin-bottom: 20px;\n line-height: 1.42857143;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 4px;\n -webkit-transition: border 0.2s ease-in-out;\n -o-transition: border 0.2s ease-in-out;\n transition: border 0.2s ease-in-out;\n}\n.thumbnail > img,\n.thumbnail a > img {\n margin-left: auto;\n margin-right: auto;\n}\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n border-color: #337ab7;\n}\n.thumbnail .caption {\n padding: 9px;\n color: #333333;\n}\n.alert {\n padding: 15px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.alert h4 {\n margin-top: 0;\n color: inherit;\n}\n.alert .alert-link {\n font-weight: bold;\n}\n.alert > p,\n.alert > ul {\n margin-bottom: 0;\n}\n.alert > p + p {\n margin-top: 5px;\n}\n.alert-dismissable,\n.alert-dismissible {\n padding-right: 35px;\n}\n.alert-dismissable .close,\n.alert-dismissible .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n}\n.alert-success {\n background-color: #dff0d8;\n border-color: #d6e9c6;\n color: #3c763d;\n}\n.alert-success hr {\n border-top-color: #c9e2b3;\n}\n.alert-success .alert-link {\n color: #2b542c;\n}\n.alert-info {\n background-color: #d9edf7;\n border-color: #bce8f1;\n color: #31708f;\n}\n.alert-info hr {\n border-top-color: #a6e1ec;\n}\n.alert-info .alert-link {\n color: #245269;\n}\n.alert-warning {\n background-color: #fcf8e3;\n border-color: #faebcc;\n color: #8a6d3b;\n}\n.alert-warning hr {\n border-top-color: #f7e1b5;\n}\n.alert-warning .alert-link {\n color: #66512c;\n}\n.alert-danger {\n background-color: #f2dede;\n border-color: #ebccd1;\n color: #a94442;\n}\n.alert-danger hr {\n border-top-color: #e4b9c0;\n}\n.alert-danger .alert-link {\n color: #843534;\n}\n@-webkit-keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n@keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n.progress {\n overflow: hidden;\n height: 20px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: 12px;\n line-height: 20px;\n color: #fff;\n text-align: center;\n background-color: #337ab7;\n -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n -webkit-transition: width 0.6s ease;\n -o-transition: width 0.6s ease;\n transition: width 0.6s ease;\n}\n.progress-striped .progress-bar,\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 40px 40px;\n}\n.progress.active .progress-bar,\n.progress-bar.active {\n -webkit-animation: progress-bar-stripes 2s linear infinite;\n -o-animation: progress-bar-stripes 2s linear infinite;\n animation: progress-bar-stripes 2s linear infinite;\n}\n.progress-bar-success {\n background-color: #5cb85c;\n}\n.progress-striped .progress-bar-success {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-info {\n background-color: #5bc0de;\n}\n.progress-striped .progress-bar-info {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-warning {\n background-color: #f0ad4e;\n}\n.progress-striped .progress-bar-warning {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-danger {\n background-color: #d9534f;\n}\n.progress-striped .progress-bar-danger {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.media {\n margin-top: 15px;\n}\n.media:first-child {\n margin-top: 0;\n}\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n.media-body {\n width: 10000px;\n}\n.media-object {\n display: block;\n}\n.media-object.img-thumbnail {\n max-width: none;\n}\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n.media-middle {\n vertical-align: middle;\n}\n.media-bottom {\n vertical-align: bottom;\n}\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n.list-group {\n margin-bottom: 20px;\n padding-left: 0;\n}\n.list-group-item {\n position: relative;\n display: block;\n padding: 10px 15px;\n margin-bottom: -1px;\n background-color: #fff;\n border: 1px solid #ddd;\n}\n.list-group-item:first-child {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n}\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\na.list-group-item,\nbutton.list-group-item {\n color: #555;\n}\na.list-group-item .list-group-item-heading,\nbutton.list-group-item .list-group-item-heading {\n color: #333;\n}\na.list-group-item:hover,\nbutton.list-group-item:hover,\na.list-group-item:focus,\nbutton.list-group-item:focus {\n text-decoration: none;\n color: #555;\n background-color: #f5f5f5;\n}\nbutton.list-group-item {\n width: 100%;\n text-align: left;\n}\n.list-group-item.disabled,\n.list-group-item.disabled:hover,\n.list-group-item.disabled:focus {\n background-color: #eeeeee;\n color: #777777;\n cursor: not-allowed;\n}\n.list-group-item.disabled .list-group-item-heading,\n.list-group-item.disabled:hover .list-group-item-heading,\n.list-group-item.disabled:focus .list-group-item-heading {\n color: inherit;\n}\n.list-group-item.disabled .list-group-item-text,\n.list-group-item.disabled:hover .list-group-item-text,\n.list-group-item.disabled:focus .list-group-item-text {\n color: #777777;\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n z-index: 2;\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading,\n.list-group-item.active .list-group-item-heading > small,\n.list-group-item.active:hover .list-group-item-heading > small,\n.list-group-item.active:focus .list-group-item-heading > small,\n.list-group-item.active .list-group-item-heading > .small,\n.list-group-item.active:hover .list-group-item-heading > .small,\n.list-group-item.active:focus .list-group-item-heading > .small {\n color: inherit;\n}\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n color: #c7ddef;\n}\n.list-group-item-success {\n color: #3c763d;\n background-color: #dff0d8;\n}\na.list-group-item-success,\nbutton.list-group-item-success {\n color: #3c763d;\n}\na.list-group-item-success .list-group-item-heading,\nbutton.list-group-item-success .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-success:hover,\nbutton.list-group-item-success:hover,\na.list-group-item-success:focus,\nbutton.list-group-item-success:focus {\n color: #3c763d;\n background-color: #d0e9c6;\n}\na.list-group-item-success.active,\nbutton.list-group-item-success.active,\na.list-group-item-success.active:hover,\nbutton.list-group-item-success.active:hover,\na.list-group-item-success.active:focus,\nbutton.list-group-item-success.active:focus {\n color: #fff;\n background-color: #3c763d;\n border-color: #3c763d;\n}\n.list-group-item-info {\n color: #31708f;\n background-color: #d9edf7;\n}\na.list-group-item-info,\nbutton.list-group-item-info {\n color: #31708f;\n}\na.list-group-item-info .list-group-item-heading,\nbutton.list-group-item-info .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-info:hover,\nbutton.list-group-item-info:hover,\na.list-group-item-info:focus,\nbutton.list-group-item-info:focus {\n color: #31708f;\n background-color: #c4e3f3;\n}\na.list-group-item-info.active,\nbutton.list-group-item-info.active,\na.list-group-item-info.active:hover,\nbutton.list-group-item-info.active:hover,\na.list-group-item-info.active:focus,\nbutton.list-group-item-info.active:focus {\n color: #fff;\n background-color: #31708f;\n border-color: #31708f;\n}\n.list-group-item-warning {\n color: #8a6d3b;\n background-color: #fcf8e3;\n}\na.list-group-item-warning,\nbutton.list-group-item-warning {\n color: #8a6d3b;\n}\na.list-group-item-warning .list-group-item-heading,\nbutton.list-group-item-warning .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-warning:hover,\nbutton.list-group-item-warning:hover,\na.list-group-item-warning:focus,\nbutton.list-group-item-warning:focus {\n color: #8a6d3b;\n background-color: #faf2cc;\n}\na.list-group-item-warning.active,\nbutton.list-group-item-warning.active,\na.list-group-item-warning.active:hover,\nbutton.list-group-item-warning.active:hover,\na.list-group-item-warning.active:focus,\nbutton.list-group-item-warning.active:focus {\n color: #fff;\n background-color: #8a6d3b;\n border-color: #8a6d3b;\n}\n.list-group-item-danger {\n color: #a94442;\n background-color: #f2dede;\n}\na.list-group-item-danger,\nbutton.list-group-item-danger {\n color: #a94442;\n}\na.list-group-item-danger .list-group-item-heading,\nbutton.list-group-item-danger .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-danger:hover,\nbutton.list-group-item-danger:hover,\na.list-group-item-danger:focus,\nbutton.list-group-item-danger:focus {\n color: #a94442;\n background-color: #ebcccc;\n}\na.list-group-item-danger.active,\nbutton.list-group-item-danger.active,\na.list-group-item-danger.active:hover,\nbutton.list-group-item-danger.active:hover,\na.list-group-item-danger.active:focus,\nbutton.list-group-item-danger.active:focus {\n color: #fff;\n background-color: #a94442;\n border-color: #a94442;\n}\n.list-group-item-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.list-group-item-text {\n margin-bottom: 0;\n line-height: 1.3;\n}\n.panel {\n margin-bottom: 20px;\n background-color: #fff;\n border: 1px solid transparent;\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.panel-body {\n padding: 15px;\n}\n.panel-heading {\n padding: 10px 15px;\n border-bottom: 1px solid transparent;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel-heading > .dropdown .dropdown-toggle {\n color: inherit;\n}\n.panel-title {\n margin-top: 0;\n margin-bottom: 0;\n font-size: 16px;\n color: inherit;\n}\n.panel-title > a,\n.panel-title > small,\n.panel-title > .small,\n.panel-title > small > a,\n.panel-title > .small > a {\n color: inherit;\n}\n.panel-footer {\n padding: 10px 15px;\n background-color: #f5f5f5;\n border-top: 1px solid #ddd;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .list-group,\n.panel > .panel-collapse > .list-group {\n margin-bottom: 0;\n}\n.panel > .list-group .list-group-item,\n.panel > .panel-collapse > .list-group .list-group-item {\n border-width: 1px 0;\n border-radius: 0;\n}\n.panel > .list-group:first-child .list-group-item:first-child,\n.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {\n border-top: 0;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .list-group:last-child .list-group-item:last-child,\n.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {\n border-bottom: 0;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.panel-heading + .list-group .list-group-item:first-child {\n border-top-width: 0;\n}\n.list-group + .panel-footer {\n border-top-width: 0;\n}\n.panel > .table,\n.panel > .table-responsive > .table,\n.panel > .panel-collapse > .table {\n margin-bottom: 0;\n}\n.panel > .table caption,\n.panel > .table-responsive > .table caption,\n.panel > .panel-collapse > .table caption {\n padding-left: 15px;\n padding-right: 15px;\n}\n.panel > .table:first-child,\n.panel > .table-responsive:first-child > .table:first-child {\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\n border-top-right-radius: 3px;\n}\n.panel > .table:last-child,\n.panel > .table-responsive:last-child > .table:last-child {\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {\n border-bottom-left-radius: 3px;\n border-bottom-right-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\n border-bottom-right-radius: 3px;\n}\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive,\n.panel > .table + .panel-body,\n.panel > .table-responsive + .panel-body {\n border-top: 1px solid #ddd;\n}\n.panel > .table > tbody:first-child > tr:first-child th,\n.panel > .table > tbody:first-child > tr:first-child td {\n border-top: 0;\n}\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n border: 0;\n}\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n}\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n}\n.panel > .table-bordered > thead > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\n.panel > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-bordered > thead > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\n.panel > .table-bordered > tbody > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\n border-bottom: 0;\n}\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\n border-bottom: 0;\n}\n.panel > .table-responsive {\n border: 0;\n margin-bottom: 0;\n}\n.panel-group {\n margin-bottom: 20px;\n}\n.panel-group .panel {\n margin-bottom: 0;\n border-radius: 4px;\n}\n.panel-group .panel + .panel {\n margin-top: 5px;\n}\n.panel-group .panel-heading {\n border-bottom: 0;\n}\n.panel-group .panel-heading + .panel-collapse > .panel-body,\n.panel-group .panel-heading + .panel-collapse > .list-group {\n border-top: 1px solid #ddd;\n}\n.panel-group .panel-footer {\n border-top: 0;\n}\n.panel-group .panel-footer + .panel-collapse .panel-body {\n border-bottom: 1px solid #ddd;\n}\n.panel-default {\n border-color: #ddd;\n}\n.panel-default > .panel-heading {\n color: #333333;\n background-color: #f5f5f5;\n border-color: #ddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ddd;\n}\n.panel-default > .panel-heading .badge {\n color: #f5f5f5;\n background-color: #333333;\n}\n.panel-default > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ddd;\n}\n.panel-primary {\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #337ab7;\n}\n.panel-primary > .panel-heading .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.panel-primary > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #337ab7;\n}\n.panel-success {\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading {\n color: #3c763d;\n background-color: #dff0d8;\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #d6e9c6;\n}\n.panel-success > .panel-heading .badge {\n color: #dff0d8;\n background-color: #3c763d;\n}\n.panel-success > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #d6e9c6;\n}\n.panel-info {\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading {\n color: #31708f;\n background-color: #d9edf7;\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #bce8f1;\n}\n.panel-info > .panel-heading .badge {\n color: #d9edf7;\n background-color: #31708f;\n}\n.panel-info > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #bce8f1;\n}\n.panel-warning {\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading {\n color: #8a6d3b;\n background-color: #fcf8e3;\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #faebcc;\n}\n.panel-warning > .panel-heading .badge {\n color: #fcf8e3;\n background-color: #8a6d3b;\n}\n.panel-warning > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #faebcc;\n}\n.panel-danger {\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading {\n color: #a94442;\n background-color: #f2dede;\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ebccd1;\n}\n.panel-danger > .panel-heading .badge {\n color: #f2dede;\n background-color: #a94442;\n}\n.panel-danger > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ebccd1;\n}\n.embed-responsive {\n position: relative;\n display: block;\n height: 0;\n padding: 0;\n overflow: hidden;\n}\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n height: 100%;\n width: 100%;\n border: 0;\n}\n.embed-responsive-16by9 {\n padding-bottom: 56.25%;\n}\n.embed-responsive-4by3 {\n padding-bottom: 75%;\n}\n.well {\n min-height: 20px;\n padding: 19px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border: 1px solid #e3e3e3;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.well blockquote {\n border-color: #ddd;\n border-color: rgba(0, 0, 0, 0.15);\n}\n.well-lg {\n padding: 24px;\n border-radius: 6px;\n}\n.well-sm {\n padding: 9px;\n border-radius: 3px;\n}\n.close {\n float: right;\n font-size: 21px;\n font-weight: bold;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n opacity: 0.2;\n filter: alpha(opacity=20);\n}\n.close:hover,\n.close:focus {\n color: #000;\n text-decoration: none;\n cursor: pointer;\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\nbutton.close {\n padding: 0;\n cursor: pointer;\n background: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n.modal-open {\n overflow: hidden;\n}\n.modal {\n display: none;\n overflow: hidden;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n -webkit-overflow-scrolling: touch;\n outline: 0;\n}\n.modal.fade .modal-dialog {\n -webkit-transform: translate(0, -25%);\n -ms-transform: translate(0, -25%);\n -o-transform: translate(0, -25%);\n transform: translate(0, -25%);\n -webkit-transition: -webkit-transform 0.3s ease-out;\n -moz-transition: -moz-transform 0.3s ease-out;\n -o-transition: -o-transform 0.3s ease-out;\n transition: transform 0.3s ease-out;\n}\n.modal.in .modal-dialog {\n -webkit-transform: translate(0, 0);\n -ms-transform: translate(0, 0);\n -o-transform: translate(0, 0);\n transform: translate(0, 0);\n}\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 10px;\n}\n.modal-content {\n position: relative;\n background-color: #fff;\n border: 1px solid #999;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n background-clip: padding-box;\n outline: 0;\n}\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000;\n}\n.modal-backdrop.fade {\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.modal-backdrop.in {\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\n.modal-header {\n padding: 15px;\n border-bottom: 1px solid #e5e5e5;\n}\n.modal-header .close {\n margin-top: -2px;\n}\n.modal-title {\n margin: 0;\n line-height: 1.42857143;\n}\n.modal-body {\n position: relative;\n padding: 15px;\n}\n.modal-footer {\n padding: 15px;\n text-align: right;\n border-top: 1px solid #e5e5e5;\n}\n.modal-footer .btn + .btn {\n margin-left: 5px;\n margin-bottom: 0;\n}\n.modal-footer .btn-group .btn + .btn {\n margin-left: -1px;\n}\n.modal-footer .btn-block + .btn-block {\n margin-left: 0;\n}\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n@media (min-width: 768px) {\n .modal-dialog {\n width: 600px;\n margin: 30px auto;\n }\n .modal-content {\n -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n }\n .modal-sm {\n width: 300px;\n }\n}\n@media (min-width: 992px) {\n .modal-lg {\n width: 900px;\n }\n}\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 12px;\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.tooltip.in {\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.tooltip.top {\n margin-top: -3px;\n padding: 5px 0;\n}\n.tooltip.right {\n margin-left: 3px;\n padding: 0 5px;\n}\n.tooltip.bottom {\n margin-top: 3px;\n padding: 5px 0;\n}\n.tooltip.left {\n margin-left: -3px;\n padding: 0 5px;\n}\n.tooltip-inner {\n max-width: 200px;\n padding: 3px 8px;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 4px;\n}\n.tooltip-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.tooltip.top .tooltip-arrow {\n bottom: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.top-left .tooltip-arrow {\n bottom: 0;\n right: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.top-right .tooltip-arrow {\n bottom: 0;\n left: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.right .tooltip-arrow {\n top: 50%;\n left: 0;\n margin-top: -5px;\n border-width: 5px 5px 5px 0;\n border-right-color: #000;\n}\n.tooltip.left .tooltip-arrow {\n top: 50%;\n right: 0;\n margin-top: -5px;\n border-width: 5px 0 5px 5px;\n border-left-color: #000;\n}\n.tooltip.bottom .tooltip-arrow {\n top: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.tooltip.bottom-left .tooltip-arrow {\n top: 0;\n right: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.tooltip.bottom-right .tooltip-arrow {\n top: 0;\n left: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: none;\n max-width: 276px;\n padding: 1px;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 14px;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ccc;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n}\n.popover.top {\n margin-top: -10px;\n}\n.popover.right {\n margin-left: 10px;\n}\n.popover.bottom {\n margin-top: 10px;\n}\n.popover.left {\n margin-left: -10px;\n}\n.popover-title {\n margin: 0;\n padding: 8px 14px;\n font-size: 14px;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-radius: 5px 5px 0 0;\n}\n.popover-content {\n padding: 9px 14px;\n}\n.popover > .arrow,\n.popover > .arrow:after {\n position: absolute;\n display: block;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.popover > .arrow {\n border-width: 11px;\n}\n.popover > .arrow:after {\n border-width: 10px;\n content: \"\";\n}\n.popover.top > .arrow {\n left: 50%;\n margin-left: -11px;\n border-bottom-width: 0;\n border-top-color: #999999;\n border-top-color: rgba(0, 0, 0, 0.25);\n bottom: -11px;\n}\n.popover.top > .arrow:after {\n content: \" \";\n bottom: 1px;\n margin-left: -10px;\n border-bottom-width: 0;\n border-top-color: #fff;\n}\n.popover.right > .arrow {\n top: 50%;\n left: -11px;\n margin-top: -11px;\n border-left-width: 0;\n border-right-color: #999999;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n.popover.right > .arrow:after {\n content: \" \";\n left: 1px;\n bottom: -10px;\n border-left-width: 0;\n border-right-color: #fff;\n}\n.popover.bottom > .arrow {\n left: 50%;\n margin-left: -11px;\n border-top-width: 0;\n border-bottom-color: #999999;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n top: -11px;\n}\n.popover.bottom > .arrow:after {\n content: \" \";\n top: 1px;\n margin-left: -10px;\n border-top-width: 0;\n border-bottom-color: #fff;\n}\n.popover.left > .arrow {\n top: 50%;\n right: -11px;\n margin-top: -11px;\n border-right-width: 0;\n border-left-color: #999999;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n.popover.left > .arrow:after {\n content: \" \";\n right: 1px;\n border-right-width: 0;\n border-left-color: #fff;\n bottom: -10px;\n}\n.carousel {\n position: relative;\n}\n.carousel-inner {\n position: relative;\n overflow: hidden;\n width: 100%;\n}\n.carousel-inner > .item {\n display: none;\n position: relative;\n -webkit-transition: 0.6s ease-in-out left;\n -o-transition: 0.6s ease-in-out left;\n transition: 0.6s ease-in-out left;\n}\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n line-height: 1;\n}\n@media all and (transform-3d), (-webkit-transform-3d) {\n .carousel-inner > .item {\n -webkit-transition: -webkit-transform 0.6s ease-in-out;\n -moz-transition: -moz-transform 0.6s ease-in-out;\n -o-transition: -o-transform 0.6s ease-in-out;\n transition: transform 0.6s ease-in-out;\n -webkit-backface-visibility: hidden;\n -moz-backface-visibility: hidden;\n backface-visibility: hidden;\n -webkit-perspective: 1000px;\n -moz-perspective: 1000px;\n perspective: 1000px;\n }\n .carousel-inner > .item.next,\n .carousel-inner > .item.active.right {\n -webkit-transform: translate3d(100%, 0, 0);\n transform: translate3d(100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.prev,\n .carousel-inner > .item.active.left {\n -webkit-transform: translate3d(-100%, 0, 0);\n transform: translate3d(-100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.next.left,\n .carousel-inner > .item.prev.right,\n .carousel-inner > .item.active {\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n left: 0;\n }\n}\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n display: block;\n}\n.carousel-inner > .active {\n left: 0;\n}\n.carousel-inner > .next,\n.carousel-inner > .prev {\n position: absolute;\n top: 0;\n width: 100%;\n}\n.carousel-inner > .next {\n left: 100%;\n}\n.carousel-inner > .prev {\n left: -100%;\n}\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n left: 0;\n}\n.carousel-inner > .active.left {\n left: -100%;\n}\n.carousel-inner > .active.right {\n left: 100%;\n}\n.carousel-control {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n width: 15%;\n opacity: 0.5;\n filter: alpha(opacity=50);\n font-size: 20px;\n color: #fff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n background-color: rgba(0, 0, 0, 0);\n}\n.carousel-control.left {\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n}\n.carousel-control.right {\n left: auto;\n right: 0;\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n}\n.carousel-control:hover,\n.carousel-control:focus {\n outline: 0;\n color: #fff;\n text-decoration: none;\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n position: absolute;\n top: 50%;\n margin-top: -10px;\n z-index: 5;\n display: inline-block;\n}\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n left: 50%;\n margin-left: -10px;\n}\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n right: 50%;\n margin-right: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n width: 20px;\n height: 20px;\n line-height: 1;\n font-family: serif;\n}\n.carousel-control .icon-prev:before {\n content: '\\2039';\n}\n.carousel-control .icon-next:before {\n content: '\\203a';\n}\n.carousel-indicators {\n position: absolute;\n bottom: 10px;\n left: 50%;\n z-index: 15;\n width: 60%;\n margin-left: -30%;\n padding-left: 0;\n list-style: none;\n text-align: center;\n}\n.carousel-indicators li {\n display: inline-block;\n width: 10px;\n height: 10px;\n margin: 1px;\n text-indent: -999px;\n border: 1px solid #fff;\n border-radius: 10px;\n cursor: pointer;\n background-color: #000 \\9;\n background-color: rgba(0, 0, 0, 0);\n}\n.carousel-indicators .active {\n margin: 0;\n width: 12px;\n height: 12px;\n background-color: #fff;\n}\n.carousel-caption {\n position: absolute;\n left: 15%;\n right: 15%;\n bottom: 20px;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-caption .btn {\n text-shadow: none;\n}\n@media screen and (min-width: 768px) {\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-prev,\n .carousel-control .icon-next {\n width: 30px;\n height: 30px;\n margin-top: -10px;\n font-size: 30px;\n }\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .icon-prev {\n margin-left: -10px;\n }\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-next {\n margin-right: -10px;\n }\n .carousel-caption {\n left: 20%;\n right: 20%;\n padding-bottom: 30px;\n }\n .carousel-indicators {\n bottom: 20px;\n }\n}\n.clearfix:before,\n.clearfix:after,\n.dl-horizontal dd:before,\n.dl-horizontal dd:after,\n.container:before,\n.container:after,\n.container-fluid:before,\n.container-fluid:after,\n.row:before,\n.row:after,\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after,\n.btn-toolbar:before,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after,\n.nav:before,\n.nav:after,\n.navbar:before,\n.navbar:after,\n.navbar-header:before,\n.navbar-header:after,\n.navbar-collapse:before,\n.navbar-collapse:after,\n.pager:before,\n.pager:after,\n.panel-body:before,\n.panel-body:after,\n.modal-header:before,\n.modal-header:after,\n.modal-footer:before,\n.modal-footer:after {\n content: \" \";\n display: table;\n}\n.clearfix:after,\n.dl-horizontal dd:after,\n.container:after,\n.container-fluid:after,\n.row:after,\n.form-horizontal .form-group:after,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:after,\n.nav:after,\n.navbar:after,\n.navbar-header:after,\n.navbar-collapse:after,\n.pager:after,\n.panel-body:after,\n.modal-header:after,\n.modal-footer:after {\n clear: both;\n}\n.center-block {\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n.hidden {\n display: none !important;\n}\n.affix {\n position: fixed;\n}\n@-ms-viewport {\n width: device-width;\n}\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n display: none !important;\n}\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n display: none !important;\n}\n@media (max-width: 767px) {\n .visible-xs {\n display: block !important;\n }\n table.visible-xs {\n display: table !important;\n }\n tr.visible-xs {\n display: table-row !important;\n }\n th.visible-xs,\n td.visible-xs {\n display: table-cell !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-block {\n display: block !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline {\n display: inline !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm {\n display: block !important;\n }\n table.visible-sm {\n display: table !important;\n }\n tr.visible-sm {\n display: table-row !important;\n }\n th.visible-sm,\n td.visible-sm {\n display: table-cell !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-block {\n display: block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline {\n display: inline !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md {\n display: block !important;\n }\n table.visible-md {\n display: table !important;\n }\n tr.visible-md {\n display: table-row !important;\n }\n th.visible-md,\n td.visible-md {\n display: table-cell !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-block {\n display: block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline {\n display: inline !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg {\n display: block !important;\n }\n table.visible-lg {\n display: table !important;\n }\n tr.visible-lg {\n display: table-row !important;\n }\n th.visible-lg,\n td.visible-lg {\n display: table-cell !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-block {\n display: block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline {\n display: inline !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline-block {\n display: inline-block !important;\n }\n}\n@media (max-width: 767px) {\n .hidden-xs {\n display: none !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .hidden-sm {\n display: none !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .hidden-md {\n display: none !important;\n }\n}\n@media (min-width: 1200px) {\n .hidden-lg {\n display: none !important;\n }\n}\n.visible-print {\n display: none !important;\n}\n@media print {\n .visible-print {\n display: block !important;\n }\n table.visible-print {\n display: table !important;\n }\n tr.visible-print {\n display: table-row !important;\n }\n th.visible-print,\n td.visible-print {\n display: table-cell !important;\n }\n}\n.visible-print-block {\n display: none !important;\n}\n@media print {\n .visible-print-block {\n display: block !important;\n }\n}\n.visible-print-inline {\n display: none !important;\n}\n@media print {\n .visible-print-inline {\n display: inline !important;\n }\n}\n.visible-print-inline-block {\n display: none !important;\n}\n@media print {\n .visible-print-inline-block {\n display: inline-block !important;\n }\n}\n@media print {\n .hidden-print {\n display: none !important;\n }\n}\n/*# sourceMappingURL=bootstrap.css.map */", + "/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS and IE text size adjust after device orientation change,\n// without disabling user zoom.\n//\n\nhtml {\n font-family: sans-serif; // 1\n -ms-text-size-adjust: 100%; // 2\n -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11\n// and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block; // 1\n vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n background-color: transparent;\n}\n\n//\n// Improve readability of focused elements when they are also in an\n// active/hover state.\n//\n\na:active,\na:hover {\n outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n background: #ff0;\n color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsup {\n top: -0.5em;\n}\n\nsub {\n bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n box-sizing: content-box;\n height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n// Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit; // 1\n font: inherit; // 2\n margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n// and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n// `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button; // 2\n cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box; // 1\n padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n//\n\ninput[type=\"search\"] {\n -webkit-appearance: textfield; // 1\n box-sizing: content-box; //2\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n border: 0; // 1\n padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\ntd,\nth {\n padding: 0;\n}\n", + "/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n\n// ==========================================================================\n// Print styles.\n// Inlined to avoid the additional HTTP request: h5bp.com/r\n// ==========================================================================\n\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important; // Black prints faster: h5bp.com/s\n box-shadow: none !important;\n text-shadow: none !important;\n }\n\n a,\n a:visited {\n text-decoration: underline;\n }\n\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n\n // Don't show links that are fragment identifiers,\n // or use the `javascript:` pseudo protocol\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n\n thead {\n display: table-header-group; // h5bp.com/t\n }\n\n tr,\n img {\n page-break-inside: avoid;\n }\n\n img {\n max-width: 100% !important;\n }\n\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n\n h2,\n h3 {\n page-break-after: avoid;\n }\n\n // Bootstrap specific changes start\n\n // Bootstrap components\n .navbar {\n display: none;\n }\n .btn,\n .dropup > .btn {\n > .caret {\n border-top-color: #000 !important;\n }\n }\n .label {\n border: 1px solid #000;\n }\n\n .table {\n border-collapse: collapse !important;\n\n td,\n th {\n background-color: #fff !important;\n }\n }\n .table-bordered {\n th,\n td {\n border: 1px solid #ddd !important;\n }\n }\n\n // Bootstrap specific changes end\n}\n", + "//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// Star\n\n// Import the fonts\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('@{icon-font-path}@{icon-font-name}.eot');\n src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'),\n url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk { &:before { content: \"\\002a\"; } }\n.glyphicon-plus { &:before { content: \"\\002b\"; } }\n.glyphicon-euro,\n.glyphicon-eur { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil { &:before { content: \"\\270f\"; } }\n.glyphicon-glass { &:before { content: \"\\e001\"; } }\n.glyphicon-music { &:before { content: \"\\e002\"; } }\n.glyphicon-search { &:before { content: \"\\e003\"; } }\n.glyphicon-heart { &:before { content: \"\\e005\"; } }\n.glyphicon-star { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty { &:before { content: \"\\e007\"; } }\n.glyphicon-user { &:before { content: \"\\e008\"; } }\n.glyphicon-film { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large { &:before { content: \"\\e010\"; } }\n.glyphicon-th { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list { &:before { content: \"\\e012\"; } }\n.glyphicon-ok { &:before { content: \"\\e013\"; } }\n.glyphicon-remove { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out { &:before { content: \"\\e016\"; } }\n.glyphicon-off { &:before { content: \"\\e017\"; } }\n.glyphicon-signal { &:before { content: \"\\e018\"; } }\n.glyphicon-cog { &:before { content: \"\\e019\"; } }\n.glyphicon-trash { &:before { content: \"\\e020\"; } }\n.glyphicon-home { &:before { content: \"\\e021\"; } }\n.glyphicon-file { &:before { content: \"\\e022\"; } }\n.glyphicon-time { &:before { content: \"\\e023\"; } }\n.glyphicon-road { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt { &:before { content: \"\\e025\"; } }\n.glyphicon-download { &:before { content: \"\\e026\"; } }\n.glyphicon-upload { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt { &:before { content: \"\\e032\"; } }\n.glyphicon-lock { &:before { content: \"\\e033\"; } }\n.glyphicon-flag { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode { &:before { content: \"\\e040\"; } }\n.glyphicon-tag { &:before { content: \"\\e041\"; } }\n.glyphicon-tags { &:before { content: \"\\e042\"; } }\n.glyphicon-book { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark { &:before { content: \"\\e044\"; } }\n.glyphicon-print { &:before { content: \"\\e045\"; } }\n.glyphicon-camera { &:before { content: \"\\e046\"; } }\n.glyphicon-font { &:before { content: \"\\e047\"; } }\n.glyphicon-bold { &:before { content: \"\\e048\"; } }\n.glyphicon-italic { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify { &:before { content: \"\\e055\"; } }\n.glyphicon-list { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video { &:before { content: \"\\e059\"; } }\n.glyphicon-picture { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust { &:before { content: \"\\e063\"; } }\n.glyphicon-tint { &:before { content: \"\\e064\"; } }\n.glyphicon-edit { &:before { content: \"\\e065\"; } }\n.glyphicon-share { &:before { content: \"\\e066\"; } }\n.glyphicon-check { &:before { content: \"\\e067\"; } }\n.glyphicon-move { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward { &:before { content: \"\\e070\"; } }\n.glyphicon-backward { &:before { content: \"\\e071\"; } }\n.glyphicon-play { &:before { content: \"\\e072\"; } }\n.glyphicon-pause { &:before { content: \"\\e073\"; } }\n.glyphicon-stop { &:before { content: \"\\e074\"; } }\n.glyphicon-forward { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward { &:before { content: \"\\e077\"; } }\n.glyphicon-eject { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign { &:before { content: \"\\e101\"; } }\n.glyphicon-gift { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf { &:before { content: \"\\e103\"; } }\n.glyphicon-fire { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign { &:before { content: \"\\e107\"; } }\n.glyphicon-plane { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar { &:before { content: \"\\e109\"; } }\n.glyphicon-random { &:before { content: \"\\e110\"; } }\n.glyphicon-comment { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn { &:before { content: \"\\e122\"; } }\n.glyphicon-bell { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down { &:before { content: \"\\e134\"; } }\n.glyphicon-globe { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks { &:before { content: \"\\e137\"; } }\n.glyphicon-filter { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty { &:before { content: \"\\e143\"; } }\n.glyphicon-link { &:before { content: \"\\e144\"; } }\n.glyphicon-phone { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin { &:before { content: \"\\e146\"; } }\n.glyphicon-usd { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp { &:before { content: \"\\e149\"; } }\n.glyphicon-sort { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked { &:before { content: \"\\e157\"; } }\n.glyphicon-expand { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in { &:before { content: \"\\e161\"; } }\n.glyphicon-flash { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window { &:before { content: \"\\e164\"; } }\n.glyphicon-record { &:before { content: \"\\e165\"; } }\n.glyphicon-save { &:before { content: \"\\e166\"; } }\n.glyphicon-open { &:before { content: \"\\e167\"; } }\n.glyphicon-saved { &:before { content: \"\\e168\"; } }\n.glyphicon-import { &:before { content: \"\\e169\"; } }\n.glyphicon-export { &:before { content: \"\\e170\"; } }\n.glyphicon-send { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery { &:before { content: \"\\e179\"; } }\n.glyphicon-header { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt { &:before { content: \"\\e183\"; } }\n.glyphicon-tower { &:before { content: \"\\e184\"; } }\n.glyphicon-stats { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1 { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1 { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1 { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous { &:before { content: \"\\e200\"; } }\n.glyphicon-cd { &:before { content: \"\\e201\"; } }\n.glyphicon-save-file { &:before { content: \"\\e202\"; } }\n.glyphicon-open-file { &:before { content: \"\\e203\"; } }\n.glyphicon-level-up { &:before { content: \"\\e204\"; } }\n.glyphicon-copy { &:before { content: \"\\e205\"; } }\n.glyphicon-paste { &:before { content: \"\\e206\"; } }\n// The following 2 Glyphicons are omitted for the time being because\n// they currently use Unicode codepoints that are outside the\n// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle\n// non-BMP codepoints in CSS string escapes, and thus can't display these two icons.\n// Notably, the bug affects some older versions of the Android Browser.\n// More info: https://github.com/twbs/bootstrap/issues/10106\n// .glyphicon-door { &:before { content: \"\\1f6aa\"; } }\n// .glyphicon-key { &:before { content: \"\\1f511\"; } }\n.glyphicon-alert { &:before { content: \"\\e209\"; } }\n.glyphicon-equalizer { &:before { content: \"\\e210\"; } }\n.glyphicon-king { &:before { content: \"\\e211\"; } }\n.glyphicon-queen { &:before { content: \"\\e212\"; } }\n.glyphicon-pawn { &:before { content: \"\\e213\"; } }\n.glyphicon-bishop { &:before { content: \"\\e214\"; } }\n.glyphicon-knight { &:before { content: \"\\e215\"; } }\n.glyphicon-baby-formula { &:before { content: \"\\e216\"; } }\n.glyphicon-tent { &:before { content: \"\\26fa\"; } }\n.glyphicon-blackboard { &:before { content: \"\\e218\"; } }\n.glyphicon-bed { &:before { content: \"\\e219\"; } }\n.glyphicon-apple { &:before { content: \"\\f8ff\"; } }\n.glyphicon-erase { &:before { content: \"\\e221\"; } }\n.glyphicon-hourglass { &:before { content: \"\\231b\"; } }\n.glyphicon-lamp { &:before { content: \"\\e223\"; } }\n.glyphicon-duplicate { &:before { content: \"\\e224\"; } }\n.glyphicon-piggy-bank { &:before { content: \"\\e225\"; } }\n.glyphicon-scissors { &:before { content: \"\\e226\"; } }\n.glyphicon-bitcoin { &:before { content: \"\\e227\"; } }\n.glyphicon-btc { &:before { content: \"\\e227\"; } }\n.glyphicon-xbt { &:before { content: \"\\e227\"; } }\n.glyphicon-yen { &:before { content: \"\\00a5\"; } }\n.glyphicon-jpy { &:before { content: \"\\00a5\"; } }\n.glyphicon-ruble { &:before { content: \"\\20bd\"; } }\n.glyphicon-rub { &:before { content: \"\\20bd\"; } }\n.glyphicon-scale { &:before { content: \"\\e230\"; } }\n.glyphicon-ice-lolly { &:before { content: \"\\e231\"; } }\n.glyphicon-ice-lolly-tasted { &:before { content: \"\\e232\"; } }\n.glyphicon-education { &:before { content: \"\\e233\"; } }\n.glyphicon-option-horizontal { &:before { content: \"\\e234\"; } }\n.glyphicon-option-vertical { &:before { content: \"\\e235\"; } }\n.glyphicon-menu-hamburger { &:before { content: \"\\e236\"; } }\n.glyphicon-modal-window { &:before { content: \"\\e237\"; } }\n.glyphicon-oil { &:before { content: \"\\e238\"; } }\n.glyphicon-grain { &:before { content: \"\\e239\"; } }\n.glyphicon-sunglasses { &:before { content: \"\\e240\"; } }\n.glyphicon-text-size { &:before { content: \"\\e241\"; } }\n.glyphicon-text-color { &:before { content: \"\\e242\"; } }\n.glyphicon-text-background { &:before { content: \"\\e243\"; } }\n.glyphicon-object-align-top { &:before { content: \"\\e244\"; } }\n.glyphicon-object-align-bottom { &:before { content: \"\\e245\"; } }\n.glyphicon-object-align-horizontal{ &:before { content: \"\\e246\"; } }\n.glyphicon-object-align-left { &:before { content: \"\\e247\"; } }\n.glyphicon-object-align-vertical { &:before { content: \"\\e248\"; } }\n.glyphicon-object-align-right { &:before { content: \"\\e249\"; } }\n.glyphicon-triangle-right { &:before { content: \"\\e250\"; } }\n.glyphicon-triangle-left { &:before { content: \"\\e251\"; } }\n.glyphicon-triangle-bottom { &:before { content: \"\\e252\"; } }\n.glyphicon-triangle-top { &:before { content: \"\\e253\"; } }\n.glyphicon-console { &:before { content: \"\\e254\"; } }\n.glyphicon-superscript { &:before { content: \"\\e255\"; } }\n.glyphicon-subscript { &:before { content: \"\\e256\"; } }\n.glyphicon-menu-left { &:before { content: \"\\e257\"; } }\n.glyphicon-menu-right { &:before { content: \"\\e258\"; } }\n.glyphicon-menu-down { &:before { content: \"\\e259\"; } }\n.glyphicon-menu-up { &:before { content: \"\\e260\"; } }\n", + "//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n .box-sizing(border-box);\n}\n*:before,\n*:after {\n .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n font-family: @font-family-base;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @text-color;\n background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\n\n// Links\n\na {\n color: @link-color;\n text-decoration: none;\n\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n }\n\n &:focus {\n .tab-focus();\n }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n margin: 0;\n}\n\n\n// Images\n\nimg {\n vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n padding: @thumbnail-padding;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(all .2s ease-in-out);\n\n // Keep them at most 100% wide\n .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n margin-top: @line-height-computed;\n margin-bottom: @line-height-computed;\n border: 0;\n border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0,0,0,0);\n border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n &:active,\n &:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n }\n}\n\n\n// iOS \"clickable elements\" fix for role=\"button\"\n//\n// Fixes \"clickability\" issue (and more generally, the firing of events such as focus as well)\n// for traditionally non-focusable elements with role=\"button\"\n// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n\n[role=\"button\"] {\n cursor: pointer;\n}\n", + "// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n", + "// WebKit-style focus\n\n.tab-focus() {\n // WebKit-specific. Other browsers will keep their default outline style.\n // (Initially tried to also force default via `outline: initial`,\n // but that seems to erroneously remove the outline in Firefox altogether.)\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n", + "// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n display: @display;\n max-width: 100%; // Part 1: Set a maximum relative to the parent\n height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n background-image: url(\"@{file-1x}\");\n\n @media\n only screen and (-webkit-min-device-pixel-ratio: 2),\n only screen and ( min--moz-device-pixel-ratio: 2),\n only screen and ( -o-min-device-pixel-ratio: 2/1),\n only screen and ( min-device-pixel-ratio: 2),\n only screen and ( min-resolution: 192dpi),\n only screen and ( min-resolution: 2dppx) {\n background-image: url(\"@{file-2x}\");\n background-size: @width-1x @height-1x;\n }\n}\n", + "//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n font-family: @headings-font-family;\n font-weight: @headings-font-weight;\n line-height: @headings-line-height;\n color: @headings-color;\n\n small,\n .small {\n font-weight: normal;\n line-height: 1;\n color: @headings-small-color;\n }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n margin-top: @line-height-computed;\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 65%;\n }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n margin-top: (@line-height-computed / 2);\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 75%;\n }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n margin-bottom: @line-height-computed;\n font-size: floor((@font-size-base * 1.15));\n font-weight: 300;\n line-height: 1.4;\n\n @media (min-width: @screen-sm-min) {\n font-size: (@font-size-base * 1.5);\n }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\nmark,\n.mark {\n background-color: @state-warning-bg;\n padding: .2em;\n}\n\n// Alignment\n.text-left { text-align: left; }\n.text-right { text-align: right; }\n.text-center { text-align: center; }\n.text-justify { text-align: justify; }\n.text-nowrap { white-space: nowrap; }\n\n// Transformation\n.text-lowercase { text-transform: lowercase; }\n.text-uppercase { text-transform: uppercase; }\n.text-capitalize { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n color: @text-muted;\n}\n.text-primary {\n .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n // Given the contrast here, this is the only class to have its color inverted\n // automatically.\n color: #fff;\n .bg-variant(@brand-primary);\n}\n.bg-success {\n .bg-variant(@state-success-bg);\n}\n.bg-info {\n .bg-variant(@state-info-bg);\n}\n.bg-warning {\n .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n padding-bottom: ((@line-height-computed / 2) - 1);\n margin: (@line-height-computed * 2) 0 @line-height-computed;\n border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n margin-top: 0;\n margin-bottom: (@line-height-computed / 2);\n ul,\n ol {\n margin-bottom: 0;\n }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n .list-unstyled();\n margin-left: -5px;\n\n > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n }\n}\n\n// Description Lists\ndl {\n margin-top: 0; // Remove browser default\n margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n line-height: @line-height-base;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n dd {\n &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n }\n\n @media (min-width: @dl-horizontal-breakpoint) {\n dt {\n float: left;\n width: (@dl-horizontal-offset - 20);\n clear: left;\n text-align: right;\n .text-overflow();\n }\n dd {\n margin-left: @dl-horizontal-offset;\n }\n }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n font-size: 90%;\n .text-uppercase();\n}\n\n// Blockquotes\nblockquote {\n padding: (@line-height-computed / 2) @line-height-computed;\n margin: 0 0 @line-height-computed;\n font-size: @blockquote-font-size;\n border-left: 5px solid @blockquote-border-color;\n\n p,\n ul,\n ol {\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Note: Deprecated small and .small as of v3.1.0\n // Context: https://github.com/twbs/bootstrap/issues/11660\n footer,\n small,\n .small {\n display: block;\n font-size: 80%; // back to default font-size\n line-height: @line-height-base;\n color: @blockquote-small-color;\n\n &:before {\n content: '\\2014 \\00A0'; // em dash, nbsp\n }\n }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid @blockquote-border-color;\n border-left: 0;\n text-align: right;\n\n // Account for citation\n footer,\n small,\n .small {\n &:before { content: ''; }\n &:after {\n content: '\\00A0 \\2014'; // nbsp, em dash\n }\n }\n}\n\n// Addresses\naddress {\n margin-bottom: @line-height-computed;\n font-style: normal;\n line-height: @line-height-base;\n}\n", + "// Typography\n\n.text-emphasis-variant(@color) {\n color: @color;\n a&:hover,\n a&:focus {\n color: darken(@color, 10%);\n }\n}\n", + "// Contextual backgrounds\n\n.bg-variant(@color) {\n background-color: @color;\n a&:hover,\n a&:focus {\n background-color: darken(@color, 10%);\n }\n}\n", + "// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n", + "//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: @code-color;\n background-color: @code-bg;\n border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: @kbd-color;\n background-color: @kbd-bg;\n border-radius: @border-radius-small;\n box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n }\n}\n\n// Blocks of code\npre {\n display: block;\n padding: ((@line-height-computed - 1) / 2);\n margin: 0 0 (@line-height-computed / 2);\n font-size: (@font-size-base - 1); // 14px to 13px\n line-height: @line-height-base;\n word-break: break-all;\n word-wrap: break-word;\n color: @pre-color;\n background-color: @pre-bg;\n border: 1px solid @pre-border-color;\n border-radius: @border-radius-base;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: @pre-scrollable-max-height;\n overflow-y: scroll;\n}\n", + "//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n .container-fixed();\n\n @media (min-width: @screen-sm-min) {\n width: @container-sm;\n }\n @media (min-width: @screen-md-min) {\n width: @container-md;\n }\n @media (min-width: @screen-lg-min) {\n width: @container-lg;\n }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n .make-grid(lg);\n}\n", + "// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n margin-right: auto;\n margin-left: auto;\n padding-left: floor((@gutter / 2));\n padding-right: ceil((@gutter / 2));\n &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n margin-left: ceil((@gutter / -2));\n margin-right: floor((@gutter / -2));\n &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n float: left;\n width: percentage((@columns / @grid-columns));\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-sm-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-offset(@columns) {\n @media (min-width: @screen-sm-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-push(@columns) {\n @media (min-width: @screen-sm-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-pull(@columns) {\n @media (min-width: @screen-sm-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-md-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-offset(@columns) {\n @media (min-width: @screen-md-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-push(@columns) {\n @media (min-width: @screen-md-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-pull(@columns) {\n @media (min-width: @screen-md-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-lg-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-offset(@columns) {\n @media (min-width: @screen-lg-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-push(@columns) {\n @media (min-width: @screen-lg-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-pull(@columns) {\n @media (min-width: @screen-lg-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n", + "// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n // Common styles for all sizes of grid columns, widths 1-12\n .col(@index) { // initial\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n position: relative;\n // Prevent columns from collapsing when empty\n min-height: 1px;\n // Inner gutter via padding\n padding-left: ceil((@grid-gutter-width / 2));\n padding-right: floor((@grid-gutter-width / 2));\n }\n }\n .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n .col(@index) { // initial\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n float: left;\n }\n }\n .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n .col-@{class}-@{index} {\n width: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n .col-@{class}-push-@{index} {\n left: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n .col-@{class}-push-0 {\n left: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n .col-@{class}-pull-@{index} {\n right: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n .col-@{class}-pull-0 {\n right: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n .col-@{class}-offset-@{index} {\n margin-left: percentage((@index / @grid-columns));\n }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n .calc-grid-column(@index, @class, @type);\n // next iteration\n .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n .float-grid-columns(@class);\n .loop-grid-columns(@grid-columns, @class, width);\n .loop-grid-columns(@grid-columns, @class, pull);\n .loop-grid-columns(@grid-columns, @class, push);\n .loop-grid-columns(@grid-columns, @class, offset);\n}\n", + "//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n background-color: @table-bg;\n}\ncaption {\n padding-top: @table-cell-padding;\n padding-bottom: @table-cell-padding;\n color: @text-muted;\n text-align: left;\n}\nth {\n text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: @line-height-computed;\n // Cells\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-cell-padding;\n line-height: @line-height-base;\n vertical-align: top;\n border-top: 1px solid @table-border-color;\n }\n }\n }\n // Bottom align for column headings\n > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid @table-border-color;\n }\n // Remove top border from thead by default\n > caption + thead,\n > colgroup + thead,\n > thead:first-child {\n > tr:first-child {\n > th,\n > td {\n border-top: 0;\n }\n }\n }\n // Account for multiple tbody instances\n > tbody + tbody {\n border-top: 2px solid @table-border-color;\n }\n\n // Nesting\n .table {\n background-color: @body-bg;\n }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-condensed-cell-padding;\n }\n }\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: 1px solid @table-border-color;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n border: 1px solid @table-border-color;\n }\n }\n }\n > thead > tr {\n > th,\n > td {\n border-bottom-width: 2px;\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n > tbody > tr:nth-of-type(odd) {\n background-color: @table-bg-accent;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n > tbody > tr:hover {\n background-color: @table-bg-hover;\n }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-column;\n}\ntable {\n td,\n th {\n &[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-cell;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)\n\n @media screen and (max-width: @screen-xs-max) {\n width: 100%;\n margin-bottom: (@line-height-computed * 0.75);\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid @table-border-color;\n\n // Tighten up spacing\n > .table {\n margin-bottom: 0;\n\n // Ensure the content doesn't wrap\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n white-space: nowrap;\n }\n }\n }\n }\n\n // Special overrides for the bordered tables\n > .table-bordered {\n border: 0;\n\n // Nuke the appropriate borders so that the parent can handle them\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n\n // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n // chances are there will be only one `tr` in a `thead` and that would\n // remove the border altogether.\n > tbody,\n > tfoot {\n > tr:last-child {\n > th,\n > td {\n border-bottom: 0;\n }\n }\n }\n\n }\n }\n}\n", + "// Tables\n\n.table-row-variant(@state; @background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table > thead > tr,\n .table > tbody > tr,\n .table > tfoot > tr {\n > td.@{state},\n > th.@{state},\n &.@{state} > td,\n &.@{state} > th {\n background-color: @background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover > tbody > tr {\n > td.@{state}:hover,\n > th.@{state}:hover,\n &.@{state}:hover > td,\n &:hover > .@{state},\n &.@{state}:hover > th {\n background-color: darken(@background, 5%);\n }\n }\n}\n", + "//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n // so we reset that to ensure it behaves more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359.\n min-width: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: @line-height-computed;\n font-size: (@font-size-base * 1.5);\n line-height: inherit;\n color: @legend-color;\n border: 0;\n border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n display: inline-block;\n max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n margin-bottom: 5px;\n font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9; // IE8-9\n line-height: normal;\n}\n\ninput[type=\"file\"] {\n display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n .tab-focus();\n}\n\n// Adjust output element\noutput {\n display: block;\n padding-top: (@padding-base-vertical + 1);\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n display: block;\n width: 100%;\n height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n background-color: @input-bg;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid @input-border;\n border-radius: @input-border-radius; // Note: This has no effect on s in CSS.\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n // Customize the `:focus` state to imitate native WebKit styles.\n .form-control-focus();\n\n // Placeholder\n .placeholder();\n\n // Unstyle the caret on ``\n// element gets special love because it's special, and that's a fact!\n.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n height: @input-height;\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n border-radius: @border-radius;\n\n select& {\n height: @input-height;\n line-height: @input-height;\n }\n\n textarea&,\n select[multiple]& {\n height: auto;\n }\n}\n", + "//\n// Buttons\n// --------------------------------------------------\n\n\n// Base styles\n// --------------------------------------------------\n\n.btn {\n display: inline-block;\n margin-bottom: 0; // For input.btn\n font-weight: @btn-font-weight;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n white-space: nowrap;\n .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @btn-border-radius-base);\n .user-select(none);\n\n &,\n &:active,\n &.active {\n &:focus,\n &.focus {\n .tab-focus();\n }\n }\n\n &:hover,\n &:focus,\n &.focus {\n color: @btn-default-color;\n text-decoration: none;\n }\n\n &:active,\n &.active {\n outline: 0;\n background-image: none;\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n cursor: @cursor-disabled;\n .opacity(.65);\n .box-shadow(none);\n }\n\n a& {\n &.disabled,\n fieldset[disabled] & {\n pointer-events: none; // Future-proof disabling of clicks on `` elements\n }\n }\n}\n\n\n// Alternate buttons\n// --------------------------------------------------\n\n.btn-default {\n .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);\n}\n.btn-primary {\n .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);\n}\n// Success appears as green\n.btn-success {\n .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);\n}\n// Info appears as blue-green\n.btn-info {\n .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);\n}\n// Warning appears as orange\n.btn-warning {\n .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);\n}\n// Danger and error appear as red\n.btn-danger {\n .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);\n}\n\n\n// Link buttons\n// -------------------------\n\n// Make a button look and behave like a link\n.btn-link {\n color: @link-color;\n font-weight: normal;\n border-radius: 0;\n\n &,\n &:active,\n &.active,\n &[disabled],\n fieldset[disabled] & {\n background-color: transparent;\n .box-shadow(none);\n }\n &,\n &:hover,\n &:focus,\n &:active {\n border-color: transparent;\n }\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n background-color: transparent;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @btn-link-disabled-color;\n text-decoration: none;\n }\n }\n}\n\n\n// Button Sizes\n// --------------------------------------------------\n\n.btn-lg {\n // line-height: ensure even-numbered height of button next to large input\n .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @btn-border-radius-large);\n}\n.btn-sm {\n // line-height: ensure proper height of button next to small input\n .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n.btn-xs {\n .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n\n\n// Block button\n// --------------------------------------------------\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n// Vertically space out multiple block buttons\n.btn-block + .btn-block {\n margin-top: 5px;\n}\n\n// Specificity overrides\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"] {\n &.btn-block {\n width: 100%;\n }\n}\n", + "// Button variants\n//\n// Easily pump out default styles, as well as :hover, :focus, :active,\n// and disabled options for all buttons\n\n.button-variant(@color; @background; @border) {\n color: @color;\n background-color: @background;\n border-color: @border;\n\n &:focus,\n &.focus {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 25%);\n }\n &:hover {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 12%);\n }\n &:active,\n &.active,\n .open > .dropdown-toggle& {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 12%);\n\n &:hover,\n &:focus,\n &.focus {\n color: @color;\n background-color: darken(@background, 17%);\n border-color: darken(@border, 25%);\n }\n }\n &:active,\n &.active,\n .open > .dropdown-toggle& {\n background-image: none;\n }\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus,\n &.focus {\n background-color: @background;\n border-color: @border;\n }\n }\n\n .badge {\n color: @background;\n background-color: @color;\n }\n}\n\n// Button sizes\n.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n border-radius: @border-radius;\n}\n", + "// Opacity\n\n.opacity(@opacity) {\n opacity: @opacity;\n // IE8 filter\n @opacity-ie: (@opacity * 100);\n filter: ~\"alpha(opacity=@{opacity-ie})\";\n}\n", + "//\n// Component animations\n// --------------------------------------------------\n\n// Heads up!\n//\n// We don't use the `.opacity()` mixin here since it causes a bug with text\n// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552.\n\n.fade {\n opacity: 0;\n .transition(opacity .15s linear);\n &.in {\n opacity: 1;\n }\n}\n\n.collapse {\n display: none;\n\n &.in { display: block; }\n tr&.in { display: table-row; }\n tbody&.in { display: table-row-group; }\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n .transition-property(~\"height, visibility\");\n .transition-duration(.35s);\n .transition-timing-function(ease);\n}\n", + "//\n// Dropdown menus\n// --------------------------------------------------\n\n\n// Dropdown arrow/caret\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: @caret-width-base dashed;\n border-top: @caret-width-base solid ~\"\\9\"; // IE8\n border-right: @caret-width-base solid transparent;\n border-left: @caret-width-base solid transparent;\n}\n\n// The dropdown wrapper (div)\n.dropup,\n.dropdown {\n position: relative;\n}\n\n// Prevent the focus on the dropdown toggle when closing dropdowns\n.dropdown-toggle:focus {\n outline: 0;\n}\n\n// The dropdown menu (ul)\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: @zindex-dropdown;\n display: none; // none by default, but block on \"open\" of the menu\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0; // override default ul\n list-style: none;\n font-size: @font-size-base;\n text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)\n background-color: @dropdown-bg;\n border: 1px solid @dropdown-fallback-border; // IE8 fallback\n border: 1px solid @dropdown-border;\n border-radius: @border-radius-base;\n .box-shadow(0 6px 12px rgba(0,0,0,.175));\n background-clip: padding-box;\n\n // Aligns the dropdown menu to right\n //\n // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]`\n &.pull-right {\n right: 0;\n left: auto;\n }\n\n // Dividers (basically an hr) within the dropdown\n .divider {\n .nav-divider(@dropdown-divider-bg);\n }\n\n // Links within the dropdown menu\n > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: @line-height-base;\n color: @dropdown-link-color;\n white-space: nowrap; // prevent links from randomly breaking onto new lines\n }\n}\n\n// Hover/Focus state\n.dropdown-menu > li > a {\n &:hover,\n &:focus {\n text-decoration: none;\n color: @dropdown-link-hover-color;\n background-color: @dropdown-link-hover-bg;\n }\n}\n\n// Active state\n.dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-active-color;\n text-decoration: none;\n outline: 0;\n background-color: @dropdown-link-active-bg;\n }\n}\n\n// Disabled state\n//\n// Gray out text and ensure the hover/focus state remains gray\n\n.dropdown-menu > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-disabled-color;\n }\n\n // Nuke hover/focus effects\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none; // Remove CSS gradient\n .reset-filter();\n cursor: @cursor-disabled;\n }\n}\n\n// Open state for the dropdown\n.open {\n // Show the menu\n > .dropdown-menu {\n display: block;\n }\n\n // Remove the outline when :focus is triggered\n > a {\n outline: 0;\n }\n}\n\n// Menu positioning\n//\n// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown\n// menu with the parent.\n.dropdown-menu-right {\n left: auto; // Reset the default from `.dropdown-menu`\n right: 0;\n}\n// With v3, we enabled auto-flipping if you have a dropdown within a right\n// aligned nav component. To enable the undoing of that, we provide an override\n// to restore the default dropdown menu alignment.\n//\n// This is only for left-aligning a dropdown menu within a `.navbar-right` or\n// `.pull-right` nav component.\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n\n// Dropdown section headers\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: @font-size-small;\n line-height: @line-height-base;\n color: @dropdown-header-color;\n white-space: nowrap; // as with > li > a\n}\n\n// Backdrop to catch body clicks on mobile, etc.\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: (@zindex-dropdown - 10);\n}\n\n// Right aligned dropdowns\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n\n// Allow for dropdowns to go bottom up (aka, dropup-menu)\n//\n// Just add .dropup after the standard .dropdown class and you're set, bro.\n// TODO: abstract this so that the navbar fixed styles are not placed here?\n\n.dropup,\n.navbar-fixed-bottom .dropdown {\n // Reverse the caret\n .caret {\n border-top: 0;\n border-bottom: @caret-width-base dashed;\n border-bottom: @caret-width-base solid ~\"\\9\"; // IE8\n content: \"\";\n }\n // Different positioning for bottom up menu\n .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n }\n}\n\n\n// Component alignment\n//\n// Reiterate per navbar.less and the modified component alignment there.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-right {\n .dropdown-menu {\n .dropdown-menu-right();\n }\n // Necessary for overrides of the default right aligned menu.\n // Will remove come v4 in all likelihood.\n .dropdown-menu-left {\n .dropdown-menu-left();\n }\n }\n}\n", + "// Horizontal dividers\n//\n// Dividers (basically an hr) within dropdowns and nav lists\n\n.nav-divider(@color: #e5e5e5) {\n height: 1px;\n margin: ((@line-height-computed / 2) - 1) 0;\n overflow: hidden;\n background-color: @color;\n}\n", + "// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n", + "//\n// Button groups\n// --------------------------------------------------\n\n// Make the div behave like a button\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle; // match .btn alignment given font-size hack above\n > .btn {\n position: relative;\n float: left;\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active,\n &.active {\n z-index: 2;\n }\n }\n}\n\n// Prevent double borders when buttons are next to each other\n.btn-group {\n .btn + .btn,\n .btn + .btn-group,\n .btn-group + .btn,\n .btn-group + .btn-group {\n margin-left: -1px;\n }\n}\n\n// Optional: Group multiple button groups together for a toolbar\n.btn-toolbar {\n margin-left: -5px; // Offset the first child's margin\n &:extend(.clearfix all);\n\n .btn,\n .btn-group,\n .input-group {\n float: left;\n }\n > .btn,\n > .btn-group,\n > .input-group {\n margin-left: 5px;\n }\n}\n\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n\n// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match\n.btn-group > .btn:first-child {\n margin-left: 0;\n &:not(:last-child):not(.dropdown-toggle) {\n .border-right-radius(0);\n }\n}\n// Need .dropdown-toggle since :last-child doesn't apply, given that a .dropdown-menu is used immediately after it\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n .border-left-radius(0);\n}\n\n// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-right-radius(0);\n }\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n .border-left-radius(0);\n}\n\n// On active and open, don't show outline\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n\n\n// Sizing\n//\n// Remix the default button sizing classes into new ones for easier manipulation.\n\n.btn-group-xs > .btn { &:extend(.btn-xs); }\n.btn-group-sm > .btn { &:extend(.btn-sm); }\n.btn-group-lg > .btn { &:extend(.btn-lg); }\n\n\n// Split button dropdowns\n// ----------------------\n\n// Give the line between buttons some depth\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n// The clickable button for toggling the menu\n// Remove the gradient and set the same inset shadow as the :active state\n.btn-group.open .dropdown-toggle {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n\n // Show no shadow for `.btn-link` since it has no other button styles.\n &.btn-link {\n .box-shadow(none);\n }\n}\n\n\n// Reposition the caret\n.btn .caret {\n margin-left: 0;\n}\n// Carets in other button sizes\n.btn-lg .caret {\n border-width: @caret-width-large @caret-width-large 0;\n border-bottom-width: 0;\n}\n// Upside down carets for .dropup\n.dropup .btn-lg .caret {\n border-width: 0 @caret-width-large @caret-width-large;\n}\n\n\n// Vertical button groups\n// ----------------------\n\n.btn-group-vertical {\n > .btn,\n > .btn-group,\n > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n }\n\n // Clear floats so dropdown menus can be properly placed\n > .btn-group {\n &:extend(.clearfix all);\n > .btn {\n float: none;\n }\n }\n\n > .btn + .btn,\n > .btn + .btn-group,\n > .btn-group + .btn,\n > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n }\n}\n\n.btn-group-vertical > .btn {\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n &:first-child:not(:last-child) {\n .border-top-radius(@btn-border-radius-base);\n .border-bottom-radius(0);\n }\n &:last-child:not(:first-child) {\n .border-top-radius(0);\n .border-bottom-radius(@btn-border-radius-base);\n }\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-bottom-radius(0);\n }\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n .border-top-radius(0);\n}\n\n\n// Justified button groups\n// ----------------------\n\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n > .btn,\n > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n }\n > .btn-group .btn {\n width: 100%;\n }\n\n > .btn-group .dropdown-menu {\n left: auto;\n }\n}\n\n\n// Checkbox and radio options\n//\n// In order to support the browser's form validation feedback, powered by the\n// `required` attribute, we have to \"hide\" the inputs via `clip`. We cannot use\n// `display: none;` or `visibility: hidden;` as that also hides the popover.\n// Simply visually hiding the inputs via `opacity` would leave them clickable in\n// certain cases which is prevented by using `clip` and `pointer-events`.\n// This way, we ensure a DOM element is visible to position the popover from.\n//\n// See https://github.com/twbs/bootstrap/pull/12794 and\n// https://github.com/twbs/bootstrap/pull/14559 for more information.\n\n[data-toggle=\"buttons\"] {\n > .btn,\n > .btn-group > .btn {\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0,0,0,0);\n pointer-events: none;\n }\n }\n}\n", + "// Single side border-radius\n\n.border-top-radius(@radius) {\n border-top-right-radius: @radius;\n border-top-left-radius: @radius;\n}\n.border-right-radius(@radius) {\n border-bottom-right-radius: @radius;\n border-top-right-radius: @radius;\n}\n.border-bottom-radius(@radius) {\n border-bottom-right-radius: @radius;\n border-bottom-left-radius: @radius;\n}\n.border-left-radius(@radius) {\n border-bottom-left-radius: @radius;\n border-top-left-radius: @radius;\n}\n", + "//\n// Input groups\n// --------------------------------------------------\n\n// Base styles\n// -------------------------\n.input-group {\n position: relative; // For dropdowns\n display: table;\n border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table\n\n // Undo padding and float of grid classes\n &[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n }\n\n .form-control {\n // Ensure that the input is always above the *appended* addon button for\n // proper border colors.\n position: relative;\n z-index: 2;\n\n // IE9 fubars the placeholder attribute in text inputs and the arrows on\n // select elements in input groups. To fix it, we float the input. Details:\n // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855\n float: left;\n\n width: 100%;\n margin-bottom: 0;\n\n &:focus {\n z-index: 3;\n }\n }\n}\n\n// Sizing options\n//\n// Remix the default form control sizing classes into new ones for easier\n// manipulation.\n\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n .input-lg();\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n .input-sm();\n}\n\n\n// Display as table-cell\n// -------------------------\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n}\n// Addon and addon wrapper for buttons\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle; // Match the inputs\n}\n\n// Text input groups\n// -------------------------\n.input-group-addon {\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n font-weight: normal;\n line-height: 1;\n color: @input-color;\n text-align: center;\n background-color: @input-group-addon-bg;\n border: 1px solid @input-group-addon-border-color;\n border-radius: @input-border-radius;\n\n // Sizing\n &.input-sm {\n padding: @padding-small-vertical @padding-small-horizontal;\n font-size: @font-size-small;\n border-radius: @input-border-radius-small;\n }\n &.input-lg {\n padding: @padding-large-vertical @padding-large-horizontal;\n font-size: @font-size-large;\n border-radius: @input-border-radius-large;\n }\n\n // Nuke default margins from checkboxes and radios to vertically center within.\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n margin-top: 0;\n }\n}\n\n// Reset rounded corners\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n .border-right-radius(0);\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n .border-left-radius(0);\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n\n// Button input groups\n// -------------------------\n.input-group-btn {\n position: relative;\n // Jankily prevent input button groups from wrapping with `white-space` and\n // `font-size` in combination with `inline-block` on buttons.\n font-size: 0;\n white-space: nowrap;\n\n // Negative margin for spacing, position for bringing hovered/focused/actived\n // element above the siblings.\n > .btn {\n position: relative;\n + .btn {\n margin-left: -1px;\n }\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active {\n z-index: 2;\n }\n }\n\n // Negative margin to only have a 1px border between the two\n &:first-child {\n > .btn,\n > .btn-group {\n margin-right: -1px;\n }\n }\n &:last-child {\n > .btn,\n > .btn-group {\n z-index: 2;\n margin-left: -1px;\n }\n }\n}\n", + "//\n// Navs\n// --------------------------------------------------\n\n\n// Base class\n// --------------------------------------------------\n\n.nav {\n margin-bottom: 0;\n padding-left: 0; // Override default ul/ol\n list-style: none;\n &:extend(.clearfix all);\n\n > li {\n position: relative;\n display: block;\n\n > a {\n position: relative;\n display: block;\n padding: @nav-link-padding;\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: @nav-link-hover-bg;\n }\n }\n\n // Disabled state sets text to gray and nukes hover/tab effects\n &.disabled > a {\n color: @nav-disabled-link-color;\n\n &:hover,\n &:focus {\n color: @nav-disabled-link-hover-color;\n text-decoration: none;\n background-color: transparent;\n cursor: @cursor-disabled;\n }\n }\n }\n\n // Open dropdowns\n .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @nav-link-hover-bg;\n border-color: @link-color;\n }\n }\n\n // Nav dividers (deprecated with v3.0.1)\n //\n // This should have been removed in v3 with the dropping of `.nav-list`, but\n // we missed it. We don't currently support this anywhere, but in the interest\n // of maintaining backward compatibility in case you use it, it's deprecated.\n .nav-divider {\n .nav-divider();\n }\n\n // Prevent IE8 from misplacing imgs\n //\n // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989\n > li > a > img {\n max-width: none;\n }\n}\n\n\n// Tabs\n// -------------------------\n\n// Give the tabs something to sit on\n.nav-tabs {\n border-bottom: 1px solid @nav-tabs-border-color;\n > li {\n float: left;\n // Make the list-items overlay the bottom border\n margin-bottom: -1px;\n\n // Actual tabs (as links)\n > a {\n margin-right: 2px;\n line-height: @line-height-base;\n border: 1px solid transparent;\n border-radius: @border-radius-base @border-radius-base 0 0;\n &:hover {\n border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color;\n }\n }\n\n // Active state, and its :hover to override normal :hover\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-tabs-active-link-hover-color;\n background-color: @nav-tabs-active-link-hover-bg;\n border: 1px solid @nav-tabs-active-link-hover-border-color;\n border-bottom-color: transparent;\n cursor: default;\n }\n }\n }\n // pulling this in mainly for less shorthand\n &.nav-justified {\n .nav-justified();\n .nav-tabs-justified();\n }\n}\n\n\n// Pills\n// -------------------------\n.nav-pills {\n > li {\n float: left;\n\n // Links rendered as pills\n > a {\n border-radius: @nav-pills-border-radius;\n }\n + li {\n margin-left: 2px;\n }\n\n // Active state\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-pills-active-link-hover-color;\n background-color: @nav-pills-active-link-hover-bg;\n }\n }\n }\n}\n\n\n// Stacked pills\n.nav-stacked {\n > li {\n float: none;\n + li {\n margin-top: 2px;\n margin-left: 0; // no need for this gap between nav items\n }\n }\n}\n\n\n// Nav variations\n// --------------------------------------------------\n\n// Justified nav links\n// -------------------------\n\n.nav-justified {\n width: 100%;\n\n > li {\n float: none;\n > a {\n text-align: center;\n margin-bottom: 5px;\n }\n }\n\n > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n }\n\n @media (min-width: @screen-sm-min) {\n > li {\n display: table-cell;\n width: 1%;\n > a {\n margin-bottom: 0;\n }\n }\n }\n}\n\n// Move borders to anchors instead of bottom of list\n//\n// Mixin for adding on top the shared `.nav-justified` styles for our tabs\n.nav-tabs-justified {\n border-bottom: 0;\n\n > li > a {\n // Override margin from .nav-tabs\n margin-right: 0;\n border-radius: @border-radius-base;\n }\n\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border: 1px solid @nav-tabs-justified-link-border-color;\n }\n\n @media (min-width: @screen-sm-min) {\n > li > a {\n border-bottom: 1px solid @nav-tabs-justified-link-border-color;\n border-radius: @border-radius-base @border-radius-base 0 0;\n }\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border-bottom-color: @nav-tabs-justified-active-link-border-color;\n }\n }\n}\n\n\n// Tabbable tabs\n// -------------------------\n\n// Hide tabbable panes to start, show them when `.active`\n.tab-content {\n > .tab-pane {\n display: none;\n }\n > .active {\n display: block;\n }\n}\n\n\n// Dropdowns\n// -------------------------\n\n// Specific dropdowns\n.nav-tabs .dropdown-menu {\n // make dropdown border overlap tab border\n margin-top: -1px;\n // Remove the top rounded corners here since there is a hard edge above the menu\n .border-top-radius(0);\n}\n", + "//\n// Navbars\n// --------------------------------------------------\n\n\n// Wrapper and base class\n//\n// Provide a static navbar from which we expand to create full-width, fixed, and\n// other navbar variations.\n\n.navbar {\n position: relative;\n min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode)\n margin-bottom: @navbar-margin-bottom;\n border: 1px solid transparent;\n\n // Prevent floats from breaking the navbar\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: @navbar-border-radius;\n }\n}\n\n\n// Navbar heading\n//\n// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy\n// styling of responsive aspects.\n\n.navbar-header {\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n }\n}\n\n\n// Navbar collapse (body)\n//\n// Group your navbar content into this for easy collapsing and expanding across\n// various device sizes. By default, this content is collapsed when <768px, but\n// will expand past that for a horizontal display.\n//\n// To start (on mobile devices) the navbar links, forms, and buttons are stacked\n// vertically and include a `max-height` to overflow in case you have too much\n// content for the user's viewport.\n\n.navbar-collapse {\n overflow-x: visible;\n padding-right: @navbar-padding-horizontal;\n padding-left: @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255,255,255,.1);\n &:extend(.clearfix all);\n -webkit-overflow-scrolling: touch;\n\n &.in {\n overflow-y: auto;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border-top: 0;\n box-shadow: none;\n\n &.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0; // Override default setting\n overflow: visible !important;\n }\n\n &.in {\n overflow-y: visible;\n }\n\n // Undo the collapse side padding for navbars with containers to ensure\n // alignment of right-aligned contents.\n .navbar-fixed-top &,\n .navbar-static-top &,\n .navbar-fixed-bottom & {\n padding-left: 0;\n padding-right: 0;\n }\n }\n}\n\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n .navbar-collapse {\n max-height: @navbar-collapse-max-height;\n\n @media (max-device-width: @screen-xs-min) and (orientation: landscape) {\n max-height: 200px;\n }\n }\n}\n\n\n// Both navbar header and collapse\n//\n// When a container is present, change the behavior of the header and collapse.\n\n.container,\n.container-fluid {\n > .navbar-header,\n > .navbar-collapse {\n margin-right: -@navbar-padding-horizontal;\n margin-left: -@navbar-padding-horizontal;\n\n @media (min-width: @grid-float-breakpoint) {\n margin-right: 0;\n margin-left: 0;\n }\n }\n}\n\n\n//\n// Navbar alignment options\n//\n// Display the navbar across the entirety of the page or fixed it to the top or\n// bottom of the page.\n\n// Static top (unfixed, but 100% wide) navbar\n.navbar-static-top {\n z-index: @zindex-navbar;\n border-width: 0 0 1px;\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: 0;\n }\n}\n\n// Fix the top/bottom navbars when screen real estate supports it\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: @zindex-navbar-fixed;\n\n // Undo the rounded corners\n @media (min-width: @grid-float-breakpoint) {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0; // override .navbar defaults\n border-width: 1px 0 0;\n}\n\n\n// Brand/project name\n\n.navbar-brand {\n float: left;\n padding: @navbar-padding-vertical @navbar-padding-horizontal;\n font-size: @font-size-large;\n line-height: @line-height-computed;\n height: @navbar-height;\n\n &:hover,\n &:focus {\n text-decoration: none;\n }\n\n > img {\n display: block;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n .navbar > .container &,\n .navbar > .container-fluid & {\n margin-left: -@navbar-padding-horizontal;\n }\n }\n}\n\n\n// Navbar toggle\n//\n// Custom button for toggling the `.navbar-collapse`, powered by the collapse\n// JavaScript plugin.\n\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: @navbar-padding-horizontal;\n padding: 9px 10px;\n .navbar-vertical-align(34px);\n background-color: transparent;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n border-radius: @border-radius-base;\n\n // We remove the `outline` here, but later compensate by attaching `:hover`\n // styles to `:focus`.\n &:focus {\n outline: 0;\n }\n\n // Bars\n .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n }\n .icon-bar + .icon-bar {\n margin-top: 4px;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n display: none;\n }\n}\n\n\n// Navbar nav links\n//\n// Builds on top of the `.nav` components with its own modifier class to make\n// the nav the full height of the horizontal nav (above 768px).\n\n.navbar-nav {\n margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal;\n\n > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: @line-height-computed;\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n > li > a,\n .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n > li > a {\n line-height: @line-height-computed;\n &:hover,\n &:focus {\n background-image: none;\n }\n }\n }\n }\n\n // Uncollapse the nav\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin: 0;\n\n > li {\n float: left;\n > a {\n padding-top: @navbar-padding-vertical;\n padding-bottom: @navbar-padding-vertical;\n }\n }\n }\n}\n\n\n// Navbar form\n//\n// Extension of the `.form-inline` with some extra flavor for optimum display in\n// our navbars.\n\n.navbar-form {\n margin-left: -@navbar-padding-horizontal;\n margin-right: -@navbar-padding-horizontal;\n padding: 10px @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n\n // Mixin behavior for optimum display\n .form-inline();\n\n .form-group {\n @media (max-width: @grid-float-breakpoint-max) {\n margin-bottom: 5px;\n\n &:last-child {\n margin-bottom: 0;\n }\n }\n }\n\n // Vertically center in expanded, horizontal navbar\n .navbar-vertical-align(@input-height-base);\n\n // Undo 100% width for pull classes\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n .box-shadow(none);\n }\n}\n\n\n// Dropdown menus\n\n// Menu position and menu carets\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n .border-top-radius(0);\n}\n// Menu position and menu caret support for dropups via extra dropup class\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n .border-top-radius(@navbar-border-radius);\n .border-bottom-radius(0);\n}\n\n\n// Buttons in navbars\n//\n// Vertically center a button within a navbar (when *not* in a form).\n\n.navbar-btn {\n .navbar-vertical-align(@input-height-base);\n\n &.btn-sm {\n .navbar-vertical-align(@input-height-small);\n }\n &.btn-xs {\n .navbar-vertical-align(22);\n }\n}\n\n\n// Text in navbars\n//\n// Add a class to make any element properly align itself vertically within the navbars.\n\n.navbar-text {\n .navbar-vertical-align(@line-height-computed);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin-left: @navbar-padding-horizontal;\n margin-right: @navbar-padding-horizontal;\n }\n}\n\n\n// Component alignment\n//\n// Repurpose the pull utilities as their own navbar utilities to avoid specificity\n// issues with parents and chaining. Only do this when the navbar is uncollapsed\n// though so that navbar contents properly stack and align in mobile.\n//\n// Declared after the navbar components to ensure more specificity on the margins.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-left { .pull-left(); }\n .navbar-right {\n .pull-right();\n margin-right: -@navbar-padding-horizontal;\n\n ~ .navbar-right {\n margin-right: 0;\n }\n }\n}\n\n\n// Alternate navbars\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n background-color: @navbar-default-bg;\n border-color: @navbar-default-border;\n\n .navbar-brand {\n color: @navbar-default-brand-color;\n &:hover,\n &:focus {\n color: @navbar-default-brand-hover-color;\n background-color: @navbar-default-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-default-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-default-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n\n .navbar-toggle {\n border-color: @navbar-default-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-default-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-default-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: @navbar-default-border;\n }\n\n // Dropdown menu items\n .navbar-nav {\n // Remove background color from open dropdown\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-default-link-active-bg;\n color: @navbar-default-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .open .dropdown-menu {\n > li > a {\n color: @navbar-default-link-color;\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n }\n }\n\n\n // Links in navbars\n //\n // Add a class to ensure links outside the navbar nav are colored correctly.\n\n .navbar-link {\n color: @navbar-default-link-color;\n &:hover {\n color: @navbar-default-link-hover-color;\n }\n }\n\n .btn-link {\n color: @navbar-default-link-color;\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n }\n }\n }\n}\n\n// Inverse navbar\n\n.navbar-inverse {\n background-color: @navbar-inverse-bg;\n border-color: @navbar-inverse-border;\n\n .navbar-brand {\n color: @navbar-inverse-brand-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-brand-hover-color;\n background-color: @navbar-inverse-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-inverse-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-inverse-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n\n // Darken the responsive nav toggle\n .navbar-toggle {\n border-color: @navbar-inverse-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-inverse-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-inverse-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: darken(@navbar-inverse-bg, 7%);\n }\n\n // Dropdowns\n .navbar-nav {\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-inverse-link-active-bg;\n color: @navbar-inverse-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display\n .open .dropdown-menu {\n > .dropdown-header {\n border-color: @navbar-inverse-border;\n }\n .divider {\n background-color: @navbar-inverse-border;\n }\n > li > a {\n color: @navbar-inverse-link-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n }\n }\n\n .navbar-link {\n color: @navbar-inverse-link-color;\n &:hover {\n color: @navbar-inverse-link-hover-color;\n }\n }\n\n .btn-link {\n color: @navbar-inverse-link-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n }\n }\n }\n}\n", + "// Navbar vertical align\n//\n// Vertically center elements in the navbar.\n// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.\n\n.navbar-vertical-align(@element-height) {\n margin-top: ((@navbar-height - @element-height) / 2);\n margin-bottom: ((@navbar-height - @element-height) / 2);\n}\n", + "//\n// Utility classes\n// --------------------------------------------------\n\n\n// Floats\n// -------------------------\n\n.clearfix {\n .clearfix();\n}\n.center-block {\n .center-block();\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n\n\n// Toggling content\n// -------------------------\n\n// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n .text-hide();\n}\n\n\n// Hide from screenreaders and browsers\n//\n// Credit: HTML5 Boilerplate\n\n.hidden {\n display: none !important;\n}\n\n\n// For Affix plugin\n// -------------------------\n\n.affix {\n position: fixed;\n}\n", + "//\n// Breadcrumbs\n// --------------------------------------------------\n\n\n.breadcrumb {\n padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;\n margin-bottom: @line-height-computed;\n list-style: none;\n background-color: @breadcrumb-bg;\n border-radius: @border-radius-base;\n\n > li {\n display: inline-block;\n\n + li:before {\n content: \"@{breadcrumb-separator}\\00a0\"; // Unicode space added since inline-block means non-collapsing white-space\n padding: 0 5px;\n color: @breadcrumb-color;\n }\n }\n\n > .active {\n color: @breadcrumb-active-color;\n }\n}\n", + "//\n// Pagination (multiple pages)\n// --------------------------------------------------\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: @line-height-computed 0;\n border-radius: @border-radius-base;\n\n > li {\n display: inline; // Remove list-style and block-level defaults\n > a,\n > span {\n position: relative;\n float: left; // Collapse white-space\n padding: @padding-base-vertical @padding-base-horizontal;\n line-height: @line-height-base;\n text-decoration: none;\n color: @pagination-color;\n background-color: @pagination-bg;\n border: 1px solid @pagination-border;\n margin-left: -1px;\n }\n &:first-child {\n > a,\n > span {\n margin-left: 0;\n .border-left-radius(@border-radius-base);\n }\n }\n &:last-child {\n > a,\n > span {\n .border-right-radius(@border-radius-base);\n }\n }\n }\n\n > li > a,\n > li > span {\n &:hover,\n &:focus {\n z-index: 2;\n color: @pagination-hover-color;\n background-color: @pagination-hover-bg;\n border-color: @pagination-hover-border;\n }\n }\n\n > .active > a,\n > .active > span {\n &,\n &:hover,\n &:focus {\n z-index: 3;\n color: @pagination-active-color;\n background-color: @pagination-active-bg;\n border-color: @pagination-active-border;\n cursor: default;\n }\n }\n\n > .disabled {\n > span,\n > span:hover,\n > span:focus,\n > a,\n > a:hover,\n > a:focus {\n color: @pagination-disabled-color;\n background-color: @pagination-disabled-bg;\n border-color: @pagination-disabled-border;\n cursor: @cursor-disabled;\n }\n }\n}\n\n// Sizing\n// --------------------------------------------------\n\n// Large\n.pagination-lg {\n .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);\n}\n\n// Small\n.pagination-sm {\n .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n", + "// Pagination\n\n.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n > li {\n > a,\n > span {\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n }\n &:first-child {\n > a,\n > span {\n .border-left-radius(@border-radius);\n }\n }\n &:last-child {\n > a,\n > span {\n .border-right-radius(@border-radius);\n }\n }\n }\n}\n", + "//\n// Pager pagination\n// --------------------------------------------------\n\n\n.pager {\n padding-left: 0;\n margin: @line-height-computed 0;\n list-style: none;\n text-align: center;\n &:extend(.clearfix all);\n li {\n display: inline;\n > a,\n > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: @pager-bg;\n border: 1px solid @pager-border;\n border-radius: @pager-border-radius;\n }\n\n > a:hover,\n > a:focus {\n text-decoration: none;\n background-color: @pager-hover-bg;\n }\n }\n\n .next {\n > a,\n > span {\n float: right;\n }\n }\n\n .previous {\n > a,\n > span {\n float: left;\n }\n }\n\n .disabled {\n > a,\n > a:hover,\n > a:focus,\n > span {\n color: @pager-disabled-color;\n background-color: @pager-bg;\n cursor: @cursor-disabled;\n }\n }\n}\n", + "//\n// Labels\n// --------------------------------------------------\n\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: @label-color;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n\n // Add hover effects, but only for links\n a& {\n &:hover,\n &:focus {\n color: @label-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n }\n\n // Empty labels collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for labels in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n}\n\n// Colors\n// Contextual variations (linked labels get darker on :hover)\n\n.label-default {\n .label-variant(@label-default-bg);\n}\n\n.label-primary {\n .label-variant(@label-primary-bg);\n}\n\n.label-success {\n .label-variant(@label-success-bg);\n}\n\n.label-info {\n .label-variant(@label-info-bg);\n}\n\n.label-warning {\n .label-variant(@label-warning-bg);\n}\n\n.label-danger {\n .label-variant(@label-danger-bg);\n}\n", + "// Labels\n\n.label-variant(@color) {\n background-color: @color;\n\n &[href] {\n &:hover,\n &:focus {\n background-color: darken(@color, 10%);\n }\n }\n}\n", + "//\n// Badges\n// --------------------------------------------------\n\n\n// Base class\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: @font-size-small;\n font-weight: @badge-font-weight;\n color: @badge-color;\n line-height: @badge-line-height;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: @badge-bg;\n border-radius: @badge-border-radius;\n\n // Empty badges collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for badges in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n\n .btn-xs &,\n .btn-group-xs > .btn & {\n top: 0;\n padding: 1px 5px;\n }\n\n // Hover state, but only for links\n a& {\n &:hover,\n &:focus {\n color: @badge-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n }\n\n // Account for badges in navs\n .list-group-item.active > &,\n .nav-pills > .active > a > & {\n color: @badge-active-color;\n background-color: @badge-active-bg;\n }\n\n .list-group-item > & {\n float: right;\n }\n\n .list-group-item > & + & {\n margin-right: 5px;\n }\n\n .nav-pills > li > a > & {\n margin-left: 3px;\n }\n}\n", + "//\n// Jumbotron\n// --------------------------------------------------\n\n\n.jumbotron {\n padding-top: @jumbotron-padding;\n padding-bottom: @jumbotron-padding;\n margin-bottom: @jumbotron-padding;\n color: @jumbotron-color;\n background-color: @jumbotron-bg;\n\n h1,\n .h1 {\n color: @jumbotron-heading-color;\n }\n\n p {\n margin-bottom: (@jumbotron-padding / 2);\n font-size: @jumbotron-font-size;\n font-weight: 200;\n }\n\n > hr {\n border-top-color: darken(@jumbotron-bg, 10%);\n }\n\n .container &,\n .container-fluid & {\n border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container\n padding-left: (@grid-gutter-width / 2);\n padding-right: (@grid-gutter-width / 2);\n }\n\n .container {\n max-width: 100%;\n }\n\n @media screen and (min-width: @screen-sm-min) {\n padding-top: (@jumbotron-padding * 1.6);\n padding-bottom: (@jumbotron-padding * 1.6);\n\n .container &,\n .container-fluid & {\n padding-left: (@jumbotron-padding * 2);\n padding-right: (@jumbotron-padding * 2);\n }\n\n h1,\n .h1 {\n font-size: @jumbotron-heading-font-size;\n }\n }\n}\n", + "//\n// Thumbnails\n// --------------------------------------------------\n\n\n// Mixin and adjust the regular image class\n.thumbnail {\n display: block;\n padding: @thumbnail-padding;\n margin-bottom: @line-height-computed;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(border .2s ease-in-out);\n\n > img,\n a > img {\n &:extend(.img-responsive);\n margin-left: auto;\n margin-right: auto;\n }\n\n // Add a hover state for linked versions only\n a&:hover,\n a&:focus,\n a&.active {\n border-color: @link-color;\n }\n\n // Image captions\n .caption {\n padding: @thumbnail-caption-padding;\n color: @thumbnail-caption-color;\n }\n}\n", + "//\n// Alerts\n// --------------------------------------------------\n\n\n// Base styles\n// -------------------------\n\n.alert {\n padding: @alert-padding;\n margin-bottom: @line-height-computed;\n border: 1px solid transparent;\n border-radius: @alert-border-radius;\n\n // Headings for larger alerts\n h4 {\n margin-top: 0;\n // Specified for the h4 to prevent conflicts of changing @headings-color\n color: inherit;\n }\n\n // Provide class for links that match alerts\n .alert-link {\n font-weight: @alert-link-font-weight;\n }\n\n // Improve alignment and spacing of inner content\n > p,\n > ul {\n margin-bottom: 0;\n }\n\n > p + p {\n margin-top: 5px;\n }\n}\n\n// Dismissible alerts\n//\n// Expand the right padding and account for the close button's positioning.\n\n.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0.\n.alert-dismissible {\n padding-right: (@alert-padding + 20);\n\n // Adjust close link position\n .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n }\n}\n\n// Alternate styles\n//\n// Generate contextual modifier classes for colorizing the alert.\n\n.alert-success {\n .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);\n}\n\n.alert-info {\n .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);\n}\n\n.alert-warning {\n .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);\n}\n\n.alert-danger {\n .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);\n}\n", + "// Alerts\n\n.alert-variant(@background; @border; @text-color) {\n background-color: @background;\n border-color: @border;\n color: @text-color;\n\n hr {\n border-top-color: darken(@border, 5%);\n }\n .alert-link {\n color: darken(@text-color, 10%);\n }\n}\n", + "//\n// Progress bars\n// --------------------------------------------------\n\n\n// Bar animations\n// -------------------------\n\n// WebKit\n@-webkit-keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n// Spec and IE10+\n@keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n\n// Bar itself\n// -------------------------\n\n// Outer container\n.progress {\n overflow: hidden;\n height: @line-height-computed;\n margin-bottom: @line-height-computed;\n background-color: @progress-bg;\n border-radius: @progress-border-radius;\n .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));\n}\n\n// Bar of progress\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: @font-size-small;\n line-height: @line-height-computed;\n color: @progress-bar-color;\n text-align: center;\n background-color: @progress-bar-bg;\n .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));\n .transition(width .6s ease);\n}\n\n// Striped bars\n//\n// `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar-striped` class, which you just add to an existing\n// `.progress-bar`.\n.progress-striped .progress-bar,\n.progress-bar-striped {\n #gradient > .striped();\n background-size: 40px 40px;\n}\n\n// Call animation for the active one\n//\n// `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar.active` approach.\n.progress.active .progress-bar,\n.progress-bar.active {\n .animation(progress-bar-stripes 2s linear infinite);\n}\n\n\n// Variations\n// -------------------------\n\n.progress-bar-success {\n .progress-bar-variant(@progress-bar-success-bg);\n}\n\n.progress-bar-info {\n .progress-bar-variant(@progress-bar-info-bg);\n}\n\n.progress-bar-warning {\n .progress-bar-variant(@progress-bar-warning-bg);\n}\n\n.progress-bar-danger {\n .progress-bar-variant(@progress-bar-danger-bg);\n}\n", + "// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n", + "// Progress bars\n\n.progress-bar-variant(@color) {\n background-color: @color;\n\n // Deprecated parent class requirement as of v3.2.0\n .progress-striped & {\n #gradient > .striped();\n }\n}\n", + ".media {\n // Proper spacing between instances of .media\n margin-top: 15px;\n\n &:first-child {\n margin-top: 0;\n }\n}\n\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n\n.media-body {\n width: 10000px;\n}\n\n.media-object {\n display: block;\n\n // Fix collapse in webkit from max-width: 100% and display: table-cell.\n &.img-thumbnail {\n max-width: none;\n }\n}\n\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n\n.media-middle {\n vertical-align: middle;\n}\n\n.media-bottom {\n vertical-align: bottom;\n}\n\n// Reset margins on headings for tighter default spacing\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n\n// Media list variation\n//\n// Undo default ul/ol styles\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n", + "//\n// List groups\n// --------------------------------------------------\n\n\n// Base class\n//\n// Easily usable on