update
This commit is contained in:
1
go.mod
1
go.mod
@@ -39,6 +39,7 @@ require (
|
|||||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
github.com/gorilla/websocket v1.4.2 // indirect
|
github.com/gorilla/websocket v1.4.2 // indirect
|
||||||
github.com/holiman/uint256 v1.3.2 // indirect
|
github.com/holiman/uint256 v1.3.2 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/rabbitmq/amqp091-go v1.10.0
|
github.com/rabbitmq/amqp091-go v1.10.0
|
||||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
|
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
|
||||||
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect
|
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -119,6 +119,8 @@ github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo
|
|||||||
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
|
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
|
||||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
|
github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
|
||||||
github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
|
github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
|
||||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
|
// 区块链网络抽象接口统一实现
|
||||||
package blockchain
|
package blockchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IChainServer interface {
|
type IChainServer interface {
|
||||||
AddAddress(msg any) error
|
Listen() error
|
||||||
RemoveAddress(msg any) error
|
ListenMsg()
|
||||||
Listen(ch chan any)
|
Transfer(from, to, symbol string, amount, fee float64) error
|
||||||
Transfer(msg any) error
|
|
||||||
Stop()
|
Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
type BlockChainServer struct {
|
type BlockChainServer struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
chains map[string]IChainServer // "ETH", "TRON", "BTC"
|
chains map[string]IChainServer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlockChainServer() *BlockChainServer {
|
func NewBlockChainServer() *BlockChainServer {
|
||||||
@@ -30,40 +31,41 @@ func (b *BlockChainServer) RegisterChain(name string, chain IChainServer) {
|
|||||||
b.chains[name] = chain
|
b.chains[name] = chain
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BlockChainServer) AddAddress(chain string, msg any) error {
|
|
||||||
if srv, ok := b.chains[chain]; ok {
|
|
||||||
srv.AddAddress(msg)
|
|
||||||
fmt.Printf("✅ 添加监听地址: chain=%s, msg=%v\n", chain, msg)
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("⚠️ 链未注册: %s\n", chain)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BlockChainServer) RemoveAddress(chain string, msg any) error {
|
|
||||||
if srv, ok := b.chains[chain]; ok {
|
|
||||||
srv.RemoveAddress(msg)
|
|
||||||
fmt.Printf("🗑️ 移除监听地址: chain=%s, msg=%s\n", chain, msg)
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("⚠️ 链未注册: %s\n", chain)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BlockChainServer) Listen(chain string, ch chan any) error {
|
func (b *BlockChainServer) Listen(chain string, ch chan any) error {
|
||||||
if srv, ok := b.chains[chain]; ok {
|
if srv, ok := b.chains[chain]; ok {
|
||||||
go func() {
|
go func() {
|
||||||
srv.Listen(ch)
|
err := srv.Listen()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Start Listen error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("链未注册: %s", chain)
|
return fmt.Errorf("链未注册: %s", chain)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BlockChainServer) Transfer(chain string, msg any) error {
|
func (b *BlockChainServer) ListenMsg(chain string) error {
|
||||||
if srv, ok := b.chains[chain]; ok {
|
if srv, ok := b.chains[chain]; ok {
|
||||||
fmt.Printf("💸 %s发起转账: %+v\n", chain, msg)
|
go func() {
|
||||||
return srv.Transfer(msg)
|
srv.ListenMsg()
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("链未注册: %s", chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BlockChainServer) Transfer(chain, from, to, symbol string, amount, fee float64) error {
|
||||||
|
if srv, ok := b.chains[chain]; ok {
|
||||||
|
fmt.Printf("💸 发起转账: Chain(%s)-Symbol(%s), From(%s), To(%s), Amount(%f)\n", chain, symbol, from, to, amount)
|
||||||
|
go func() {
|
||||||
|
err := srv.Transfer(from, to, symbol, amount, fee)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Transfer Error: %v\n Transfer has Exited", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("链未注册: %s", chain)
|
return fmt.Errorf("链未注册: %s", chain)
|
||||||
}
|
}
|
||||||
@@ -72,6 +74,7 @@ func (b *BlockChainServer) Stop(chain string) {
|
|||||||
if srv, ok := b.chains[chain]; ok {
|
if srv, ok := b.chains[chain]; ok {
|
||||||
fmt.Printf("💸 关闭服务: %+v\n", chain)
|
fmt.Printf("💸 关闭服务: %+v\n", chain)
|
||||||
srv.Stop()
|
srv.Stop()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
fmt.Printf("链未注册: %s", chain)
|
fmt.Printf("链未注册: %s", chain)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
pragma solidity ^0.8.0;
|
|
||||||
|
|
||||||
contract MultiSend {
|
|
||||||
function multiTransfer(address[] calldata to, uint256[] calldata amounts) external payable {
|
|
||||||
uint256 length = to.length;
|
|
||||||
require(length == amounts.length, "Arrays must have the same length");
|
|
||||||
|
|
||||||
for (uint256 i = 0; i < length; i++) {
|
|
||||||
payable(to[i]).transfer(amounts[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
pragma solidity ^0.8.0;
|
|
||||||
|
|
||||||
interface IERC20 {
|
|
||||||
function transfer(address recipient, uint256 amount) external returns (bool);
|
|
||||||
}
|
|
||||||
|
|
||||||
contract MultiSendUSDT {
|
|
||||||
address public tokenAddress;
|
|
||||||
|
|
||||||
constructor(address _tokenAddress) {
|
|
||||||
tokenAddress = _tokenAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function multiTransfer(address[] calldata to, uint256[] calldata amounts) external {
|
|
||||||
IERC20 token = IERC20(tokenAddress);
|
|
||||||
uint256 length = to.length;
|
|
||||||
require(length == amounts.length, "Arrays must have the same length");
|
|
||||||
|
|
||||||
for (uint256 i = 0; i < length; i++) {
|
|
||||||
token.transfer(to[i], amounts[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
1295
internal/blockchain/eth/eth_prv.go
Normal file
1295
internal/blockchain/eth/eth_prv.go
Normal file
File diff suppressed because it is too large
Load Diff
44
internal/constant/constant.go
Normal file
44
internal/constant/constant.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package constant
|
||||||
|
|
||||||
|
const (
|
||||||
|
STATUS_FAILED = 0 // 失败
|
||||||
|
STATUS_SUCCESS = 1 // 成功
|
||||||
|
STATUS_PENDING = 2 // 待确认
|
||||||
|
STATUS_VERIFY_FAILED = 3 // 验证失败
|
||||||
|
STATUS_BALANCE_NOT_ENOUGH = 4 // 钱包余额不足
|
||||||
|
STATUS_UNTRANSFER = 5 // 未支付
|
||||||
|
STATUS_END = 6 // 完成
|
||||||
|
STATUS_ERROR = 7
|
||||||
|
)
|
||||||
|
const ETH_ERC20_USDT_CONTRACT_ADDRESS = "0xdAC17F958D2ee523a2206206994597C13D831ec7"
|
||||||
|
const ETH_ERC20_ABI = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [{"name": "_owner", "type": "address"}],
|
||||||
|
"name": "balanceOf",
|
||||||
|
"outputs": [{"name": "balance", "type": "uint256"}],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": false,
|
||||||
|
"inputs": [
|
||||||
|
{"name": "_to", "type": "address"},
|
||||||
|
{"name": "_value", "type": "uint256"}
|
||||||
|
],
|
||||||
|
"name": "transfer",
|
||||||
|
"outputs": [{"name": "", "type": "bool"}],
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{"indexed": true, "name": "from", "type": "address"},
|
||||||
|
{"indexed": true, "name": "to", "type": "address"},
|
||||||
|
{"indexed": false,"name": "value","type": "uint256"}
|
||||||
|
],
|
||||||
|
"name": "Transfer",
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
`
|
||||||
@@ -13,7 +13,7 @@ type MySQLPool struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewMySQLPool 初始化连接池
|
// NewMySQLPool 初始化连接池
|
||||||
func NewMySQLPool(cfg message.DbConfig) (*MySQLPool, error) {
|
func NewMySQLPool(cfg message.MysqlConfig) (*MySQLPool, error) {
|
||||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||||
cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.Database)
|
cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.Database)
|
||||||
db, err := sql.Open("mysql", dsn)
|
db, err := sql.Open("mysql", dsn)
|
||||||
@@ -9,15 +9,20 @@ import (
|
|||||||
|
|
||||||
type SQLite struct {
|
type SQLite struct {
|
||||||
DB *sql.DB
|
DB *sql.DB
|
||||||
|
// Ch chan any
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化连接
|
// 初始化连接
|
||||||
func NewSQLite(path string) (*SQLite, error) {
|
func NewSQLite(path string) (*SQLite, error) {
|
||||||
db, err := sql.Open("sqlite", path)
|
db, err := sql.Open("sqlite", path)
|
||||||
|
// ch := make(chan any, 1000)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("open sqlite failed: %v", err)
|
return nil, fmt.Errorf("open sqlite failed: %v", err)
|
||||||
}
|
}
|
||||||
return &SQLite{DB: db}, nil
|
return &SQLite{
|
||||||
|
DB: db,
|
||||||
|
// Ch: ch,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭数据库
|
// 关闭数据库
|
||||||
@@ -27,8 +32,8 @@ func (s *SQLite) Close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通用新建表
|
// 新建表
|
||||||
func (s *SQLite) Exec_(sql string) error {
|
func (s *SQLite) CreateTable(sql string) error {
|
||||||
_, err := s.DB.Exec(sql)
|
_, err := s.DB.Exec(sql)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Exec DB error: %w", err)
|
return fmt.Errorf("Exec DB error: %w", err)
|
||||||
@@ -36,6 +41,48 @@ func (s *SQLite) Exec_(sql string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 插入数据(支持事务和批量)
|
||||||
|
func (s *SQLite) Insert(sqlStr string, args ...[]any) error {
|
||||||
|
tx, err := s.DB.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stmt, err := tx.Prepare(sqlStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
for _, a := range args {
|
||||||
|
_, err = stmt.Exec(a...)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return fmt.Errorf("exec insert error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除数据
|
||||||
|
func (s *SQLite) Delete(sqlStr string, args ...any) (int64, error) {
|
||||||
|
res, err := s.DB.Exec(sqlStr, args...)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("delete error: %v", err)
|
||||||
|
}
|
||||||
|
rows, _ := res.RowsAffected()
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新数据
|
||||||
|
func (s *SQLite) Update(sqlStr string, args ...any) (int64, error) {
|
||||||
|
res, err := s.DB.Exec(sqlStr, args...)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("update error: %v", err)
|
||||||
|
}
|
||||||
|
rows, _ := res.RowsAffected()
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
// 通用查询方法(返回[]map[string]any)
|
// 通用查询方法(返回[]map[string]any)
|
||||||
func (s *SQLite) Query_(query string, args ...any) ([]map[string]any, error) {
|
func (s *SQLite) Query_(query string, args ...any) ([]map[string]any, error) {
|
||||||
rows, err := s.DB.Query(query, args...)
|
rows, err := s.DB.Query(query, args...)
|
||||||
@@ -77,44 +124,44 @@ func (s *SQLite) Query_(query string, args ...any) ([]map[string]any, error) {
|
|||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 插入数据(支持事务和批量)
|
// 事务增删改(多条增删改串行,只要有一条增删改失败则全部回滚并返回错误)
|
||||||
func (s *SQLite) Insert(sqlStr string, args ...[]any) error {
|
func (s *SQLite) ExecuteTransactions(str_sqls []string, params [][]any) error {
|
||||||
|
// 检查 SQL 和参数的数量是否匹配
|
||||||
|
if len(str_sqls) != len(params) {
|
||||||
|
return fmt.Errorf("sql length != params length")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始事务
|
||||||
tx, err := s.DB.Begin()
|
tx, err := s.DB.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to begin transaction: %v", err)
|
||||||
}
|
}
|
||||||
stmt, err := tx.Prepare(sqlStr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
for _, a := range args {
|
// 确保在函数结束时提交或回滚事务
|
||||||
_, err = stmt.Exec(a...)
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
// 发生错误时回滚事务
|
||||||
return fmt.Errorf("exec insert error: %v", err)
|
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||||
|
err = fmt.Errorf("failed to rollback transaction: %v", rollbackErr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果没有错误,提交事务
|
||||||
|
if commitErr := tx.Commit(); commitErr != nil {
|
||||||
|
err = fmt.Errorf("failed to commit transaction: %v", commitErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 执行每个 SQL 语句
|
||||||
|
for i, sql_str := range str_sqls {
|
||||||
|
// 使用事务对象 tx 来执行 SQL
|
||||||
|
_, err := tx.Exec(sql_str, params[i]...)
|
||||||
|
if err != nil {
|
||||||
|
// 如果执行失败,立即返回并且触发回滚
|
||||||
|
return fmt.Errorf("failed to execute SQL: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tx.Commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新数据
|
// 如果所有 SQL 执行成功,则返回 nil
|
||||||
func (s *SQLite) Update(sqlStr string, args ...any) (int64, error) {
|
return nil
|
||||||
res, err := s.DB.Exec(sqlStr, args...)
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("update error: %v", err)
|
|
||||||
}
|
|
||||||
rows, _ := res.RowsAffected()
|
|
||||||
return rows, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除数据
|
|
||||||
func (s *SQLite) Delete(sqlStr string, args ...any) (int64, error) {
|
|
||||||
res, err := s.DB.Exec(sqlStr, args...)
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("delete error: %v", err)
|
|
||||||
}
|
|
||||||
rows, _ := res.RowsAffected()
|
|
||||||
return rows, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
328
internal/listen/listen.go
Normal file
328
internal/listen/listen.go
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
package listen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"m2pool-payment/internal/db"
|
||||||
|
message "m2pool-payment/internal/msg"
|
||||||
|
"m2pool-payment/internal/utils"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListenServer struct {
|
||||||
|
Config message.Config
|
||||||
|
// MysqlDB *db.MySQLPool
|
||||||
|
SqliteDB *db.SQLite
|
||||||
|
TopupMsgs map[string]*TopupMsgs // {"ETH": TopupMsgs{"queue_id": message.Topupmsg_req{}, ...}}
|
||||||
|
WithdrawMsgs map[string]*WithdrawMsgs
|
||||||
|
PayMsgs map[string]*PayMsgs
|
||||||
|
RemoveMsgs map[string]*RemoveMsgs
|
||||||
|
ChToChainServer map[string]chan any
|
||||||
|
ChFromChainServer map[string]chan any
|
||||||
|
ChFromRmqServer chan any
|
||||||
|
ChToRmqServer chan any
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type TopupMsgs struct {
|
||||||
|
Msgs map[string]message.TopupMsg_req // {"queue_id": message.Topupmsg_req{}, ...}
|
||||||
|
mu sync.RWMutex // 读写锁
|
||||||
|
}
|
||||||
|
|
||||||
|
type WithdrawMsgs struct {
|
||||||
|
Msgs map[string]message.WithdrawMsg_req
|
||||||
|
mu sync.RWMutex // 读写锁
|
||||||
|
}
|
||||||
|
|
||||||
|
type PayMsgs struct {
|
||||||
|
Msgs map[string]message.PayMsg_req
|
||||||
|
mu sync.RWMutex // 读写锁
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoveMsgs struct {
|
||||||
|
Msgs map[string]message.RemoveListenMsg_req
|
||||||
|
mu sync.RWMutex // 读写锁
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewListenServer(cfg message.Config) *ListenServer {
|
||||||
|
// // 初始化MySQL数据库
|
||||||
|
// dbConn, err := db.NewMySQLPool(cfg.MysqlConfig["wallet"])
|
||||||
|
// if err != nil {
|
||||||
|
// log.Printf("mysql connect error: %v", err)
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 初始化SQLite3
|
||||||
|
sqlite, err := db.NewSQLite(cfg.MsgConfig.SqlitePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("sqlite3 connect error: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
nets := cfg.Net
|
||||||
|
topup_msgs := make(map[string]*TopupMsgs)
|
||||||
|
withdraw_msgs := make(map[string]*WithdrawMsgs)
|
||||||
|
pay_msgs := make(map[string]*PayMsgs)
|
||||||
|
remove_msgs := make(map[string]*RemoveMsgs)
|
||||||
|
for _, net := range nets {
|
||||||
|
topup_msgs[net] = &TopupMsgs{
|
||||||
|
Msgs: make(map[string]message.TopupMsg_req),
|
||||||
|
}
|
||||||
|
withdraw_msgs[net] = &WithdrawMsgs{
|
||||||
|
Msgs: make(map[string]message.WithdrawMsg_req),
|
||||||
|
}
|
||||||
|
pay_msgs[net] = &PayMsgs{
|
||||||
|
Msgs: make(map[string]message.PayMsg_req),
|
||||||
|
}
|
||||||
|
remove_msgs[net] = &RemoveMsgs{
|
||||||
|
Msgs: make(map[string]message.RemoveListenMsg_req),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ch := make(map[string]chan any)
|
||||||
|
chin := make(map[string]chan any)
|
||||||
|
rmq_ch_in := make(chan any, 1000)
|
||||||
|
rmq_ch_out := make(chan any, 1000)
|
||||||
|
for _, net := range cfg.Net {
|
||||||
|
ch[net] = make(chan any, 1000)
|
||||||
|
chin[net] = make(chan any, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("✅ 消息监听处理已启动")
|
||||||
|
return &ListenServer{
|
||||||
|
Config: cfg,
|
||||||
|
// MysqlDB: dbConn,
|
||||||
|
SqliteDB: sqlite,
|
||||||
|
TopupMsgs: topup_msgs,
|
||||||
|
WithdrawMsgs: withdraw_msgs,
|
||||||
|
PayMsgs: pay_msgs,
|
||||||
|
RemoveMsgs: remove_msgs,
|
||||||
|
ChToChainServer: ch,
|
||||||
|
ChFromChainServer: chin,
|
||||||
|
ChFromRmqServer: rmq_ch_in,
|
||||||
|
ChToRmqServer: rmq_ch_out,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// rmq -> listen -> chain server
|
||||||
|
func (l *ListenServer) RmqMsgIn() {
|
||||||
|
for msg := range l.ChFromRmqServer {
|
||||||
|
switch v := msg.(type) {
|
||||||
|
case message.TopupMsg_req:
|
||||||
|
if !utils.Contains(l.Config.Net, v.Chain) {
|
||||||
|
log.Printf("%s has not support", v.Chain)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go l.handleRmqTopup_req(v)
|
||||||
|
case message.WithdrawMsg_req:
|
||||||
|
if !utils.Contains(l.Config.Net, v.Chain) {
|
||||||
|
log.Printf("%s has not support", v.Chain)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go l.handleRmqWithdraw_req(v)
|
||||||
|
case message.PayMsg_req:
|
||||||
|
if !utils.Contains(l.Config.Net, v.Chain) {
|
||||||
|
log.Printf("%s has not support", v.Chain)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go l.handleRmqPay_req(v)
|
||||||
|
case message.RemoveListenMsg_req:
|
||||||
|
if !utils.Contains(l.Config.Net, v.Chain) {
|
||||||
|
log.Printf("%s has not support", v.Chain)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go l.handleRmqRemove_req(v)
|
||||||
|
default:
|
||||||
|
log.Printf("ListenMsg error: %v", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// chain server -> listen -> rmq
|
||||||
|
func (l *ListenServer) NetMsgIn() {
|
||||||
|
for _, ch := range l.ChFromChainServer {
|
||||||
|
for msg := range ch {
|
||||||
|
switch v := msg.(type) {
|
||||||
|
// 接收到区块链节点服务返回的resp消息,除了修改相关状态和数据库,还需通过rmq_out通道返回出去
|
||||||
|
case message.TopupMsg_resp:
|
||||||
|
go l.handleChainTopup_resp(v)
|
||||||
|
case message.WithdrawMsg_resp:
|
||||||
|
go l.handleChainWithdraw_resp(v)
|
||||||
|
case message.PayData:
|
||||||
|
go l.handleChainPay_resp(v)
|
||||||
|
case message.RemoveListenMsg_resp:
|
||||||
|
go l.handleChainRemove_resp(v)
|
||||||
|
default:
|
||||||
|
log.Printf("MsgType not found: %v", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据 address 查找对应的消息
|
||||||
|
func (l *ListenServer) FindMsgWithAddress(chain, address string) (any, error) {
|
||||||
|
// 检查链是否支持
|
||||||
|
if !utils.Contains(l.Config.Net, chain) {
|
||||||
|
return nil, fmt.Errorf("%s has not support", chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找 TopupMsgs
|
||||||
|
l.TopupMsgs[chain].mu.RLock()
|
||||||
|
for _, topup_msg := range l.TopupMsgs[chain].Msgs {
|
||||||
|
if address == topup_msg.Address {
|
||||||
|
l.TopupMsgs[chain].mu.RUnlock()
|
||||||
|
return topup_msg, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.TopupMsgs[chain].mu.RUnlock()
|
||||||
|
|
||||||
|
// 查找 WithdrawMsgs
|
||||||
|
l.WithdrawMsgs[chain].mu.RLock()
|
||||||
|
for _, withdraw_msg := range l.WithdrawMsgs[chain].Msgs {
|
||||||
|
if address == withdraw_msg.FromAddress {
|
||||||
|
l.WithdrawMsgs[chain].mu.RUnlock()
|
||||||
|
return withdraw_msg, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.WithdrawMsgs[chain].mu.RUnlock()
|
||||||
|
|
||||||
|
// 查找 PayMsgs
|
||||||
|
l.PayMsgs[chain].mu.RLock()
|
||||||
|
for _, pay_msg := range l.PayMsgs[chain].Msgs {
|
||||||
|
if address == pay_msg.FromAddress {
|
||||||
|
l.PayMsgs[chain].mu.RUnlock()
|
||||||
|
return pay_msg, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.PayMsgs[chain].mu.RUnlock()
|
||||||
|
|
||||||
|
// 如果没有找到匹配项,返回错误
|
||||||
|
return nil, fmt.Errorf("Address(%s) not found in any message", address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据address查找Topup消息
|
||||||
|
func (l *ListenServer) FindTopupMsgWithToaddress(chain, toAddress string) (message.TopupMsg_req, error) {
|
||||||
|
// 检查链是否支持
|
||||||
|
if !utils.Contains(l.Config.Net, chain) {
|
||||||
|
return message.TopupMsg_req{}, fmt.Errorf("%s has not support", chain)
|
||||||
|
}
|
||||||
|
l.TopupMsgs[chain].mu.RLock()
|
||||||
|
for _, topup_msg := range l.TopupMsgs[chain].Msgs {
|
||||||
|
if toAddress == topup_msg.Address {
|
||||||
|
l.TopupMsgs[chain].mu.RUnlock()
|
||||||
|
return topup_msg, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.TopupMsgs[chain].mu.RUnlock()
|
||||||
|
return message.TopupMsg_req{}, fmt.Errorf("Address(%s) not found in topup message", toAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据address查找Topup消息
|
||||||
|
func (l *ListenServer) FindWithdrawMsgWithToaddress(chain, fromAddress string) (message.WithdrawMsg_req, error) {
|
||||||
|
// 检查链是否支持
|
||||||
|
if !utils.Contains(l.Config.Net, chain) {
|
||||||
|
return message.WithdrawMsg_req{}, fmt.Errorf("%s has not support", chain)
|
||||||
|
}
|
||||||
|
l.WithdrawMsgs[chain].mu.RLock()
|
||||||
|
for _, withdraw_msg := range l.WithdrawMsgs[chain].Msgs {
|
||||||
|
if fromAddress == withdraw_msg.FromAddress {
|
||||||
|
l.WithdrawMsgs[chain].mu.RUnlock()
|
||||||
|
return withdraw_msg, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.WithdrawMsgs[chain].mu.RUnlock()
|
||||||
|
return message.WithdrawMsg_req{}, fmt.Errorf("Address(%s) not found in withdraw message", fromAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据address查找Topup消息
|
||||||
|
func (l *ListenServer) FindPayMsgWithToaddress(chain, fromAddress string) (message.PayMsg_req, error) {
|
||||||
|
// 检查链是否支持
|
||||||
|
if !utils.Contains(l.Config.Net, chain) {
|
||||||
|
return message.PayMsg_req{}, fmt.Errorf("%s has not support", chain)
|
||||||
|
}
|
||||||
|
l.PayMsgs[chain].mu.RLock()
|
||||||
|
for _, pay_msg := range l.PayMsgs[chain].Msgs {
|
||||||
|
if fromAddress == pay_msg.FromAddress {
|
||||||
|
l.PayMsgs[chain].mu.RUnlock()
|
||||||
|
return pay_msg, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.PayMsgs[chain].mu.RUnlock()
|
||||||
|
return message.PayMsg_req{}, fmt.Errorf("Address(%s) not found in pay message", fromAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据queue_id查找到对应消息
|
||||||
|
func (l *ListenServer) FindMsgWithQueueID(chain, queue_id string, txType int) (any, error) {
|
||||||
|
if !utils.Contains(l.Config.Net, chain) {
|
||||||
|
return nil, fmt.Errorf("%s has not support", chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch txType {
|
||||||
|
case 0:
|
||||||
|
l.TopupMsgs[chain].mu.RLock()
|
||||||
|
defer l.TopupMsgs[chain].mu.RUnlock()
|
||||||
|
|
||||||
|
if msg, ok := l.TopupMsgs[chain].Msgs[queue_id]; ok {
|
||||||
|
return msg, nil
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("QueueID(%s) not found in txType(%d)", queue_id, txType)
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
l.WithdrawMsgs[chain].mu.RLock()
|
||||||
|
defer l.WithdrawMsgs[chain].mu.RUnlock()
|
||||||
|
|
||||||
|
if msg, ok := l.WithdrawMsgs[chain].Msgs[queue_id]; ok {
|
||||||
|
return msg, nil
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("QueueID(%s) not found in txType(%d)", queue_id, txType)
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
l.PayMsgs[chain].mu.RLock()
|
||||||
|
defer l.PayMsgs[chain].mu.RUnlock()
|
||||||
|
|
||||||
|
if msg, ok := l.PayMsgs[chain].Msgs[queue_id]; ok {
|
||||||
|
return msg, nil
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("QueueID(%s) not found in txType(%d)", queue_id, txType)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("This txType(%d) don`t support:txType(0,1,2)", txType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *ListenServer) ReadTopupMsgs(chain string) (map[string]message.TopupMsg_req, error) {
|
||||||
|
l.TopupMsgs[chain].mu.RLock()
|
||||||
|
defer l.TopupMsgs[chain].mu.RUnlock()
|
||||||
|
|
||||||
|
var topup_msgs = make(map[string]message.TopupMsg_req)
|
||||||
|
if !utils.Contains(l.Config.Net, chain) {
|
||||||
|
return topup_msgs, fmt.Errorf("%s has not support", chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
topup_msgs = l.TopupMsgs[chain].Msgs
|
||||||
|
return topup_msgs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *ListenServer) ReadWithdrawMsgs(chain string) (map[string]message.WithdrawMsg_req, error) {
|
||||||
|
l.WithdrawMsgs[chain].mu.RLock()
|
||||||
|
defer l.WithdrawMsgs[chain].mu.RUnlock()
|
||||||
|
|
||||||
|
var withdraw_msgs = make(map[string]message.WithdrawMsg_req)
|
||||||
|
if !utils.Contains(l.Config.Net, chain) {
|
||||||
|
return withdraw_msgs, fmt.Errorf("%s has not support", chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
withdraw_msgs = l.WithdrawMsgs[chain].Msgs
|
||||||
|
return withdraw_msgs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *ListenServer) ReadPayMsgs(chain string) (map[string]message.PayMsg_req, error) {
|
||||||
|
l.PayMsgs[chain].mu.RLock()
|
||||||
|
defer l.PayMsgs[chain].mu.RUnlock()
|
||||||
|
|
||||||
|
var pay_msgs = make(map[string]message.PayMsg_req)
|
||||||
|
if !utils.Contains(l.Config.Net, chain) {
|
||||||
|
return pay_msgs, fmt.Errorf("%s has not support", chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
pay_msgs = l.PayMsgs[chain].Msgs
|
||||||
|
return pay_msgs, nil
|
||||||
|
}
|
||||||
295
internal/listen/listen_prv.go
Normal file
295
internal/listen/listen_prv.go
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
package listen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"m2pool-payment/internal/constant"
|
||||||
|
message "m2pool-payment/internal/msg"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 充值消息
|
||||||
|
func (l *ListenServer) handleRmqTopup_req(msg message.TopupMsg_req) {
|
||||||
|
// 添加到TopupMsgs
|
||||||
|
l.TopupMsgs[msg.Chain].mu.RLock()
|
||||||
|
l.TopupMsgs[msg.Chain].Msgs[msg.QueueId] = msg
|
||||||
|
l.TopupMsgs[msg.Chain].mu.RUnlock()
|
||||||
|
// 写数据库
|
||||||
|
go func() {
|
||||||
|
sql := "INSERT INTO topup_req_msg (queue_id, chain, symbol, address, timestamp, sign, status) VALUES (?,?,?,?,?,?,?)"
|
||||||
|
params := []any{msg.QueueId, msg.Chain, msg.Symbol, msg.Address, msg.Timestamp, msg.Sign, 1}
|
||||||
|
err := l.SqliteDB.Insert(sql, params)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Insert Topup_req msg error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// 传给对应的node server
|
||||||
|
go l.asyncSendMsgToChain(msg, msg.Chain, 3, 5*time.Second)
|
||||||
|
log.Printf("Insert Topup_req msg success: QueueId(%s)", msg.QueueId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提现消息
|
||||||
|
func (l *ListenServer) handleRmqWithdraw_req(msg message.WithdrawMsg_req) {
|
||||||
|
// 添加到WithdrawMsgs
|
||||||
|
l.WithdrawMsgs[msg.Chain].mu.RLock()
|
||||||
|
l.WithdrawMsgs[msg.Chain].Msgs[msg.QueueId] = msg
|
||||||
|
l.WithdrawMsgs[msg.Chain].mu.RUnlock()
|
||||||
|
// 写数据库
|
||||||
|
go func() {
|
||||||
|
sql := "INSERT INTO withdraw_req_msg (queue_id, from_addr, to_addr, amount, fee, chain, symbol, timestamp, sign) VALUES (?,?,?,?,?,?,?,?,?)"
|
||||||
|
params := []any{msg.QueueId, msg.FromAddress, msg.ToAddress, fmt.Sprintf("%f", msg.Amount), fmt.Sprintf("%f", msg.Fee), msg.Chain, msg.Symbol, msg.Timestamp, msg.Sign}
|
||||||
|
err := l.SqliteDB.Insert(sql, params)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Insert Withdraw_req msg error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// 传给对应的node server
|
||||||
|
go l.asyncSendMsgToChain(msg, msg.Chain, 3, 5*time.Second)
|
||||||
|
log.Printf("Insert Withdraw_req msg success: QueueId(%s)", msg.QueueId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支付消息
|
||||||
|
func (l *ListenServer) handleRmqPay_req(msg message.PayMsg_req) {
|
||||||
|
l.PayMsgs[msg.Chain].mu.RLock()
|
||||||
|
l.PayMsgs[msg.Chain].Msgs[msg.QueueId] = msg
|
||||||
|
l.PayMsgs[msg.Chain].mu.RUnlock()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
sql1 := "INSERT INTO pay_req_msg (queue_id, chain, symbol, from_addr, total_amount, total_fee, timestamp, sign) VALUES (?,?,?,?,?,?,?,?)"
|
||||||
|
sql2 := "INSERT INTO transactions (queue_id, chain, symbol, from_addr, to_addr, amount, fee) VALUES "
|
||||||
|
params1 := []any{msg.QueueId, msg.Chain, msg.Symbol, msg.FromAddress, fmt.Sprintf("%f", msg.TotalAmount), fmt.Sprintf("%f", msg.TotalFee), msg.Timestamp, msg.Sign}
|
||||||
|
params2 := []any{}
|
||||||
|
for to_addr, tx := range msg.Transactions {
|
||||||
|
sql2 += "(?,?,?,?,?,?,?), "
|
||||||
|
params2 = append(params2, msg.QueueId, msg.Chain, msg.Symbol, strings.ToLower(msg.FromAddress), strings.ToLower(to_addr), fmt.Sprintf("%f", tx.Amount), fmt.Sprintf("%f", tx.Fee))
|
||||||
|
}
|
||||||
|
sql2 = sql2[:len(sql2)-2]
|
||||||
|
err := l.SqliteDB.ExecuteTransactions([]string{sql1, sql2}, [][]any{params1, params2})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Insert Pay_req msg error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go l.asyncSendMsgToChain(msg, msg.Chain, 3, 5*time.Second)
|
||||||
|
log.Printf("Insert Pay_req msg success: QueueId(%s)", msg.QueueId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除监听消息
|
||||||
|
func (l *ListenServer) handleRmqRemove_req(msg message.RemoveListenMsg_req) {
|
||||||
|
l.RemoveMsgs[msg.Chain].mu.RLock()
|
||||||
|
l.RemoveMsgs[msg.Chain].Msgs[msg.QueueId] = msg
|
||||||
|
l.RemoveMsgs[msg.Chain].mu.RUnlock()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
sql := "INSERT INTO remove_req_msg (queue_id, msg_type, chain, symbol, address, timestamp, sign) VALUES (?,?,?,?,?,?,?)"
|
||||||
|
params := []any{msg.QueueId, msg.MsgType, msg.Chain, msg.Symbol, msg.Address, msg.Timestamp, msg.Sign}
|
||||||
|
err := l.SqliteDB.Insert(sql, params)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Insert Remove_req msg error: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go l.asyncSendMsgToChain(msg, msg.Chain, 3, 5*time.Second)
|
||||||
|
log.Printf("Insert Remove_req msg success: QueueId(%s)", msg.QueueId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 充值响应
|
||||||
|
func (l *ListenServer) handleChainTopup_resp(msg message.TopupMsg_resp) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提现响应
|
||||||
|
func (l *ListenServer) handleChainWithdraw_resp(msg message.WithdrawMsg_resp) {
|
||||||
|
switch msg.Status {
|
||||||
|
// pending状态表示转账已完成且在区块链中被找到,等待后续确认+记录到数据库
|
||||||
|
case constant.STATUS_PENDING:
|
||||||
|
go func() {
|
||||||
|
str := "INSERT INTO withdraw_resp_msg (queue_id, chain, symbol, from_addr, to_addr, tx_hash, amount, fee, height, status) VALUES (?,?,?,?,?,?,?,?,?,?)"
|
||||||
|
params := []any{msg.QueueId, msg.Chain, msg.Symbol, msg.FromAddress, msg.ToAddress, msg.TxHash, msg.Amount, msg.Fee, msg.BlockHeight, msg.Status}
|
||||||
|
err := l.SqliteDB.Insert(str, params)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
case constant.STATUS_SUCCESS, constant.STATUS_FAILED:
|
||||||
|
l.WithdrawMsgs[msg.Chain].mu.Lock()
|
||||||
|
delete(l.WithdrawMsgs[msg.Chain].Msgs, msg.QueueId)
|
||||||
|
l.WithdrawMsgs[msg.Chain].mu.Unlock()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
str := "UPDATE withdraw_resp_msg SET status = ? WHERE tx_hash = ?"
|
||||||
|
params := []any{msg.Status, msg.TxHash}
|
||||||
|
count, err := l.SqliteDB.Update(str, params)
|
||||||
|
if err != nil || count != 1 {
|
||||||
|
log.Printf("count=%d, err=%v", count, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go l.asyncSendMsgToRmq(msg, 3, 5*time.Second)
|
||||||
|
default:
|
||||||
|
l.WithdrawMsgs[msg.Chain].mu.Lock()
|
||||||
|
delete(l.WithdrawMsgs[msg.Chain].Msgs, msg.QueueId)
|
||||||
|
l.WithdrawMsgs[msg.Chain].mu.Unlock()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
str := "INSERT INTO withdraw_resp_msg (queue_id, chain, symbol, from_addr, to_addr, amount, fee, status) VALUES (?,?,?,?,?,?,?,?)"
|
||||||
|
params := []any{msg.QueueId, msg.Chain, msg.Symbol, msg.FromAddress, msg.ToAddress, fmt.Sprintf("%f", msg.Amount), fmt.Sprintf("%f", msg.Fee), msg.Status}
|
||||||
|
err := l.SqliteDB.Insert(str, params)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go l.asyncSendMsgToRmq(msg, 3, 5*time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支付响应
|
||||||
|
func (l *ListenServer) handleChainPay_resp(msg message.PayData) {
|
||||||
|
switch msg.Status {
|
||||||
|
case constant.STATUS_PENDING:
|
||||||
|
l.PayMsgs[msg.Chain].mu.Lock()
|
||||||
|
l.PayMsgs[msg.Chain].Msgs[msg.QueueId].Transactions[msg.ToAddress] = msg
|
||||||
|
l.PayMsgs[msg.Chain].mu.Unlock()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
str := "INSERT INTO pay_msg_tx (queue_id, tx_hash, to_addr, amount, fee, height, status) VALUES (?,?,?,?,?,?,?)"
|
||||||
|
params := []any{msg.QueueId, msg.TxHash, msg.ToAddress, fmt.Sprintf("%f", msg.Amount), fmt.Sprintf("%f", msg.Fee), msg.BlockHeight, msg.Status}
|
||||||
|
err := l.SqliteDB.Insert(str, params)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Insert pay_msg_tx msg error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
case constant.STATUS_SUCCESS, constant.STATUS_FAILED:
|
||||||
|
l.PayMsgs[msg.Chain].mu.Lock()
|
||||||
|
l.PayMsgs[msg.Chain].Msgs[msg.QueueId].Transactions[msg.ToAddress] = msg
|
||||||
|
l.PayMsgs[msg.Chain].mu.Unlock()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
str := "UPDATE pay_msg_tx SET status = ? WHERE tx_hash = ?"
|
||||||
|
params := []any{msg.Status, msg.TxHash}
|
||||||
|
count, err := l.SqliteDB.Update(str, params...)
|
||||||
|
if err != nil {
|
||||||
|
// 更详细的错误日志,包括 QueueId 和 Status
|
||||||
|
log.Printf("Failed to update remove_resp_msg for queue_id %s: %v", msg.QueueId, err)
|
||||||
|
} else if count != 1 {
|
||||||
|
// 如果更新的行数不是 1,日志中记录详细信息
|
||||||
|
log.Printf("Unexpected update count for queue_id %s: expected 1, got %d", msg.QueueId, count)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
need_send := true
|
||||||
|
l.PayMsgs[msg.Chain].mu.Lock()
|
||||||
|
for _, tx := range l.PayMsgs[msg.Chain].Msgs[msg.QueueId].Transactions {
|
||||||
|
if tx.Status != constant.STATUS_SUCCESS && tx.Status != constant.STATUS_FAILED {
|
||||||
|
need_send = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.PayMsgs[msg.Chain].mu.Unlock()
|
||||||
|
if need_send {
|
||||||
|
str := "INSERT INTO pay_resp_msg (queue_id, chain, symbol, from_addr, pay_status) VALUES (?,?,?,?,?)"
|
||||||
|
params := []any{l.PayMsgs[msg.Chain].Msgs[msg.QueueId].QueueId, l.PayMsgs[msg.Chain].Msgs[msg.QueueId].Chain, l.PayMsgs[msg.Chain].Msgs[msg.QueueId].Symbol, l.PayMsgs[msg.Chain].Msgs[msg.QueueId].FromAddress, constant.STATUS_SUCCESS}
|
||||||
|
err := l.SqliteDB.Insert(str, params)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Insert pay_resp_msg msg error: %v", err)
|
||||||
|
}
|
||||||
|
resp := message.PayMsg_resp{
|
||||||
|
QueueId: msg.QueueId,
|
||||||
|
Chain: l.PayMsgs[msg.Chain].Msgs[msg.QueueId].Chain,
|
||||||
|
Symbol: l.PayMsgs[msg.Chain].Msgs[msg.QueueId].Symbol,
|
||||||
|
FromAddress: l.PayMsgs[msg.Chain].Msgs[msg.QueueId].FromAddress,
|
||||||
|
PayStatus: constant.STATUS_SUCCESS,
|
||||||
|
Transactions: l.PayMsgs[msg.Chain].Msgs[msg.QueueId].Transactions,
|
||||||
|
}
|
||||||
|
go l.asyncSendMsgToRmq(resp, 3, 5*time.Second)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
default:
|
||||||
|
l.PayMsgs[msg.Chain].mu.Lock()
|
||||||
|
l.PayMsgs[msg.Chain].Msgs[msg.QueueId].Transactions[msg.ToAddress] = msg
|
||||||
|
l.PayMsgs[msg.Chain].mu.Unlock()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
str := "UPDATE pay_msg_tx SET status = ? WHERE tx_hash = ?"
|
||||||
|
params := []any{msg.Status, msg.TxHash}
|
||||||
|
count, err := l.SqliteDB.Update(str, params...)
|
||||||
|
if err != nil {
|
||||||
|
// 更详细的错误日志,包括 QueueId 和 Status
|
||||||
|
log.Printf("Failed to update remove_resp_msg for queue_id %s: %v", msg.QueueId, err)
|
||||||
|
} else if count != 1 {
|
||||||
|
// 如果更新的行数不是 1,日志中记录详细信息
|
||||||
|
log.Printf("Unexpected update count for queue_id %s: expected 1, got %d", msg.QueueId, count)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除监听响应
|
||||||
|
func (l *ListenServer) handleChainRemove_resp(msg message.RemoveListenMsg_resp) {
|
||||||
|
// 如果状态为成功,移除该 QueueId
|
||||||
|
if msg.Status == constant.STATUS_SUCCESS {
|
||||||
|
l.RemoveMsgs[msg.Chain].mu.Lock()
|
||||||
|
delete(l.RemoveMsgs[msg.Chain].Msgs, msg.QueueId)
|
||||||
|
l.RemoveMsgs[msg.Chain].mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 goroutine 异步更新数据库
|
||||||
|
go func() {
|
||||||
|
// 更新数据库
|
||||||
|
str := "UPDATE remove_resp_msg SET status = ? WHERE queue_id = ?"
|
||||||
|
params := []any{msg.Status, msg.QueueId}
|
||||||
|
count, err := l.SqliteDB.Update(str, params...)
|
||||||
|
if err != nil {
|
||||||
|
// 更详细的错误日志,包括 QueueId 和 Status
|
||||||
|
log.Printf("Failed to update remove_resp_msg for queue_id %s: %v", msg.QueueId, err)
|
||||||
|
} else if count != 1 {
|
||||||
|
// 如果更新的行数不是 1,日志中记录详细信息
|
||||||
|
log.Printf("Unexpected update count for queue_id %s: expected 1, got %d", msg.QueueId, count)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 异步发送消息到 RMQ
|
||||||
|
go l.asyncSendMsgToRmq(msg, 3, 5*time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步发送通道消息,Listen -> Chain server
|
||||||
|
func (l *ListenServer) asyncSendMsgToChain(msg any, chain string, retries int, timeout time.Duration) {
|
||||||
|
for retries > 0 {
|
||||||
|
select {
|
||||||
|
case l.ChToChainServer[chain] <- msg: // 如果通道没有满,就发送消息
|
||||||
|
// log.Printf("Sent message to rmq_ch_out: %v", msg)
|
||||||
|
return
|
||||||
|
case <-time.After(timeout): // 超时控制
|
||||||
|
log.Printf("Timeout sending message to rmq_ch_out: %v", msg)
|
||||||
|
retries--
|
||||||
|
if retries == 0 {
|
||||||
|
log.Printf("Max retries reached, giving up on sending message: %v", msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 在超时后进行重试
|
||||||
|
log.Printf("Retrying sending message: %v, retries left: %d", msg, retries)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 异步发送通道消息,Listen -> Rmq
|
||||||
|
func (l *ListenServer) asyncSendMsgToRmq(msg any, retries int, timeout time.Duration) {
|
||||||
|
for retries > 0 {
|
||||||
|
select {
|
||||||
|
case l.ChToRmqServer <- msg: // 如果通道没有满,就发送消息
|
||||||
|
// log.Printf("Sent message to rmq_ch_out: %v", msg)
|
||||||
|
return
|
||||||
|
case <-time.After(timeout): // 超时控制
|
||||||
|
log.Printf("Timeout sending message to rmq_ch_out: %v", msg)
|
||||||
|
retries--
|
||||||
|
if retries == 0 {
|
||||||
|
log.Printf("Max retries reached, giving up on sending message: %v", msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 在超时后进行重试
|
||||||
|
log.Printf("Retrying sending message: %v, retries left: %d", msg, retries)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -211,20 +211,20 @@ func LogTopup(toAddress string, status string, amount float64, txHash string, bl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LogWithdraw 记录提现消息
|
// LogWithdraw 记录提现消息
|
||||||
func LogWithdraw(toAddress string, status string, amount float64, fromAddress string, txHash string, blockHeight uint64) {
|
func LogWithdraw(fromAddress string, status string, amount float64, toAddress string, txHash string, blockHeight uint64) {
|
||||||
if txLogger == nil {
|
if txLogger == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用 toAddress 作为文件名
|
// 使用 toAddress 作为文件名
|
||||||
lf, err := txLogger.getOrCreateLogFile(toAddress)
|
lf, err := txLogger.getOrCreateLogFile(fromAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("⚠️ 获取日志文件失败: %v\n", err)
|
fmt.Printf("⚠️ 获取日志文件失败: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
||||||
content := fmt.Sprintf("%s [withdraw]-[%s] | 金额: %.6f | FromAddress: %s | ToAddress: %s | 交易哈希: %s | 区块高度: %d",
|
content := fmt.Sprintf("%s [提现]-[%s] | 金额: %.6f | FromAddress: %s | ToAddress: %s | 交易哈希: %s | 区块高度: %d",
|
||||||
timestamp, status, amount, fromAddress, toAddress, txHash, blockHeight)
|
timestamp, status, amount, fromAddress, toAddress, txHash, blockHeight)
|
||||||
|
|
||||||
if err := lf.write(content); err != nil {
|
if err := lf.write(content); err != nil {
|
||||||
@@ -233,7 +233,7 @@ func LogWithdraw(toAddress string, status string, amount float64, fromAddress st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LogPay 记录支付消息
|
// LogPay 记录支付消息
|
||||||
func LogPay(status string, fromAddress string, queueId string, transactions map[string]*message.PayData_resp) {
|
func LogPay(status string, fromAddress string, queueId string, transactions map[string]*message.PayData) {
|
||||||
if txLogger == nil {
|
if txLogger == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -258,6 +258,26 @@ func LogPay(status string, fromAddress string, queueId string, transactions map[
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 记录当前监听的钱包和所有消息
|
||||||
|
func LogETHNode(msg string) {
|
||||||
|
if txLogger == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lf, err := txLogger.getOrCreateLogFile("ethnode")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("⚠️ 获取日志文件失败: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
||||||
|
content := fmt.Sprintf("[ETH-NODE:%s]: %s", timestamp, msg)
|
||||||
|
|
||||||
|
if err := lf.write(content); err != nil {
|
||||||
|
fmt.Printf("⚠️ 写入日志失败: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Close 关闭所有日志文件
|
// Close 关闭所有日志文件
|
||||||
func CloseTransactionLogger() {
|
func CloseTransactionLogger() {
|
||||||
if txLogger == nil {
|
if txLogger == nil {
|
||||||
|
|||||||
53
internal/msg/config.go
Normal file
53
internal/msg/config.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package msg
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// 配置文件结构
|
||||||
|
type Config struct {
|
||||||
|
Net []string `json:"net"`
|
||||||
|
RmqConfig RmqConfig `json:"rmq_config"`
|
||||||
|
MsgConfig MsgConfig `json:"msg_config"`
|
||||||
|
MysqlConfig map[string]MysqlConfig `json:"mysql_config"` // {dbname: MysqlConfig{}, ...}
|
||||||
|
ETHConfig ETHConfig `json:"eth_config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RmqConfig struct {
|
||||||
|
SubAddr string `json:"sub_addr"`
|
||||||
|
Pay Queue `json:"pay"`
|
||||||
|
Topup Queue `json:"topup"`
|
||||||
|
Withdraw Queue `json:"withdraw"`
|
||||||
|
Remove Queue `json:"remove"`
|
||||||
|
PayResp Queue `json:"pay_resp"`
|
||||||
|
TopupResp Queue `json:"topup_resp"`
|
||||||
|
WithdrawResp Queue `json:"withdraw_resp"`
|
||||||
|
RemoveResp Queue `json:"remove_resp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Queue struct {
|
||||||
|
Queue string `json:"queue"`
|
||||||
|
Exchange string `json:"exchange"`
|
||||||
|
Routing []string `json:"routing"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MsgConfig struct {
|
||||||
|
SqlitePath string `json:"sqlite_path"` // sqlite3数据库文件路径
|
||||||
|
}
|
||||||
|
|
||||||
|
type MysqlConfig struct {
|
||||||
|
User string `json:"user"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
Port int `json:"port"`
|
||||||
|
Database string `json:"database"`
|
||||||
|
MaxOpenConns int `json:"maxOpenConns"`
|
||||||
|
MaxIdleConns int `json:"maxIdleConns"`
|
||||||
|
ConnMaxLife time.Duration `json:"connMaxLife"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ETHConfig struct {
|
||||||
|
RpcUrl string `json:"rpc_url"`
|
||||||
|
WsUrl string `json:"ws_url"`
|
||||||
|
ConfirmHeight uint64 `json:"confirm_height"`
|
||||||
|
SqlitePath string `json:"sqlite_path"` // sqlite3数据库文件路径
|
||||||
|
SupportTokens []string `json:"support_tokens"` // 支持的代币列表,如["USDT", "DAI"]
|
||||||
|
}
|
||||||
@@ -1,61 +1,6 @@
|
|||||||
package msg
|
package msg
|
||||||
|
|
||||||
import "time"
|
import "math/big"
|
||||||
|
|
||||||
// 配置文件结构
|
|
||||||
type Config struct {
|
|
||||||
SQLite3 SQLite3 `json:"sqlite3"`
|
|
||||||
RMQConfig RMQConfig `json:"rmq_config"`
|
|
||||||
ETHConfig ETHConfig `json:"eth_config"`
|
|
||||||
TRONConfig TRONConfig `json:"tron_config"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SQLite3 struct {
|
|
||||||
MsgPath string `json:"msg_path"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RMQConfig struct {
|
|
||||||
SubAddr string `json:"sub_addr"` // 监听地址
|
|
||||||
PayConfig QueueConfig `json:"pay"` // 支付
|
|
||||||
TopUpConfig QueueConfig `json:"topup"` // 充值
|
|
||||||
WithdrawConfig QueueConfig `json:"withdraw"` // 提现
|
|
||||||
RemoveConfig QueueConfig `json:"remove"` // 移除监听
|
|
||||||
PayRespConfig QueueConfig `json:"pay_resp"` // 支付回复
|
|
||||||
TopUpRespConfig QueueConfig `json:"topup_resp"` // 充值回复
|
|
||||||
WithdrawRespConfig QueueConfig `json:"withdraw_resp"` // 提现回复
|
|
||||||
RemoveRespConfig QueueConfig `json:"remove_resp"` // 移除监听回复
|
|
||||||
}
|
|
||||||
|
|
||||||
type QueueConfig struct {
|
|
||||||
QueueName string `json:"queue"`
|
|
||||||
ExchangeName string `json:"exchange"`
|
|
||||||
Routing []string `json:"routing"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ETHConfig struct {
|
|
||||||
RpcURL string `json:"rpcUrl"` // rpc连接地址
|
|
||||||
WsURL string `json:"wsUrl"` // websocket连接地址
|
|
||||||
ConfirmHeight uint64 `json:"confirmHeight"` // 确认所需区块数
|
|
||||||
DbConfig DbConfig `json:"dbConfig"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TRONConfig struct {
|
|
||||||
RpcUrl string `json:"rpcUrl"`
|
|
||||||
ConfirmHeight uint64 `json:"confirmHeight"`
|
|
||||||
DbConfig DbConfig `json:"dbConfig"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config 数据库配置
|
|
||||||
type DbConfig struct {
|
|
||||||
User string `json:"user"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
Host string `json:"host"`
|
|
||||||
Port int `json:"port"`
|
|
||||||
Database string `json:"database"`
|
|
||||||
MaxOpenConns int `json:"maxOpenConns"` // 最大打开连接数
|
|
||||||
MaxIdleConns int `json:"maxIdleCoons"` // 最大空闲连接数
|
|
||||||
ConnMaxLife time.Duration `json:"connMaxLife"` // 连接最大存活时间
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================== type0 ===============================
|
// =============================== type0 ===============================
|
||||||
// 接收的充值消息
|
// 接收的充值消息
|
||||||
@@ -66,31 +11,32 @@ type TopupMsg_req struct {
|
|||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
Timestamp uint64 `json:"timestamp"`
|
Timestamp uint64 `json:"timestamp"`
|
||||||
Sign string `json:"sign"`
|
Sign string `json:"sign"`
|
||||||
Status int `json:"status,omitempty"`
|
Status int `json:"status,omitempty"` // 1监听中,2停止监听
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回充值结果消息
|
// 返回充值结果消息
|
||||||
type TopupMsg_resp struct {
|
type TopupMsg_resp struct {
|
||||||
QueueId string `json:"queue_id"`
|
QueueId string `json:"queue_id"`
|
||||||
Address string `json:"address"`
|
Chain string `json:"chain"` // 链名称
|
||||||
Status int `json:"status"` // 0失败,1成功,2待定,3sign校验失败
|
Symbol string `json:"symbol"` // 币种
|
||||||
Chain string `json:"chain"` // 链名称
|
FromAddress string `json:"from_address,omitempty"` // 充值来源地址
|
||||||
Symbol string `json:"symbol"` // 币种
|
Address string `json:"address"` // 充值目标地址
|
||||||
Amount float64 `json:"amount"`
|
TxHash string `json:"tx_hash,omitempty"`
|
||||||
TxHash string `json:"tx_hash"`
|
Amount float64 `json:"amount,omitempty"`
|
||||||
BlockHeight uint64 `json:"block_height"` // 区块高度
|
BlockHeight uint64 `json:"block_height,omitempty"` // 区块高度
|
||||||
|
Status int `json:"status"` // 0失败,1成功,2待定,3sign校验失败
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================== type1 ===============================
|
// =============================== type1 ===============================
|
||||||
// 接收的提现消息
|
// 接收的提现消息
|
||||||
type WithdrawMsg_req struct {
|
type WithdrawMsg_req struct {
|
||||||
QueueId string `json:"queue_id"`
|
QueueId string `json:"queue_id"`
|
||||||
|
Chain string `json:"chain"` // 链名称
|
||||||
|
Symbol string `json:"symbol"` // 币种
|
||||||
FromAddress string `json:"from_address"` // 我们提供的地址
|
FromAddress string `json:"from_address"` // 我们提供的地址
|
||||||
ToAddress string `json:"to_address"` // 用户要提现到的地址
|
ToAddress string `json:"to_address"` // 用户要提现到的地址
|
||||||
Amount float64 `json:"amount"`
|
Amount float64 `json:"amount"`
|
||||||
Chain string `json:"chain"` // 链名称
|
Fee float64 `json:"fee"`
|
||||||
Symbol string `json:"symbol"` // 币种
|
|
||||||
TxHash string `json:"tx_hash,omitempty"`
|
|
||||||
Timestamp uint64 `json:"timestamp"`
|
Timestamp uint64 `json:"timestamp"`
|
||||||
Sign string `json:"sign"`
|
Sign string `json:"sign"`
|
||||||
Status int `json:"status,omitempty"`
|
Status int `json:"status,omitempty"`
|
||||||
@@ -99,80 +45,52 @@ type WithdrawMsg_req struct {
|
|||||||
// 返回提现结果消息
|
// 返回提现结果消息
|
||||||
type WithdrawMsg_resp struct {
|
type WithdrawMsg_resp struct {
|
||||||
QueueId string `json:"queue_id"`
|
QueueId string `json:"queue_id"`
|
||||||
Chain string `json:"chain"` // 链名称
|
Chain string `json:"chain"` // 链名称
|
||||||
Symbol string `json:"symbol"` // 币种
|
Symbol string `json:"symbol"` // 币种
|
||||||
Status int `json:"status"` // 0失败,1成功,3sign校验失败
|
|
||||||
Amount float64 `json:"amount"`
|
|
||||||
TxHash string `json:"tx_hash"`
|
|
||||||
FromAddress string `json:"from_address"` // 来源地址
|
FromAddress string `json:"from_address"` // 来源地址
|
||||||
ToAddress string `json:"to_address"` // 目标地址
|
ToAddress string `json:"to_address"` // 目标地址
|
||||||
BlockHeight uint64 `json:"block_height"` // 区块高度
|
TxHash string `json:"tx_hash,omitempty"`
|
||||||
|
Amount float64 `json:"amount,omitempty"`
|
||||||
|
Fee float64 `json:"fee,omitempty"`
|
||||||
|
BlockHeight uint64 `json:"block_height,omitempty"` // 区块高度
|
||||||
|
Status int `json:"status"` // 0失败,1成功,3sign校验失败
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================== type2 ===============================
|
// =============================== type2 ===============================
|
||||||
|
|
||||||
type PayMsg_req struct {
|
type PayMsg_req struct {
|
||||||
QueueId string `json:"queue_id"`
|
QueueId string `json:"queue_id"`
|
||||||
FromAddress string `json:"from_address"`
|
Chain string `json:"chain"`
|
||||||
Chain string `json:"chain"`
|
Symbol string `json:"symbol"`
|
||||||
Symbol string `json:"symbol"`
|
FromAddress string `json:"from_address"`
|
||||||
TotalAmount float64 `json:"total_amount"`
|
TotalAmount float64 `json:"total_amount"`
|
||||||
Timestamp uint64 `json:"timestamp"`
|
TotalFee float64 `json:"total_fee"`
|
||||||
Sign string `json:"sign"`
|
Timestamp uint64 `json:"timestamp"`
|
||||||
Trasnactions map[string]*PayData_req `json:"transactions"` // {"to_address": PayData_req{}, ...}
|
Sign string `json:"sign"`
|
||||||
}
|
Transactions map[string]PayData `json:"transactions"` // {"to_address": PayData_req{}, ...}
|
||||||
type PayData_req struct {
|
Status int `json:"status,omitempty"`
|
||||||
OrderId string `json:"order_id"`
|
|
||||||
ToAddress string `json:"to_address"`
|
|
||||||
TxHash string `json:"tx_hash,omitempty"`
|
|
||||||
Amount float64 `json:"amount"`
|
|
||||||
Status int `json:"status,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PayMsg_resp struct {
|
type PayMsg_resp struct {
|
||||||
QueueId string `json:"queue_id"`
|
QueueId string `json:"queue_id"`
|
||||||
FromAddress string `json:"from_address"`
|
Chain string `json:"chain"`
|
||||||
PayStatus int `json:"pay_status"` // 1至少有一笔转账成功,3sign校验失败,4钱包余额不足
|
Symbol string `json:"symbol"`
|
||||||
Transactions map[string]*PayData_resp `json:"transactions"` // {"to_address": PayData_resp{}, ...}
|
FromAddress string `json:"from_address"`
|
||||||
}
|
PayStatus int `json:"pay_status"` // 1至少有一笔转账成功,3sign校验失败,4钱包余额不足
|
||||||
type PayData_resp struct {
|
Transactions map[string]PayData `json:"transactions"` // {"to_address": PayData_resp{}, ...}
|
||||||
OrderId string `json:"order_id"`
|
|
||||||
FromAddress string `json:"from_address"`
|
|
||||||
ToAddress string `json:"to_address"`
|
|
||||||
Chain string `json:"chain"`
|
|
||||||
Symbol string `json:"symbol"`
|
|
||||||
Amount float64 `json:"amount"`
|
|
||||||
TxHash string `json:"tx_hash,omitempty"`
|
|
||||||
BlockHeight uint64 `json:"block_height,omitempty"`
|
|
||||||
Status int `json:"status"` // 0失败,1成功
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 接收到的支付消息
|
type PayData struct {
|
||||||
// type PayMsg_req struct {
|
QueueId string `json:"queue_id,omitempty"`
|
||||||
// QueueId string `json:"queue_id"`
|
Chain string `json:"chain,omitempty"`
|
||||||
// FromAddress string `json:"from_address"` // 我们提供的地址
|
Symbol string `json:"symbol,omitempty"`
|
||||||
// ToAddress string `json:"to_address"` // 卖家地址
|
TxHash string `json:"tx_hash,omitempty"`
|
||||||
// Amount float64 `json:"amount"`
|
ToAddress string `json:"to_address"`
|
||||||
// Chain string `json:"chain"` // 链名称
|
Amount float64 `json:"amount"`
|
||||||
// Symbol string `json:"symbol"` // 币种
|
Fee float64 `json:"fee"`
|
||||||
// OrderId string `json:"order_id"` // 订单号
|
BlockHeight uint64 `json:"block_height,omitempty"`
|
||||||
// Timestamp uint64 `json:"timestamp"`
|
Status int `json:"status,omitempty"` // 0失败,1成功, 2待确认
|
||||||
// Sign string `json:"sign"`
|
}
|
||||||
// Transactions map[string]Transaction `json:"tx"`
|
|
||||||
// }
|
|
||||||
// 返回支付结果消息
|
|
||||||
// type PayMsg_resp struct {
|
|
||||||
// QueueId string `json:"queue_id"`
|
|
||||||
// Status int `json:"status"`
|
|
||||||
// Amount float64 `json:"amount"`
|
|
||||||
// Chain string `json:"chain"` // 链名称
|
|
||||||
// Symbol string `json:"symbol"` // 币种
|
|
||||||
// OrderId string `json:"order_id"` // 订单号
|
|
||||||
// TxHash string `json:"tx_hash"`
|
|
||||||
// FromAddress string `json:"from_address"` // 来源地址
|
|
||||||
// ToAddress string `json:"to_address"` // 目标地址
|
|
||||||
// BlockHeight uint64 `json:"block_height"` // 区块高度
|
|
||||||
// }
|
|
||||||
|
|
||||||
// =============================== type3 ===============================
|
// =============================== type3 ===============================
|
||||||
// 接收到的删除监听地址消息
|
// 接收到的删除监听地址消息
|
||||||
@@ -184,6 +102,7 @@ type RemoveListenMsg_req struct {
|
|||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
Timestamp uint64 `json:"timestamp"`
|
Timestamp uint64 `json:"timestamp"`
|
||||||
Sign string `json:"sign"`
|
Sign string `json:"sign"`
|
||||||
|
Stauts int `json:"status,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回收到的删除监听地址消息
|
// 返回收到的删除监听地址消息
|
||||||
@@ -196,28 +115,43 @@ type RemoveListenMsg_resp struct {
|
|||||||
Status int `json:"status"` // 0失败 1成功
|
Status int `json:"status"` // 0失败 1成功
|
||||||
}
|
}
|
||||||
|
|
||||||
// =====================================================================
|
// =============================== ChainServer -> ListenServer ===============================
|
||||||
// 节点通用消息结构
|
// 节点服务响应
|
||||||
type Tx_msg struct {
|
type ChainServer_resp struct {
|
||||||
TxType int `json:"tx_type"` // 转账类型:0充值,1提现,2支付
|
QueueId string
|
||||||
Tx Tx `json:"tx"`
|
MsgType int
|
||||||
}
|
Chain string
|
||||||
type Tx struct {
|
Symbol string
|
||||||
From string `json:"from"` // 充值/提现/支付的来源地址
|
Status int // 遵循constant模块定义
|
||||||
To string `json:"to"` // 充值/提现/支付的目标地址
|
|
||||||
Height uint64 `json:"height"` // 区块高度
|
|
||||||
TxHash string `json:"tx_hash"` // 交易哈希
|
|
||||||
Symbol string `json:"symbol"` // 币种
|
|
||||||
Value float64 `json:"value"` // 数量,单位是币
|
|
||||||
Status int `json:"status"` // 交易状态,1成功,0失败, 2待确认
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =============================== 其他 ===============================
|
||||||
type Transaction struct {
|
type Transaction struct {
|
||||||
From string `json:"from"` // 充值/提现/支付的来源地址
|
QueueId string `json:"queue_id,omitempty"` // 交易对应的QueueId
|
||||||
To string `json:"to"` // 充值/提现/支付的目标地址
|
// 交易对应的消息类型,0充值 1提现 2支付
|
||||||
Height uint64 `json:"height"` // 区块高度
|
// 充值:tx.to_address = msg.Address
|
||||||
TxHash string `json:"tx_hash"` // 交易哈希
|
// 提现/支付:tx.to_address = msg.ToAddress && tx.from_address = msg.FromAddress && tx.value = msg.Amount
|
||||||
Symbol string `json:"symbol"` // 币种
|
// 同时还要通过msg确认属于哪种类型,即上述3个条件是否都存在于某个msg钟
|
||||||
Value float64 `json:"value"` // 数量,单位是币
|
TxType int `json:"tx_type,omitempty"`
|
||||||
Status int `json:"status"` // 交易状态,1成功,0失败, 2待确认
|
Chain string `json:"chain"`
|
||||||
|
Symbol string `json:"symbol"`
|
||||||
|
From string `json:"from"`
|
||||||
|
To string `json:"to"`
|
||||||
|
TxHash string `json:"tx_hash,omitempty"`
|
||||||
|
Height uint64 `json:"height,omitempty"`
|
||||||
|
Amount *big.Int `json:"amount"`
|
||||||
|
GasUsed *big.Int `json:"gas_used,omitempty"` // 转账实际产生的eth消耗
|
||||||
|
FreezeFee *big.Int `json:"freeze_fee,omitempty"` // erc20转账冻结的usdt,只有提现才会修改
|
||||||
|
Status int `json:"status"` // 交易状态,1成功,0失败, 2待确认
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransferResult struct {
|
||||||
|
QueueId string
|
||||||
|
MsgType int // 1提现,2支付
|
||||||
|
Chain string
|
||||||
|
Symbol string
|
||||||
|
FromAddress string
|
||||||
|
ToAddress string
|
||||||
|
Amount float64
|
||||||
|
Status int
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
# RabbitMQ 服务模块
|
|
||||||
|
|
||||||
本模块提供 RabbitMQ 消息队列的封装,用于接收业务系统的请求和发送交易确认响应。
|
|
||||||
|
|
||||||
## 快速使用
|
|
||||||
|
|
||||||
```go
|
|
||||||
// 创建 RabbitMQ 服务
|
|
||||||
rmqServer, err := rmq.NewRabbitMQServer(config.RMQConfig)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer rmqServer.Close()
|
|
||||||
|
|
||||||
// 设置消息回调
|
|
||||||
rmqServer.OnTopupMsg = func(msg message.TopupMsg_req) {
|
|
||||||
// 处理充值请求
|
|
||||||
}
|
|
||||||
|
|
||||||
// 启动监听
|
|
||||||
rmqServer.Start()
|
|
||||||
|
|
||||||
// 发送响应
|
|
||||||
rmqServer.PublishTopupResp(response)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 消息队列结构
|
|
||||||
|
|
||||||
### 请求队列(消费)
|
|
||||||
- `topup` - 充值请求
|
|
||||||
- `withdraw` - 提现请求
|
|
||||||
- `pay` - 支付请求
|
|
||||||
|
|
||||||
### 响应队列(发布)
|
|
||||||
- `topup_resp` - 充值响应
|
|
||||||
- `withdraw_resp` - 提现响应
|
|
||||||
- `pay_resp` - 支付响应
|
|
||||||
|
|
||||||
## 特性
|
|
||||||
|
|
||||||
✅ **自动重连** - 连接断开时自动重连
|
|
||||||
✅ **消息持久化** - 消息不会丢失
|
|
||||||
✅ **手动确认** - 处理成功后才确认消息
|
|
||||||
✅ **并发安全** - 支持多 goroutine 并发发布
|
|
||||||
|
|
||||||
更多详情请参考 [主 README](../../README.md)。
|
|
||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
|
|
||||||
// RabbitMQServer RabbitMQ 服务
|
// RabbitMQServer RabbitMQ 服务
|
||||||
type RabbitMQServer struct {
|
type RabbitMQServer struct {
|
||||||
config message.RMQConfig
|
config message.RmqConfig
|
||||||
conn *amqp.Connection
|
conn *amqp.Connection
|
||||||
channel *amqp.Channel
|
channel *amqp.Channel
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
@@ -29,7 +29,7 @@ type RabbitMQServer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewRabbitMQServer 创建 RabbitMQ 服务
|
// NewRabbitMQServer 创建 RabbitMQ 服务
|
||||||
func NewRabbitMQServer(config message.RMQConfig) (*RabbitMQServer, error) {
|
func NewRabbitMQServer(config message.RmqConfig) (*RabbitMQServer, error) {
|
||||||
// 创建连接
|
// 创建连接
|
||||||
conn, err := amqp.Dial(config.SubAddr)
|
conn, err := amqp.Dial(config.SubAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -59,63 +59,63 @@ func NewRabbitMQServer(config message.RMQConfig) (*RabbitMQServer, error) {
|
|||||||
server.Close()
|
server.Close()
|
||||||
return nil, fmt.Errorf("failed to setup queues: %w", err)
|
return nil, fmt.Errorf("failed to setup queues: %w", err)
|
||||||
}
|
}
|
||||||
|
log.Println("✅ RabbitMQ队列已启动")
|
||||||
return server, nil
|
return server, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// setupQueuesAndExchanges 设置队列和交换机
|
// setupQueuesAndExchanges 设置队列和交换机
|
||||||
func (r *RabbitMQServer) setupQueuesAndExchanges() error {
|
func (r *RabbitMQServer) setupQueuesAndExchanges() error {
|
||||||
configs := []message.QueueConfig{
|
configs := []message.Queue{
|
||||||
r.config.PayConfig,
|
r.config.Pay,
|
||||||
r.config.TopUpConfig,
|
r.config.Topup,
|
||||||
r.config.WithdrawConfig,
|
r.config.Withdraw,
|
||||||
r.config.RemoveConfig,
|
r.config.Remove,
|
||||||
r.config.PayRespConfig,
|
r.config.PayResp,
|
||||||
r.config.TopUpRespConfig,
|
r.config.TopupResp,
|
||||||
r.config.WithdrawRespConfig,
|
r.config.WithdrawResp,
|
||||||
r.config.RemoveRespConfig,
|
r.config.RemoveResp,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cfg := range configs {
|
for _, cfg := range configs {
|
||||||
// 声明交换机
|
// 声明交换机
|
||||||
err := r.channel.ExchangeDeclare(
|
err := r.channel.ExchangeDeclare(
|
||||||
cfg.ExchangeName, // 交换机名称
|
cfg.Exchange, // 交换机名称
|
||||||
"direct", // 类型:direct(与现有交换机类型一致)
|
"direct", // 类型:direct(与现有交换机类型一致)
|
||||||
true, // durable
|
true, // durable
|
||||||
false, // auto-deleted
|
false, // auto-deleted
|
||||||
false, // internal
|
false, // internal
|
||||||
false, // no-wait
|
false, // no-wait
|
||||||
nil, // arguments
|
nil, // arguments
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to declare exchange %s: %w", cfg.ExchangeName, err)
|
return fmt.Errorf("failed to declare exchange %s: %w", cfg.Exchange, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 声明队列
|
// 声明队列
|
||||||
_, err = r.channel.QueueDeclare(
|
_, err = r.channel.QueueDeclare(
|
||||||
cfg.QueueName, // 队列名称
|
cfg.Exchange, // 队列名称
|
||||||
true, // durable
|
true, // durable
|
||||||
false, // delete when unused
|
false, // delete when unused
|
||||||
false, // exclusive
|
false, // exclusive
|
||||||
false, // no-wait
|
false, // no-wait
|
||||||
nil, // arguments
|
nil, // arguments
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to declare queue %s: %w", cfg.QueueName, err)
|
return fmt.Errorf("failed to declare queue %s: %w", cfg.Exchange, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绑定队列到交换机
|
// 绑定队列到交换机
|
||||||
for _, routingKey := range cfg.Routing {
|
for _, routingKey := range cfg.Routing {
|
||||||
err = r.channel.QueueBind(
|
err = r.channel.QueueBind(
|
||||||
cfg.QueueName, // 队列名称
|
cfg.Queue, // 队列名称
|
||||||
routingKey, // routing key
|
routingKey, // routing key
|
||||||
cfg.ExchangeName, // 交换机名称
|
cfg.Exchange, // 交换机名称
|
||||||
false, // no-wait
|
false, // no-wait
|
||||||
nil, // arguments
|
nil, // arguments
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to bind queue %s to exchange %s with key %s: %w",
|
return fmt.Errorf("failed to bind queue %s to exchange %s with key %s: %w",
|
||||||
cfg.QueueName, cfg.ExchangeName, routingKey, err)
|
cfg.Queue, cfg.Exchange, routingKey, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,8 +144,7 @@ func (r *RabbitMQServer) Start() error {
|
|||||||
// consumeTopup 消费充值消息
|
// consumeTopup 消费充值消息
|
||||||
func (r *RabbitMQServer) consumeTopup() {
|
func (r *RabbitMQServer) consumeTopup() {
|
||||||
r.consumeQueue(
|
r.consumeQueue(
|
||||||
r.config.TopUpConfig.QueueName,
|
r.config.Topup.Queue,
|
||||||
"topup",
|
|
||||||
func(body []byte) error {
|
func(body []byte) error {
|
||||||
var msg message.TopupMsg_req
|
var msg message.TopupMsg_req
|
||||||
if err := json.Unmarshal(body, &msg); err != nil {
|
if err := json.Unmarshal(body, &msg); err != nil {
|
||||||
@@ -165,8 +164,7 @@ func (r *RabbitMQServer) consumeTopup() {
|
|||||||
// consumeWithdraw 消费提现消息
|
// consumeWithdraw 消费提现消息
|
||||||
func (r *RabbitMQServer) consumeWithdraw() {
|
func (r *RabbitMQServer) consumeWithdraw() {
|
||||||
r.consumeQueue(
|
r.consumeQueue(
|
||||||
r.config.WithdrawConfig.QueueName,
|
r.config.Withdraw.Queue,
|
||||||
"withdraw",
|
|
||||||
func(body []byte) error {
|
func(body []byte) error {
|
||||||
var msg message.WithdrawMsg_req
|
var msg message.WithdrawMsg_req
|
||||||
if err := json.Unmarshal(body, &msg); err != nil {
|
if err := json.Unmarshal(body, &msg); err != nil {
|
||||||
@@ -186,15 +184,14 @@ func (r *RabbitMQServer) consumeWithdraw() {
|
|||||||
// consumePay 消费支付消息
|
// consumePay 消费支付消息
|
||||||
func (r *RabbitMQServer) consumePay() {
|
func (r *RabbitMQServer) consumePay() {
|
||||||
r.consumeQueue(
|
r.consumeQueue(
|
||||||
r.config.PayConfig.QueueName,
|
r.config.Pay.Queue,
|
||||||
"pay",
|
|
||||||
func(body []byte) error {
|
func(body []byte) error {
|
||||||
var msg message.PayMsg_req
|
var msg message.PayMsg_req
|
||||||
if err := json.Unmarshal(body, &msg); err != nil {
|
if err := json.Unmarshal(body, &msg); err != nil {
|
||||||
return fmt.Errorf("failed to parse pay message: %w", err)
|
return fmt.Errorf("failed to parse pay message: %w", err)
|
||||||
}
|
}
|
||||||
log.Printf("📥 [RMQ] 收到支付请求: QueueId=%s, From=%s, Chain=%s, Symbol=%s, TxCount=%d",
|
log.Printf("📥 [RMQ] 收到支付请求: QueueId=%s, From=%s, Chain=%s, Symbol=%s, TxCount=%d",
|
||||||
msg.QueueId, msg.FromAddress, msg.Chain, msg.Symbol, len(msg.Trasnactions))
|
msg.QueueId, msg.FromAddress, msg.Chain, msg.Symbol, len(msg.Transactions))
|
||||||
|
|
||||||
if r.OnPayMsg != nil {
|
if r.OnPayMsg != nil {
|
||||||
r.OnPayMsg(msg)
|
r.OnPayMsg(msg)
|
||||||
@@ -207,8 +204,7 @@ func (r *RabbitMQServer) consumePay() {
|
|||||||
// consumeRemove 消费删除充值监听消息
|
// consumeRemove 消费删除充值监听消息
|
||||||
func (r *RabbitMQServer) consumeRemove() {
|
func (r *RabbitMQServer) consumeRemove() {
|
||||||
r.consumeQueue(
|
r.consumeQueue(
|
||||||
r.config.RemoveConfig.QueueName,
|
r.config.Remove.Queue,
|
||||||
"remove",
|
|
||||||
func(body []byte) error {
|
func(body []byte) error {
|
||||||
var msg message.RemoveListenMsg_req
|
var msg message.RemoveListenMsg_req
|
||||||
if err := json.Unmarshal(body, &msg); err != nil {
|
if err := json.Unmarshal(body, &msg); err != nil {
|
||||||
@@ -225,7 +221,7 @@ func (r *RabbitMQServer) consumeRemove() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// consumeQueue 通用队列消费方法
|
// consumeQueue 通用队列消费方法
|
||||||
func (r *RabbitMQServer) consumeQueue(queueName, msgType string, handler func([]byte) error) {
|
func (r *RabbitMQServer) consumeQueue(queueName string, handler func([]byte) error) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-r.ctx.Done():
|
case <-r.ctx.Done():
|
||||||
@@ -271,7 +267,7 @@ func (r *RabbitMQServer) consumeQueue(queueName, msgType string, handler func([]
|
|||||||
// PublishTopupResp 发布充值响应
|
// PublishTopupResp 发布充值响应
|
||||||
func (r *RabbitMQServer) PublishTopupResp(resp message.TopupMsg_resp) error {
|
func (r *RabbitMQServer) PublishTopupResp(resp message.TopupMsg_resp) error {
|
||||||
return r.publishMessage(
|
return r.publishMessage(
|
||||||
r.config.TopUpRespConfig,
|
r.config.TopupResp,
|
||||||
resp,
|
resp,
|
||||||
fmt.Sprintf("充值响应: Address=%s, Status=%d, TxHash=%s",
|
fmt.Sprintf("充值响应: Address=%s, Status=%d, TxHash=%s",
|
||||||
resp.Address, resp.Status, resp.TxHash),
|
resp.Address, resp.Status, resp.TxHash),
|
||||||
@@ -281,7 +277,7 @@ func (r *RabbitMQServer) PublishTopupResp(resp message.TopupMsg_resp) error {
|
|||||||
// PublishWithdrawResp 发布提现响应
|
// PublishWithdrawResp 发布提现响应
|
||||||
func (r *RabbitMQServer) PublishWithdrawResp(resp message.WithdrawMsg_resp) error {
|
func (r *RabbitMQServer) PublishWithdrawResp(resp message.WithdrawMsg_resp) error {
|
||||||
return r.publishMessage(
|
return r.publishMessage(
|
||||||
r.config.WithdrawRespConfig,
|
r.config.WithdrawResp,
|
||||||
resp,
|
resp,
|
||||||
fmt.Sprintf("提现响应: QueueId=%s, Status=%d, TxHash=%s",
|
fmt.Sprintf("提现响应: QueueId=%s, Status=%d, TxHash=%s",
|
||||||
resp.QueueId, resp.Status, resp.TxHash),
|
resp.QueueId, resp.Status, resp.TxHash),
|
||||||
@@ -291,7 +287,7 @@ func (r *RabbitMQServer) PublishWithdrawResp(resp message.WithdrawMsg_resp) erro
|
|||||||
// PublishPayResp 发布支付响应
|
// PublishPayResp 发布支付响应
|
||||||
func (r *RabbitMQServer) PublishPayResp(resp message.PayMsg_resp) error {
|
func (r *RabbitMQServer) PublishPayResp(resp message.PayMsg_resp) error {
|
||||||
return r.publishMessage(
|
return r.publishMessage(
|
||||||
r.config.PayRespConfig,
|
r.config.PayResp,
|
||||||
resp,
|
resp,
|
||||||
"支付响应",
|
"支付响应",
|
||||||
)
|
)
|
||||||
@@ -300,14 +296,14 @@ func (r *RabbitMQServer) PublishPayResp(resp message.PayMsg_resp) error {
|
|||||||
// PublishRemoveResp 发布删除充值监听响应
|
// PublishRemoveResp 发布删除充值监听响应
|
||||||
func (r *RabbitMQServer) PublishRemoveResp(resp message.RemoveListenMsg_resp) error {
|
func (r *RabbitMQServer) PublishRemoveResp(resp message.RemoveListenMsg_resp) error {
|
||||||
return r.publishMessage(
|
return r.publishMessage(
|
||||||
r.config.RemoveRespConfig,
|
r.config.RemoveResp,
|
||||||
resp,
|
resp,
|
||||||
fmt.Sprintf("删除充值监听响应: Address=%s, Status=%d", resp.Address, resp.Status),
|
fmt.Sprintf("删除充值监听响应: Address=%s, Status=%d", resp.Address, resp.Status),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// publishMessage 通用消息发布方法
|
// publishMessage 通用消息发布方法
|
||||||
func (r *RabbitMQServer) publishMessage(config message.QueueConfig, msg interface{}, logMsg string) error {
|
func (r *RabbitMQServer) publishMessage(config message.Queue, msg interface{}, logMsg string) error {
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
defer r.mu.Unlock()
|
defer r.mu.Unlock()
|
||||||
|
|
||||||
@@ -329,10 +325,10 @@ func (r *RabbitMQServer) publishMessage(config message.QueueConfig, msg interfac
|
|||||||
|
|
||||||
err = r.channel.PublishWithContext(
|
err = r.channel.PublishWithContext(
|
||||||
ctx,
|
ctx,
|
||||||
config.ExchangeName, // 交换机
|
config.Exchange, // 交换机
|
||||||
routingKey, // routing key
|
routingKey, // routing key
|
||||||
false, // mandatory
|
false, // mandatory
|
||||||
false, // immediate
|
false, // immediate
|
||||||
amqp.Publishing{
|
amqp.Publishing{
|
||||||
ContentType: "application/json",
|
ContentType: "application/json",
|
||||||
Body: body,
|
Body: body,
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"m2pool-payment/internal/blockchain"
|
"m2pool-payment/internal/blockchain"
|
||||||
eth "m2pool-payment/internal/blockchain/eth"
|
"m2pool-payment/internal/blockchain/eth"
|
||||||
|
"m2pool-payment/internal/constant"
|
||||||
"m2pool-payment/internal/crypto"
|
"m2pool-payment/internal/crypto"
|
||||||
"m2pool-payment/internal/db"
|
"m2pool-payment/internal/listen"
|
||||||
"m2pool-payment/internal/logger"
|
"m2pool-payment/internal/logger"
|
||||||
message "m2pool-payment/internal/msg"
|
message "m2pool-payment/internal/msg"
|
||||||
rmq "m2pool-payment/internal/queue"
|
rmq "m2pool-payment/internal/queue"
|
||||||
@@ -16,267 +17,93 @@ import (
|
|||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// const MSG_KEY string = "9f3c7a12"
|
|
||||||
|
|
||||||
// 状态码常量
|
|
||||||
const (
|
|
||||||
STATUS_FAILED = 0 // 失败
|
|
||||||
STATUS_SUCCESS = 1 // 成功
|
|
||||||
STATUS_PENDING = 2 // 待确认
|
|
||||||
STATUS_VERIFY_FAILED = 3 // 验证失败
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerCtx struct {
|
type ServerCtx struct {
|
||||||
msgKey string
|
msgKey string // 解密msg-sign的密钥,启动命令参数传入
|
||||||
Config message.Config
|
Config message.Config
|
||||||
blockChainServer *blockchain.BlockChainServer
|
blockChainServer *blockchain.BlockChainServer
|
||||||
rmqServer *rmq.RabbitMQServer
|
rmqServer *rmq.RabbitMQServer
|
||||||
sqlitedb db.SQLite
|
messageServer *listen.ListenServer
|
||||||
}
|
}
|
||||||
|
|
||||||
var s_ctx ServerCtx
|
func loadConfig() message.Config {
|
||||||
|
|
||||||
// verifyMessage 验证消息签名
|
|
||||||
func verifyMessage(timestamp uint64, sign string) bool {
|
|
||||||
hash_byte := crypto.Sha256Hash(fmt.Sprintf("%x", timestamp) + s_ctx.msgKey)
|
|
||||||
hash := hex.EncodeToString(hash_byte)
|
|
||||||
return hash == sign
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadConfig(msgKey string) {
|
|
||||||
file, err := os.ReadFile("config.json")
|
file, err := os.ReadFile("config.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("读取配置文件失败: %v", err))
|
panic(fmt.Sprintf("读取配置文件失败: %v", err))
|
||||||
}
|
}
|
||||||
|
var result message.Config
|
||||||
err = json.Unmarshal(file, &s_ctx.Config)
|
err = json.Unmarshal(file, &result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("解析配置文件失败: %v", err))
|
panic(fmt.Sprintf("解析配置文件失败: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("✅ 配置加载成功: RPC=%s, WS=%s",
|
return result
|
||||||
s_ctx.Config.ETHConfig.RpcURL, s_ctx.Config.ETHConfig.WsURL)
|
|
||||||
|
|
||||||
s_ctx.msgKey = msgKey
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initBlockChainServer() {
|
func NewServer(msgKey string) *ServerCtx {
|
||||||
// 初始化节点服务
|
cfg := loadConfig()
|
||||||
|
l := listen.NewListenServer(cfg)
|
||||||
|
eth_node, err := eth.NewETHNode(cfg, msgKey, l)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
node_server := blockchain.NewBlockChainServer()
|
node_server := blockchain.NewBlockChainServer()
|
||||||
// 初始化ETH节点
|
|
||||||
eth_node, err := eth.NewETHNode(s_ctx.Config.ETHConfig, "m2pool")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("ETH-Node Start error: %v", err)
|
|
||||||
}
|
|
||||||
// 注册ETH节点
|
|
||||||
node_server.RegisterChain("ETH", eth_node)
|
node_server.RegisterChain("ETH", eth_node)
|
||||||
// 将所有注册的blockChainServer绑定至server
|
rmq_server, err := rmq.NewRabbitMQServer(cfg.RmqConfig)
|
||||||
s_ctx.blockChainServer = node_server
|
|
||||||
|
|
||||||
log.Println("✅ 区块链服务初始化完成")
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadSQLiteData() {
|
|
||||||
err1 := loadTopupReqMsg()
|
|
||||||
if err1 != nil {
|
|
||||||
log.Fatalf("load topup msg err:%v", err1)
|
|
||||||
}
|
|
||||||
err2 := loadWithdrawReqMsg()
|
|
||||||
if err2 != nil {
|
|
||||||
log.Fatalf("load withdraw msg err:%v", err2)
|
|
||||||
}
|
|
||||||
err3 := loadPayReqMsg()
|
|
||||||
if err3 != nil {
|
|
||||||
log.Fatalf("load pay msg err:%v", err3)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadTopupReqMsg() error {
|
|
||||||
sql := `SELECT chain, symbol, timestamp, to_addr FROM msg_topup_req;`
|
|
||||||
rows, err := s_ctx.sqlitedb.DB.Query(sql)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("query history topup-msg error: %w", err)
|
panic(err)
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
return &ServerCtx{
|
||||||
|
msgKey: msgKey,
|
||||||
var topupReq_msg message.TopupMsg_req
|
Config: cfg,
|
||||||
hasData := false
|
blockChainServer: node_server,
|
||||||
for rows.Next() {
|
rmqServer: rmq_server,
|
||||||
hasData = true
|
messageServer: l,
|
||||||
if err := rows.Scan(&topupReq_msg.Chain, &topupReq_msg.Symbol, &topupReq_msg.Timestamp, &topupReq_msg.Address); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s_ctx.blockChainServer.AddAddress(topupReq_msg.Chain, topupReq_msg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hasData {
|
|
||||||
log.Println("Msg_topup_req`s msg has not data, doesn`t need to load.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在遍历完所有数据后检查是否发生了错误
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
log.Printf("Error encountered while iterating over rows: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadWithdrawReqMsg() error {
|
// verifyMessage 验证消息签名
|
||||||
sql := `SELECT queueId, chain, symbol, timestamp, from_addr, to_addr, amount FROM msg_withdraw_req;`
|
func (s *ServerCtx) verifyMessage(timestamp uint64, sign string) bool {
|
||||||
rows, err := s_ctx.sqlitedb.DB.Query(sql)
|
hash_byte := crypto.Sha256Hash(fmt.Sprintf("%x", timestamp) + s.msgKey)
|
||||||
if err != nil {
|
hash := hex.EncodeToString(hash_byte)
|
||||||
return fmt.Errorf("query history withdraw-msg error: %w", err)
|
return hash == sign
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var withdrawReq_msg message.WithdrawMsg_req
|
|
||||||
hasData := false
|
|
||||||
for rows.Next() {
|
|
||||||
hasData = true
|
|
||||||
// var chain, symbol, to_addr string
|
|
||||||
// var timestamp uint64
|
|
||||||
if err := rows.Scan(&withdrawReq_msg.QueueId, &withdrawReq_msg.Chain, &withdrawReq_msg.Symbol, &withdrawReq_msg.Timestamp, &withdrawReq_msg.FromAddress, &withdrawReq_msg.ToAddress, &withdrawReq_msg.Amount); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s_ctx.blockChainServer.AddAddress(withdrawReq_msg.Chain, withdrawReq_msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !hasData {
|
|
||||||
log.Println("Msg_withdraw_req`s msg has not data, doesn`t need to load.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在遍历完所有数据后检查是否发生了错误
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
log.Printf("Error encountered while iterating over rows: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadPayReqMsg() error {
|
func (s *ServerCtx) handleTopupMsg() {
|
||||||
sql := `SELECT queueId, chain, symbol, timestamp, from_addr, total_amount FROM msg_pay_req;`
|
s.rmqServer.OnTopupMsg = func(msg message.TopupMsg_req) {
|
||||||
rows, err := s_ctx.sqlitedb.DB.Query(sql)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("query history pay-msg error: %w", err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var payReq_msg message.PayMsg_req
|
|
||||||
hasData := false
|
|
||||||
for rows.Next() {
|
|
||||||
hasData = true
|
|
||||||
if err := rows.Scan(&payReq_msg.QueueId, &payReq_msg.Chain, &payReq_msg.Symbol, &payReq_msg.Timestamp, &payReq_msg.FromAddress, &payReq_msg.TotalAmount); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s_ctx.blockChainServer.AddAddress(payReq_msg.Chain, payReq_msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !hasData {
|
|
||||||
log.Println("Msg_pay_req`s msg has not data, doesn`t need to load.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在遍历完所有数据后检查是否发生了错误
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
log.Printf("Error encountered while iterating over rows: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func initRmqServer() {
|
|
||||||
// 初始化rmq服务
|
|
||||||
rmq_server, err := rmq.NewRabbitMQServer(s_ctx.Config.RMQConfig)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("RabbitMQ Server Start error: %v", err)
|
|
||||||
}
|
|
||||||
// 将rmq服务绑定至server
|
|
||||||
s_ctx.rmqServer = rmq_server
|
|
||||||
|
|
||||||
log.Printf("✅ RabbitMQ服务初始化完成: %s", s_ctx.Config.RMQConfig.SubAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func initSQLite(sqlite3_file string) {
|
|
||||||
// 初始化sqlite3数据库
|
|
||||||
sqlite3, err := db.NewSQLite(sqlite3_file)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("connect sqlite3 error:%v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sqlByte, err := os.ReadFile("../public/SQLite3.sql")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("open sql file error: %s", "../public/SQLite3.sql")
|
|
||||||
}
|
|
||||||
sqlite3.Exec_(string(sqlByte))
|
|
||||||
s_ctx.sqlitedb = *sqlite3
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleTopupMsg() {
|
|
||||||
s_ctx.rmqServer.OnTopupMsg = func(msg message.TopupMsg_req) {
|
|
||||||
msg.Address = strings.ToLower(msg.Address)
|
msg.Address = strings.ToLower(msg.Address)
|
||||||
|
|
||||||
// 验证签名
|
// 验证签名
|
||||||
if !verifyMessage(msg.Timestamp, msg.Sign) {
|
if !s.verifyMessage(msg.Timestamp, msg.Sign) {
|
||||||
err := s_ctx.rmqServer.PublishTopupResp(message.TopupMsg_resp{
|
err := s.rmqServer.PublishTopupResp(message.TopupMsg_resp{
|
||||||
|
QueueId: msg.QueueId,
|
||||||
Address: msg.Address,
|
Address: msg.Address,
|
||||||
Status: STATUS_VERIFY_FAILED,
|
Status: constant.STATUS_VERIFY_FAILED,
|
||||||
Chain: msg.Chain,
|
Chain: msg.Chain,
|
||||||
Symbol: msg.Symbol,
|
Symbol: msg.Symbol,
|
||||||
Amount: 0,
|
|
||||||
TxHash: "",
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("❌ 发布充值失败响应失败: %v", err)
|
log.Printf("❌ 发布充值失败响应失败: %v", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
s.messageServer.ChFromRmqServer <- msg
|
||||||
// 添加监听地址
|
|
||||||
// go func() {
|
|
||||||
err := s_ctx.blockChainServer.AddAddress(msg.Chain, msg)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("❌ 添加监听地址失败: %v", err)
|
|
||||||
// 发送失败响应
|
|
||||||
err = s_ctx.rmqServer.PublishTopupResp(message.TopupMsg_resp{
|
|
||||||
Address: msg.Address,
|
|
||||||
Status: STATUS_FAILED,
|
|
||||||
Chain: msg.Chain,
|
|
||||||
Symbol: msg.Symbol,
|
|
||||||
Amount: 0,
|
|
||||||
TxHash: "",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("❌ 发布充值失败响应失败: %v", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// }()
|
|
||||||
// 将新增数据写入sqlite
|
|
||||||
insert_sql := `INSERT OR REPLACE INTO msg_topup_req (chain, symbol, timestamp, to_addr) VALUES (?, ?, ?, ?)`
|
|
||||||
data := []any{msg.Chain, msg.Symbol, msg.Timestamp, msg.Address}
|
|
||||||
err = s_ctx.sqlitedb.Insert(insert_sql, data)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("❌ 插入 msg_req 失败: %v, data: %+v", err, data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleWithdrawMsg() {
|
func (s *ServerCtx) handleWithdrawMsg() {
|
||||||
s_ctx.rmqServer.OnWithdrawMsg = func(msg message.WithdrawMsg_req) {
|
s.rmqServer.OnWithdrawMsg = func(msg message.WithdrawMsg_req) {
|
||||||
msg.FromAddress = strings.ToLower(msg.FromAddress)
|
msg.FromAddress = strings.ToLower(msg.FromAddress)
|
||||||
msg.ToAddress = strings.ToLower(msg.ToAddress)
|
msg.ToAddress = strings.ToLower(msg.ToAddress)
|
||||||
|
|
||||||
// 验证签名
|
// 验证签名
|
||||||
if !verifyMessage(msg.Timestamp, msg.Sign) {
|
if !s.verifyMessage(msg.Timestamp, msg.Sign) {
|
||||||
err := s_ctx.rmqServer.PublishWithdrawResp(message.WithdrawMsg_resp{
|
err := s.rmqServer.PublishWithdrawResp(message.WithdrawMsg_resp{
|
||||||
QueueId: msg.QueueId,
|
QueueId: msg.QueueId,
|
||||||
Status: STATUS_VERIFY_FAILED,
|
Chain: msg.Chain,
|
||||||
Chain: msg.Chain,
|
Symbol: msg.Symbol,
|
||||||
Symbol: msg.Symbol,
|
Status: constant.STATUS_VERIFY_FAILED,
|
||||||
Amount: 0,
|
FromAddress: msg.FromAddress,
|
||||||
TxHash: "",
|
ToAddress: msg.ToAddress,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("❌ 发布提现失败响应失败: %v", err)
|
log.Printf("❌ 发布提现失败响应失败: %v", err)
|
||||||
@@ -284,58 +111,22 @@ func handleWithdrawMsg() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行转账
|
s.messageServer.ChFromRmqServer <- msg
|
||||||
err := s_ctx.blockChainServer.Transfer(msg.Chain, msg)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("❌ 提现转账失败: %v", err)
|
|
||||||
// 发送失败响应
|
|
||||||
s_ctx.rmqServer.PublishWithdrawResp(message.WithdrawMsg_resp{
|
|
||||||
QueueId: msg.QueueId,
|
|
||||||
Status: STATUS_FAILED,
|
|
||||||
Amount: msg.Amount,
|
|
||||||
Chain: msg.Chain,
|
|
||||||
Symbol: msg.Symbol,
|
|
||||||
TxHash: "",
|
|
||||||
})
|
|
||||||
return // 转账失败时直接返回,不进入链上确认流程
|
|
||||||
}
|
|
||||||
// go func() {
|
|
||||||
err = s_ctx.blockChainServer.AddAddress(msg.Chain, msg)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("❌ 添加监听地址失败: %v", err)
|
|
||||||
// 发送失败响应
|
|
||||||
s_ctx.rmqServer.PublishWithdrawResp(message.WithdrawMsg_resp{
|
|
||||||
QueueId: msg.QueueId,
|
|
||||||
Status: STATUS_FAILED,
|
|
||||||
Amount: msg.Amount,
|
|
||||||
Chain: msg.Chain,
|
|
||||||
Symbol: msg.Symbol,
|
|
||||||
TxHash: "",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// }()
|
|
||||||
// 将新增数据写入sqlite
|
|
||||||
insert_sql := `INSERT OR REPLACE INTO msg_withdraw_req (queueId, chain, symbol, timestamp, from_addr, to_addr, amount) VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
||||||
data := []any{msg.QueueId, msg.Chain, msg.Symbol, msg.Timestamp, msg.FromAddress, msg.ToAddress, msg.Amount}
|
|
||||||
err = s_ctx.sqlitedb.Insert(insert_sql, data)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("❌ 插入 withdraw_req 失败: %v, data: %+v", err, data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlePayMsg() {
|
func (s *ServerCtx) handlePayMsg() {
|
||||||
s_ctx.rmqServer.OnPayMsg = func(msg message.PayMsg_req) {
|
s.rmqServer.OnPayMsg = func(msg message.PayMsg_req) {
|
||||||
msg.FromAddress = strings.ToLower(msg.FromAddress)
|
msg.FromAddress = strings.ToLower(msg.FromAddress)
|
||||||
// msg.ToAddress = strings.ToLower(msg.ToAddress)
|
|
||||||
|
|
||||||
// 验证签名
|
// 验证签名
|
||||||
if !verifyMessage(msg.Timestamp, msg.Sign) {
|
if !s.verifyMessage(msg.Timestamp, msg.Sign) {
|
||||||
err := s_ctx.rmqServer.PublishPayResp(message.PayMsg_resp{
|
err := s.rmqServer.PublishPayResp(message.PayMsg_resp{
|
||||||
QueueId: msg.QueueId,
|
QueueId: msg.QueueId,
|
||||||
FromAddress: msg.FromAddress,
|
FromAddress: msg.FromAddress,
|
||||||
PayStatus: STATUS_VERIFY_FAILED,
|
PayStatus: constant.STATUS_VERIFY_FAILED,
|
||||||
|
Transactions: msg.Transactions,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("❌ 发布支付失败响应失败: %v", err)
|
log.Printf("❌ 发布支付失败响应失败: %v", err)
|
||||||
@@ -343,168 +134,68 @@ func handlePayMsg() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行转账
|
s.messageServer.ChFromRmqServer <- msg
|
||||||
err := s_ctx.blockChainServer.Transfer(msg.Chain, msg)
|
}
|
||||||
if err != nil {
|
}
|
||||||
log.Printf("❌ 支付转账失败: %v", err)
|
|
||||||
// 发送失败响应
|
func (s *ServerCtx) handleRemoveMsg() {
|
||||||
s_ctx.rmqServer.PublishPayResp(message.PayMsg_resp{
|
s.rmqServer.OnRemoveMsg = func(msg message.RemoveListenMsg_req) {
|
||||||
QueueId: msg.QueueId,
|
msg.Address = strings.ToLower(msg.Address)
|
||||||
FromAddress: msg.FromAddress,
|
// 验证签名
|
||||||
PayStatus: STATUS_FAILED,
|
if !s.verifyMessage(msg.Timestamp, msg.Sign) {
|
||||||
})
|
err := s.rmqServer.PublishRemoveResp(message.RemoveListenMsg_resp{
|
||||||
return // 转账失败时直接返回,不进入链上确认流程
|
QueueId: msg.QueueId,
|
||||||
}
|
MsgType: msg.MsgType,
|
||||||
// go func() {
|
Chain: msg.Chain,
|
||||||
err = s_ctx.blockChainServer.AddAddress(msg.Chain, msg)
|
Symbol: msg.Symbol,
|
||||||
if err != nil {
|
Address: msg.Address,
|
||||||
log.Printf("❌ 添加监听地址失败: %v", err)
|
Status: constant.STATUS_VERIFY_FAILED,
|
||||||
// 发送失败响应
|
|
||||||
s_ctx.rmqServer.PublishPayResp(message.PayMsg_resp{
|
|
||||||
QueueId: msg.QueueId,
|
|
||||||
FromAddress: msg.FromAddress,
|
|
||||||
PayStatus: STATUS_FAILED,
|
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("❌ 发布移除监听失败响应失败: %v", err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// }()
|
|
||||||
// 将新增数据写入sqlite
|
s.messageServer.ChFromRmqServer <- msg
|
||||||
insert_sql := `INSERT OR REPLACE INTO msg_pay_req (queueId, chain, symbol, timestamp, from_addr, total_amount) VALUES (?, ?, ?, ?, ?, ?)`
|
|
||||||
data := []any{msg.QueueId, msg.Chain, msg.Symbol, msg.Timestamp, msg.FromAddress, msg.TotalAmount}
|
|
||||||
err = s_ctx.sqlitedb.Insert(insert_sql, data)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("❌ 插入 pay_req 失败: %v, data: %+v", err, data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initRmqListen() {
|
func (s *ServerCtx) handleRespMsg() {
|
||||||
// ================== 设置 RabbitMQ 消息处理回调 ==================
|
for msg := range s.messageServer.ChToRmqServer {
|
||||||
// 先设置所有回调(同步执行,避免竞态)
|
switch v := msg.(type) {
|
||||||
handleTopupMsg()
|
case message.TopupMsg_resp:
|
||||||
handleWithdrawMsg()
|
log.Printf("📨[充值响应]:QueueID=%s, Address=%s, Chain=%s, Symbol=%s, TxHash=%s, Status=%d, Amount=%f", v.QueueId, v.Address, v.Chain, v.Symbol, v.TxHash, v.Status, v.Amount)
|
||||||
handlePayMsg()
|
err := s.rmqServer.PublishTopupResp(v)
|
||||||
|
if err != nil {
|
||||||
// 回调设置完成后,再启动 RabbitMQ 监听
|
log.Printf("❌ 发送充值响应失败: %v", err)
|
||||||
if err := s_ctx.rmqServer.Start(); err != nil {
|
return
|
||||||
log.Fatalf("启动 RabbitMQ 监听失败: %v", err)
|
|
||||||
}
|
|
||||||
log.Println("✅ RabbitMQ 监听启动完成")
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleChainEvent(chainEventCh chan any) {
|
|
||||||
for event := range chainEventCh {
|
|
||||||
// 添加 panic 恢复,防止单个消息处理错误导致整个 goroutine 退出
|
|
||||||
func() {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
log.Printf("❌ 处理链上事件 panic: %v, event: %+v", r, event)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 根据消息类型发送不同的响应
|
|
||||||
switch msg := event.(type) {
|
|
||||||
case message.TopupMsg_resp:
|
|
||||||
// 充值确认
|
|
||||||
if msg.Status == STATUS_PENDING {
|
|
||||||
log.Printf("📨 [链上] 充值待确认: Address=%s, Amount=%.2f, TxHash=%s",
|
|
||||||
msg.Address, msg.Amount, msg.TxHash)
|
|
||||||
// 记录交易日志:待确认
|
|
||||||
logger.LogTopup(msg.Address, "待确认", msg.Amount, msg.TxHash, msg.BlockHeight)
|
|
||||||
} else {
|
|
||||||
log.Printf("✅ [链上] 充值确认: Address=%s, Amount=%.2f, TxHash=%s, Status=%d",
|
|
||||||
msg.Address, msg.Amount, msg.TxHash, msg.Status)
|
|
||||||
// 记录交易日志:已确认
|
|
||||||
logger.LogTopup(msg.Address, "确认", msg.Amount, msg.TxHash, msg.BlockHeight)
|
|
||||||
}
|
|
||||||
err := s_ctx.rmqServer.PublishTopupResp(msg)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("❌ 发送充值响应失败: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
// 插入响应数据
|
|
||||||
sql := `INSERT INTO msg_topup_resp (chain, symbol, timestamp, to_addr, amount, height, txHash, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
||||||
data := []any{msg.Chain, msg.Symbol, time.Now().Unix(), msg.Address, msg.Amount, msg.BlockHeight, msg.TxHash, msg.Status}
|
|
||||||
err := s_ctx.sqlitedb.Insert(sql, data)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("❌ 插入 topup_resp 失败: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
case message.WithdrawMsg_resp:
|
|
||||||
// 提现确认
|
|
||||||
log.Printf("✅ [链上] 提现确认: QueueId=%s, Amount=%.2f, TxHash=%s, Status=%d",
|
|
||||||
msg.QueueId, msg.Amount, msg.TxHash, msg.Status)
|
|
||||||
// 记录交易日志
|
|
||||||
logger.LogWithdraw(msg.ToAddress, "确认", msg.Amount, msg.FromAddress, msg.TxHash, msg.BlockHeight)
|
|
||||||
err := s_ctx.rmqServer.PublishWithdrawResp(msg)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("❌ 发送提现响应失败: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
// 插入响应数据
|
|
||||||
sql := `INSERT INTO msg_withdraw_resp (queueId, chain, symbol, timestamp, from_addr, to_addr, amount, height, txHash, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
||||||
data := []any{msg.QueueId, msg.Chain, msg.Symbol, time.Now().Unix(), msg.FromAddress, msg.ToAddress, msg.Amount, msg.BlockHeight, msg.TxHash, msg.Status}
|
|
||||||
err := s_ctx.sqlitedb.Insert(sql, data)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("❌ 插入 withdraw_resp 失败: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除对应数据
|
|
||||||
del_sql := `DELETE FROM msg_withdraw_req WHERE queueId = ?;`
|
|
||||||
count, err := s_ctx.sqlitedb.Delete(del_sql, msg.QueueId)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("❌ 清理 withdraw_req 失败: %v, queueId=%s", err, msg.QueueId)
|
|
||||||
} else if count == 0 {
|
|
||||||
log.Printf("⚠️ 未找到要删除的 withdraw_req 记录: queueId=%s", msg.QueueId)
|
|
||||||
} else {
|
|
||||||
log.Printf("✅ 清理 withdraw_req 成功: 删除了 %d 条记录, queueId=%s", count, msg.QueueId)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
case message.PayMsg_resp:
|
|
||||||
// 支付确认
|
|
||||||
log.Printf("✅ [链上] 支付确认: QueueId=%s, FromAddress=%s, Status=%d",
|
|
||||||
msg.QueueId, msg.FromAddress, msg.PayStatus)
|
|
||||||
// 记录交易日志
|
|
||||||
logger.LogPay("全部交易确认", msg.FromAddress, msg.QueueId, msg.Transactions)
|
|
||||||
err := s_ctx.rmqServer.PublishPayResp(msg)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("❌ 发送支付响应失败: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// go func() {
|
|
||||||
// // 插入响应数据
|
|
||||||
// sql := `INSERT INTO msg_pay_resp (queueId, chain, symbol, timestamp, from_addr, to_addr, amount, height, txHash, status, orderId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
||||||
// data := []any{msg.QueueId, msg.Chain, msg.Symbol, time.Now().Unix(), msg.FromAddress, msg.ToAddress, msg.Amount, msg.BlockHeight, msg.TxHash, msg.Status, msg.OrderId}
|
|
||||||
// err := s_ctx.sqlitedb.Insert(sql, data)
|
|
||||||
// if err != nil {
|
|
||||||
// log.Printf("❌ 插入 pay_resp 失败: %v", err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 删除对应数据
|
|
||||||
// del_sql := `DELETE FROM msg_pay_req WHERE queueId = ?;`
|
|
||||||
// count, err := s_ctx.sqlitedb.Delete(del_sql, msg.QueueId)
|
|
||||||
// if err != nil {
|
|
||||||
// log.Printf("❌ 清理 pay_req 失败: %v, queueId=%s", err, msg.QueueId)
|
|
||||||
// } else if count == 0 {
|
|
||||||
// log.Printf("⚠️ 未找到要删除的 pay_req 记录: queueId=%s", msg.QueueId)
|
|
||||||
// } else {
|
|
||||||
// log.Printf("✅ 清理 pay_req 成功: 删除了 %d 条记录, queueId=%s", count, msg.QueueId)
|
|
||||||
// }
|
|
||||||
// }()
|
|
||||||
|
|
||||||
default:
|
|
||||||
log.Printf("⚠️ 未知消息类型: %T", event)
|
|
||||||
}
|
}
|
||||||
}()
|
case message.WithdrawMsg_resp:
|
||||||
|
log.Printf("📨[提现响应]:QueueID=%s, Chain=%s, Symbol=%s, FromAddress=%s, ToAddress=%s, TxHash=%s, Status=%d, Amount=%f", v.QueueId, v.Chain, v.Symbol, v.FromAddress, v.ToAddress, v.TxHash, v.Status, v.Amount)
|
||||||
|
err := s.rmqServer.PublishWithdrawResp(v)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("❌ 发送提现响应失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case message.PayMsg_resp:
|
||||||
|
log.Printf("📨[提现响应]:QueueID=%s, Chain=%s, Symbol=%s, FromAddress=%s, Status=%d", v.QueueId, v.Chain, v.Symbol, v.FromAddress, v.PayStatus)
|
||||||
|
err := s.rmqServer.PublishPayResp(v)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("❌ 发送支付响应失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case message.RemoveListenMsg_resp:
|
||||||
|
log.Printf("📨[充值响应]:QueueID=%s, Address=%s, Chain=%s, Symbol=%s,Status=%d", v.QueueId, v.Address, v.Chain, v.Symbol, v.Status)
|
||||||
|
err := s.rmqServer.PublishRemoveResp(v)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("❌ 发送移除监听响应失败: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Printf("❌ 错误响应结构: %v", v)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,35 +204,37 @@ func Start(msgKey string) {
|
|||||||
log.Println("🚀 M2Pool Payment System Starting...")
|
log.Println("🚀 M2Pool Payment System Starting...")
|
||||||
log.Println("========================================")
|
log.Println("========================================")
|
||||||
|
|
||||||
// 加载配置
|
server := NewServer(msgKey)
|
||||||
loadConfig(msgKey)
|
|
||||||
|
|
||||||
// 初始化交易日志系统
|
// 启动消息处理
|
||||||
if err := logger.InitTransactionLogger("logs"); err != nil {
|
go server.handleTopupMsg()
|
||||||
log.Fatalf("❌ 初始化交易日志系统失败: %v", err)
|
go server.handleWithdrawMsg()
|
||||||
|
go server.handlePayMsg()
|
||||||
|
go server.handleRemoveMsg()
|
||||||
|
go server.handleRespMsg()
|
||||||
|
|
||||||
|
// 启动 RabbitMQ 服务
|
||||||
|
if err := server.rmqServer.Start(); err != nil {
|
||||||
|
log.Fatalf("❌ 启动 RabbitMQ 服务失败: %v", err)
|
||||||
}
|
}
|
||||||
log.Println("✅ 交易日志系统初始化完成")
|
log.Println("✅ RabbitMQ 服务启动成功")
|
||||||
|
|
||||||
// ================== 初始化区块链节点 ==================
|
// 启动消息服务器监听
|
||||||
initBlockChainServer()
|
go server.messageServer.RmqMsgIn()
|
||||||
|
go server.messageServer.NetMsgIn()
|
||||||
|
log.Println("✅ 消息服务器监听启动成功")
|
||||||
|
|
||||||
// ================== 初始化SQLite3 ==================
|
// 启动区块链监听
|
||||||
initSQLite(s_ctx.Config.SQLite3.MsgPath)
|
if err := server.blockChainServer.Listen("ETH", nil); err != nil {
|
||||||
// 读取历史信息
|
log.Fatalf("❌ 启动区块链监听失败: %v", err)
|
||||||
loadSQLiteData()
|
}
|
||||||
|
log.Println("✅ 区块链监听启动成功")
|
||||||
|
|
||||||
// ================== 初始化 RabbitMQ 服务 ==================
|
// 启动区块链消息监听
|
||||||
initRmqServer()
|
if err := server.blockChainServer.ListenMsg("ETH"); err != nil {
|
||||||
|
log.Fatalf("❌ 启动区块链消息监听失败: %v", err)
|
||||||
// ================== 启动链上事件监听通道 ==================
|
}
|
||||||
chainEventCh := make(chan any, 1000) // 增加缓冲区,避免高并发丢消息
|
log.Println("✅ 区块链消息监听启动成功")
|
||||||
go s_ctx.blockChainServer.Listen("ETH", chainEventCh)
|
|
||||||
|
|
||||||
// ================== 启动 RabbitMQ 监听 ==================
|
|
||||||
initRmqListen()
|
|
||||||
|
|
||||||
// ================== 处理链上确认事件 ==================
|
|
||||||
go handleChainEvent(chainEventCh)
|
|
||||||
|
|
||||||
log.Println("========================================")
|
log.Println("========================================")
|
||||||
log.Println("🎉 所有服务启动完成!")
|
log.Println("🎉 所有服务启动完成!")
|
||||||
@@ -556,8 +249,8 @@ func Start(msgKey string) {
|
|||||||
log.Println("🛑 收到退出信号,正在关闭服务...")
|
log.Println("🛑 收到退出信号,正在关闭服务...")
|
||||||
log.Println("========================================")
|
log.Println("========================================")
|
||||||
|
|
||||||
s_ctx.blockChainServer.Stop("ETH")
|
server.blockChainServer.Stop("ETH")
|
||||||
s_ctx.rmqServer.Close()
|
server.rmqServer.Close()
|
||||||
logger.CloseTransactionLogger()
|
logger.CloseTransactionLogger()
|
||||||
|
|
||||||
log.Println("👋 服务已全部关闭")
|
log.Println("👋 服务已全部关闭")
|
||||||
|
|||||||
@@ -56,3 +56,46 @@ func Slice_delete(arr []any, index int) []any {
|
|||||||
}
|
}
|
||||||
return arr
|
return arr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 函数:检查一个切片是否包含某个字符串
|
||||||
|
func Contains(slice []string, str string) bool {
|
||||||
|
for _, s := range slice {
|
||||||
|
if s == str {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func Float64ToBigInt(symbol string, amount float64) *big.Int {
|
||||||
|
var scale float64
|
||||||
|
switch symbol {
|
||||||
|
case "ETH":
|
||||||
|
scale = math.Pow10(ETHDecimals)
|
||||||
|
case "USDT":
|
||||||
|
scale = math.Pow10(USDTDecimals)
|
||||||
|
default:
|
||||||
|
log.Printf("don`t support symbol: %s", symbol)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
bigAmount := new(big.Int)
|
||||||
|
bigAmount.SetInt64(int64(amount * scale))
|
||||||
|
return bigAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
func BigIntToFloat64(symbol string, amount *big.Int) float64 {
|
||||||
|
var scale *big.Float
|
||||||
|
f := new(big.Float).SetInt(amount)
|
||||||
|
switch symbol {
|
||||||
|
case "ETH":
|
||||||
|
scale = new(big.Float).SetFloat64(1e18)
|
||||||
|
case "USDT":
|
||||||
|
scale = new(big.Float).SetFloat64(1e6)
|
||||||
|
default:
|
||||||
|
log.Printf("don`t support symbol: %s", symbol)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
f.Quo(f, scale)
|
||||||
|
result, _ := f.Float64()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,111 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS msg_topup_req (
|
|
||||||
chain TEXT,
|
|
||||||
symbol TEXT,
|
|
||||||
timestamp INTEGER,
|
|
||||||
to_addr TEXT,
|
|
||||||
PRIMARY KEY(to_addr)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS msg_withdraw_req (
|
|
||||||
queueId TEXT,
|
|
||||||
chain TEXT,
|
|
||||||
symbol TEXT,
|
|
||||||
timestamp INTEGER,
|
|
||||||
from_addr TEXT,
|
|
||||||
to_addr TEXT,
|
|
||||||
amount NUMERIC,
|
|
||||||
PRIMARY KEY(queueId)
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CREATE TABLE IF NOT EXISTS msg_pay_req (
|
|
||||||
-- queueId TEXT,
|
|
||||||
-- chain TEXT,
|
|
||||||
-- symbol TEXT,
|
|
||||||
-- timestamp INTEGER,
|
|
||||||
-- from_addr TEXT,
|
|
||||||
-- to_addr TEXT,
|
|
||||||
-- amount NUMERIC,
|
|
||||||
-- orderId TEXT,
|
|
||||||
-- PRIMARY KEY(queueId)
|
|
||||||
-- );
|
|
||||||
|
|
||||||
CREATE TABLE msg_pay_req (
|
|
||||||
queueId TEXT PRIMARY KEY,
|
|
||||||
from_addr TEXT NOT NULL,
|
|
||||||
chain TEXT NOT NULL,
|
|
||||||
symbol TEXT NOT NULL,
|
|
||||||
total_amount REAL NOT NULL,
|
|
||||||
timestamp INTEGER NOT NULL,
|
|
||||||
sign TEXT NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE msg_pay_req_transactions (
|
|
||||||
queueId TEXT NOT NULL,
|
|
||||||
to_addr TEXT NOT NULL,
|
|
||||||
order_id TEXT NOT NULL,
|
|
||||||
tx_hash TEXT,
|
|
||||||
amount REAL NOT NULL,
|
|
||||||
FOREIGN KEY (queueId) REFERENCES msg_pay_req(queueId)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS msg_topup_resp (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
chain TEXT,
|
|
||||||
symbol TEXT,
|
|
||||||
timestamp INTEGER,
|
|
||||||
to_addr TEXT,
|
|
||||||
amount NUMERIC,
|
|
||||||
height INTEGER,
|
|
||||||
TxHash TEXT,
|
|
||||||
status INTEGER
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS msg_withdraw_resp (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
queueId TEXT,
|
|
||||||
chain TEXT,
|
|
||||||
symbol TEXT,
|
|
||||||
timestamp INTEGER,
|
|
||||||
from_addr TEXT,
|
|
||||||
to_addr TEXT,
|
|
||||||
amount NUMERIC,
|
|
||||||
height INTEGER,
|
|
||||||
txHash TEXT,
|
|
||||||
status INTEGER
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CREATE TABLE IF NOT EXISTS msg_pay_resp (
|
|
||||||
-- id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
-- queueId TEXT,
|
|
||||||
-- chain TEXT,
|
|
||||||
-- symbol TEXT,
|
|
||||||
-- timestamp INTEGER,
|
|
||||||
-- from_addr TEXT,
|
|
||||||
-- to_addr TEXT,
|
|
||||||
-- amount NUMERIC,
|
|
||||||
-- height INTEGER,
|
|
||||||
-- txHash TEXT,
|
|
||||||
-- orderId TEXT,
|
|
||||||
-- status INTEGER
|
|
||||||
-- );
|
|
||||||
|
|
||||||
CREATE TABLE msg_pay_resp (
|
|
||||||
queueId TEXT PRIMARY KEY,
|
|
||||||
from_addr TEXT NOT NULL,
|
|
||||||
pay_status INTEGER NOT NULL, -- 1: At least one success, 3: Sign verification failed, 4: Insufficient balance
|
|
||||||
transactions JSON -- 存储交易的JSON格式
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE msg_pay_resp_transactions (
|
|
||||||
queueId TEXT NOT NULL,
|
|
||||||
order_id TEXT NOT NULL,
|
|
||||||
from_addr TEXT NOT NULL,
|
|
||||||
to_addr TEXT NOT NULL,
|
|
||||||
chain TEXT NOT NULL,
|
|
||||||
symbol TEXT NOT NULL,
|
|
||||||
amount REAL NOT NULL,
|
|
||||||
tx_hash TEXT,
|
|
||||||
height INTEGER,
|
|
||||||
status INTEGER NOT NULL, -- 0: Failed, 1: Success
|
|
||||||
FOREIGN KEY (queueId) REFERENCES msg_pay_resp(queueId)
|
|
||||||
);
|
|
||||||
45
public/eth.sql
Normal file
45
public/eth.sql
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS ETH_wallets (
|
||||||
|
address TEXT PRIMARY KEY NOT NULL,
|
||||||
|
queue_id TEXT NOT NULL,
|
||||||
|
timestamp INTEGER NOT NULL,
|
||||||
|
sign TEXT NOT NULL,
|
||||||
|
status INTEGER DEFAULT 0, -- 0未在监听 1正在监听
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS ETH_balances (
|
||||||
|
address TEXT NOT NULL,
|
||||||
|
symbol TEXT DEFAULT "ETH",
|
||||||
|
used_gas TEXT DEFAULT "0",
|
||||||
|
balance TEXT DEFAULT "0",
|
||||||
|
success_tx_hash TEXT DEFAULT NULL, --使用,隔开
|
||||||
|
failed_tx_hash TEXT DEFAULT NULL, --使用,隔开
|
||||||
|
PRIMARY KEY (address), -- 钱包地址唯一
|
||||||
|
FOREIGN KEY (address) REFERENCES eth_wallets (address) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS USDT_balances (
|
||||||
|
address TEXT NOT NULL,
|
||||||
|
symbol TEXT DEFAULT "USDT",
|
||||||
|
freeze_num TEXT DEFAULT "0",
|
||||||
|
balance TEXT DEFAULT "0",
|
||||||
|
success_tx_hash TEXT DEFAULT NULL, --使用,隔开
|
||||||
|
failed_tx_hash TEXT DEFAULT NULL, --使用,隔开
|
||||||
|
PRIMARY KEY (address), -- 钱包地址唯一
|
||||||
|
FOREIGN KEY (address) REFERENCES eth_wallets (address) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS eth_unconfirmed_tx (
|
||||||
|
queue_id TEXT NOT NULL, -- 关联的msg queue_id
|
||||||
|
tx_type INTEGER NOT NULL, --0充值,1提现,2支付
|
||||||
|
chain TEXT DEFALUT "ETH",
|
||||||
|
symbol TEXT,
|
||||||
|
from_addr TEXT,
|
||||||
|
to_addr TEXT,
|
||||||
|
tx_hash TEXT,
|
||||||
|
height INTEGER,
|
||||||
|
amount TEXT,
|
||||||
|
status INTEGER DEFAULT 2, --0充值失败,1充值成功,2充值待确认
|
||||||
|
PRIMARY KEY (tx_hash),
|
||||||
|
FOREIGN KEY (queue_id) REFERENCES eth_wallets (queue_id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
43
public/eth_mysql.sql
Normal file
43
public/eth_mysql.sql
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS ETH_wallets (
|
||||||
|
address VARCHAR(255) PRIMARY KEY NOT NULL,
|
||||||
|
queue_id VARCHAR(255) NOT NULL,
|
||||||
|
timestamp BIGINT NOT NULL,
|
||||||
|
sign VARCHAR(255) NOT NULL,
|
||||||
|
status TINYINT DEFAULT 0 -- 0未在监听 1正在监听
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS ETH_balances (
|
||||||
|
address VARCHAR(255) NOT NULL,
|
||||||
|
symbol VARCHAR(32) DEFAULT "ETH",
|
||||||
|
used_gas DECIMAL(40,16) DEFAULT 0,
|
||||||
|
balance DECIMAL(40,16) DEFAULT 0,
|
||||||
|
success_tx_hash TEXT DEFAULT NULL, -- 使用,隔开
|
||||||
|
failed_tx_hash TEXT DEFAULT NULL, -- 使用,隔开
|
||||||
|
PRIMARY KEY (address), -- 钱包地址唯一
|
||||||
|
FOREIGN KEY (address) REFERENCES ETH_wallets (address) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS USDT_balances (
|
||||||
|
address VARCHAR(255) NOT NULL,
|
||||||
|
symbol VARCHAR(32) DEFAULT "USDT",
|
||||||
|
freeze_num DECIMAL(40,16) DEFAULT 0,
|
||||||
|
balance DECIMAL(40,16) DEFAULT 0,
|
||||||
|
success_tx_hash TEXT DEFAULT NULL, -- 使用,隔开
|
||||||
|
failed_tx_hash TEXT DEFAULT NULL, -- 使用,隔开
|
||||||
|
PRIMARY KEY (address), -- 钱包地址唯一
|
||||||
|
FOREIGN KEY (address) REFERENCES ETH_wallets (address) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS ETH_unconfirmed_tx (
|
||||||
|
queue_id VARCHAR(255) NOT NULL, -- 关联的msg queue_id
|
||||||
|
tx_type TINYINT NOT NULL, -- 0充值,1提现,2支付
|
||||||
|
chain VARCHAR(32) DEFAULT "ETH",
|
||||||
|
symbol VARCHAR(32),
|
||||||
|
from_addr VARCHAR(255),
|
||||||
|
to_addr VARCHAR(255),
|
||||||
|
tx_hash VARCHAR(255),
|
||||||
|
height BIGINT,
|
||||||
|
amount DECIMAL(40,16),
|
||||||
|
status TINYINT DEFAULT 2, -- 0充值失败,1充值成功,2充值待确认
|
||||||
|
FOREIGN KEY (queue_id) REFERENCES ETH_wallets (queue_id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
3
public/infura
Normal file
3
public/infura
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
API-KEY=431dbd08d89c406fb9f2b5ee812f5faf
|
||||||
|
HTTPS=https://mainnet.infura.io/v3/431dbd08d89c406fb9f2b5ee812f5faf
|
||||||
|
mnemonic=income miracle spin apple live cigar trim social age push eye chase
|
||||||
108
public/msg.sql
Normal file
108
public/msg.sql
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS topup_req_msg (
|
||||||
|
queue_id TEXT NOT NULL,
|
||||||
|
chain TEXT NOT NULL,
|
||||||
|
symbol TEXT NOT NULL,
|
||||||
|
address TEXT NOT NULL,
|
||||||
|
timestamp INTEGER,
|
||||||
|
sign TEXT,
|
||||||
|
status INTEGER DEFAULT 1,
|
||||||
|
PRIMARY KEY (queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS topup_resp_msg (
|
||||||
|
queue_id TEXT NOT NULL,
|
||||||
|
chain TEXT NOT NULL,
|
||||||
|
symbol TEXT NOT NULL,
|
||||||
|
from_addr TEXT NOT NULL,
|
||||||
|
to_addr TEXT NOT NULL,
|
||||||
|
amount TEXT NOT NULL, -- 改为 TEXT 类型
|
||||||
|
tx_hash TEXT DEFAULT NULL,
|
||||||
|
height INTEGER DEFAULT NULL,
|
||||||
|
status INTEGER DEFAULT 5,
|
||||||
|
FOREIGN KEY (queue_id) REFERENCES topup_req_msg(queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS withdraw_req_msg (
|
||||||
|
queue_id TEXT NOT NULL,
|
||||||
|
chain TEXT NOT NULL,
|
||||||
|
symbol TEXT NOT NULL,
|
||||||
|
from_addr TEXT NOT NULL,
|
||||||
|
to_addr TEXT NOT NULL,
|
||||||
|
amount TEXT NOT NULL, -- 改为 TEXT 类型
|
||||||
|
fee TEXT NOT NULL, -- 改为 TEXT 类型
|
||||||
|
timestamp INTEGER,
|
||||||
|
sign TEXT,
|
||||||
|
status INTEGER DEFAULT 5,
|
||||||
|
PRIMARY KEY (queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS withdraw_resp_msg (
|
||||||
|
queue_id TEXT NOT NULL,
|
||||||
|
chain TEXT NOT NULL,
|
||||||
|
symbol TEXT NOT NULL,
|
||||||
|
from_addr TEXT NOT NULL,
|
||||||
|
to_addr TEXT NOT NULL,
|
||||||
|
tx_hash TEXT DEFAULT NULL,
|
||||||
|
amount TEXT DEFAULT NULL, -- 改为 TEXT 类型
|
||||||
|
fee TEXT DEFAULT NULL, -- 改为 TEXT 类型
|
||||||
|
height INTEGER DEFAULT NULL,
|
||||||
|
status INTEGER DEFAULT 5,
|
||||||
|
FOREIGN KEY (queue_id) REFERENCES withdraw_req_msg(queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS pay_req_msg (
|
||||||
|
queue_id TEXT NOT NULL,
|
||||||
|
chain TEXT NOT NULL,
|
||||||
|
symbol TEXT NOT NULL,
|
||||||
|
from_addr TEXT NOT NULL,
|
||||||
|
to_addr TEXT NOT NULL,
|
||||||
|
total_amount TEXT NOT NULL, -- 改为 TEXT 类型
|
||||||
|
total_fee TEXT NOT NULL, -- 改为 TEXT 类型
|
||||||
|
timestamp INTEGER,
|
||||||
|
sign TEXT,
|
||||||
|
status INTEGER DEFAULT 5,
|
||||||
|
PRIMARY KEY (queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS pay_resp_msg (
|
||||||
|
queue_id TEXT NOT NULL,
|
||||||
|
chain TEXT NOT NULL,
|
||||||
|
symbol TEXT NOT NULL,
|
||||||
|
from_addr TEXT NOT NULL,
|
||||||
|
pay_status INTEGER DEFAULT 5,
|
||||||
|
FOREIGN KEY (queue_id) REFERENCES pay_req_msg(queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- pay_msg的交易
|
||||||
|
CREATE TABLE IF NOT EXISTS pay_msg_tx (
|
||||||
|
queue_id TEXT NOT NULL,
|
||||||
|
tx_hash TEXT DEFAULT NULL,
|
||||||
|
to_addr TEXT NOT NULL,
|
||||||
|
amount TEXT DEFAULT NULL, -- 改为 TEXT 类型
|
||||||
|
fee TEXT, -- 改为 TEXT 类型
|
||||||
|
height INTEGER DEFAULT 0,
|
||||||
|
status INTEGER DEFAULT 5,
|
||||||
|
FOREIGN KEY (queue_id) REFERENCES pay_req_msg(queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS remove_req_msg(
|
||||||
|
queue_id TEXT NOT NULL,
|
||||||
|
msg_type INTEGER NOT NULL,
|
||||||
|
chain TEXT NOT NULL,
|
||||||
|
symbol TEXT NOT NULL,
|
||||||
|
address TEXT NOT NULL,
|
||||||
|
timestamp INTEGER,
|
||||||
|
sign TEXT,
|
||||||
|
status INTEGER DEFAULT 2,
|
||||||
|
PRIMARY KEY (queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS remove_resp_msg(
|
||||||
|
queue_id TEXT NOT NULL,
|
||||||
|
msg_type INTEGER NOT NULL,
|
||||||
|
chain TEXT NOT NULL,
|
||||||
|
symbol TEXT NOT NULL,
|
||||||
|
address TEXT NOT NULL,
|
||||||
|
status INTEGER DEFAULT 2,
|
||||||
|
FOREIGN KEY (queue_id) REFERENCES remove_req_msg(queue_id)
|
||||||
|
);
|
||||||
109
public/msg_mysql.sql
Normal file
109
public/msg_mysql.sql
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
-- mysql
|
||||||
|
CREATE TABLE IF NOT EXISTS topup_req_msg (
|
||||||
|
queue_id VARCHAR(255) NOT NULL,
|
||||||
|
chain VARCHAR(32) NOT NULL,
|
||||||
|
symbol VARCHAR(32) NOT NULL,
|
||||||
|
address VARCHAR(255) NOT NULL,
|
||||||
|
timestamp BIGINT,
|
||||||
|
sign VARCHAR(255),
|
||||||
|
status TINYINT DEFAULT 1,
|
||||||
|
PRIMARY KEY (queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS topup_resp_msg (
|
||||||
|
queue_id VARCHAR(255) NOT NULL,
|
||||||
|
chain VARCHAR(32) NOT NULL,
|
||||||
|
symbol VARCHAR(32) NOT NULL,
|
||||||
|
from_addr VARCHAR(255) NOT NULL,
|
||||||
|
to_addr VARCHAR(255) NOT NULL,
|
||||||
|
amount DECIMAL(30,16) NOT NULL,
|
||||||
|
tx_hash VARCHAR(255) DEFAULT NULL,
|
||||||
|
height BIGINT DEFAULT NULL,
|
||||||
|
status TINYINT DEFAULT 5, --遵循constant模块的定义
|
||||||
|
FOREIGN KEY (queue_id) REFERENCES topup_req_msg(queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS withdraw_req_msg (
|
||||||
|
queue_id VARCHAR(255) NOT NULL,
|
||||||
|
chain VARCHAR(32) NOT NULL,
|
||||||
|
symbol VARCHAR(32) NOT NULL,
|
||||||
|
from_addr VARCHAR(255) NOT NULL,
|
||||||
|
to_addr VARCHAR(255) NOT NULL,
|
||||||
|
amount DECIMAL(30,16) NOT NULL,
|
||||||
|
fee DECIMAL(30,16) NOT NULL,
|
||||||
|
timestamp BIGINT,
|
||||||
|
sign VARCHAR(255),
|
||||||
|
status TINYINT DEFAULT 5, --遵循constant模块的定义
|
||||||
|
PRIMARY KEY (queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS withdraw_resp_msg (
|
||||||
|
queue_id VARCHAR(255) NOT NULL,
|
||||||
|
chain VARCHAR(32) NOT NULL,
|
||||||
|
symbol VARCHAR(32) NOT NULL,
|
||||||
|
from_addr VARCHAR(255) NOT NULL,
|
||||||
|
to_addr VARCHAR(255) NOT NULL,
|
||||||
|
tx_hash VARCHAR(255) DEFAULT NULL,
|
||||||
|
amount DECIMAL(30,16) DEFAULT NULL,
|
||||||
|
fee DECIMAL(30,16) DEFAULT NULL,
|
||||||
|
height BIGINT DEFAULT NULL,
|
||||||
|
status TINYINT DEFAULT 5, --遵循constant模块的定义
|
||||||
|
FOREIGN KEY (queue_id) REFERENCES withdraw_req_msg(queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS pay_req_msg (
|
||||||
|
queue_id VARCHAR(255) NOT NULL,
|
||||||
|
chain VARCHAR(32) NOT NULL,
|
||||||
|
symbol VARCHAR(32) NOT NULL,
|
||||||
|
from_addr VARCHAR(255) NOT NULL,
|
||||||
|
to_addr VARCHAR(255) NOT NULL,
|
||||||
|
total_amount DECIMAL(40, 16) NOT NULL,
|
||||||
|
total_fee DECIMAL(40, 16) NOT NULL,
|
||||||
|
timestamp BIGINT,
|
||||||
|
sign VARCHAR(255),
|
||||||
|
status TINYINT DEFAULT 5, --遵循constant模块的定义
|
||||||
|
PRIMARY KEY (queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS pay_resp_msg (
|
||||||
|
queue_id VARCHAR(255) NOT NULL,
|
||||||
|
chain VARCHAR(32) NOT NULL,
|
||||||
|
symbol VARCHAR(32) NOT NULL,
|
||||||
|
from_addr VARCHAR(255) NOT NULL,
|
||||||
|
pay_status TINYINT DEFAULT 5, --遵循constant模块的定义
|
||||||
|
FOREIGN KEY (queue_id) REFERENCES pay_req_msg(queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- pay_msg的交易
|
||||||
|
CREATE TABLE IF NOT EXISTS pay_msg_tx (
|
||||||
|
queue_id VARCHAR(255) NOT NULL,
|
||||||
|
tx_hash VARCHAR(255) DEFAULT NULL,
|
||||||
|
to_addr VARCHAR(255) NOT NULL,
|
||||||
|
amount DECIMAL(30,16) DEFAULT NULL,
|
||||||
|
fee DECIMAL(30,16),
|
||||||
|
height BIGINT DEFAULT 0,
|
||||||
|
status TINYINT DEFAULT 5, --遵循constant模块的定义
|
||||||
|
FOREIGN KEY (queue_id) REFERENCES pay_req_msg(queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS remove_req_msg(
|
||||||
|
queue_id VARCHAR(255) NOT NULL,
|
||||||
|
msg_type TINYINT NOT NULL,
|
||||||
|
chain VARCHAR(32) NOT NULL,
|
||||||
|
symbol VARCHAR(32) NOT NULL,
|
||||||
|
address VARCHAR(255) NOT NULL,
|
||||||
|
timestamp BIGINT,
|
||||||
|
sign VARCHAR(255),
|
||||||
|
status TINYINT DEFAULT 2,
|
||||||
|
PRIMARY KEY (queue_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS remove_resp_msg(
|
||||||
|
queue_id VARCHAR(255) NOT NULL,
|
||||||
|
msg_type TINYINT NOT NULL,
|
||||||
|
chain VARCHAR(32) NOT NULL,
|
||||||
|
symbol VARCHAR(32) NOT NULL,
|
||||||
|
address VARCHAR(255) NOT NULL,
|
||||||
|
status TINYINT DEFAULT 2,
|
||||||
|
FOREIGN KEY (queue_id) REFERENCES remove_req_msg(queue_id)
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user