千家信息网

比原是怎么通过list-transactions显示交易信息的

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇文章主要讲解了"比原是怎么通过list-transactions显示交易信息的",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"比原是怎么通过list
千家信息网最后更新 2026年11月01日比原是怎么通过list-transactions显示交易信息的

这篇文章主要讲解了"比原是怎么通过list-transactions显示交易信息的",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"比原是怎么通过list-transactions显示交易信息的"吧!

问题:在提交的交易成功完成后,前端会以列表的方式显示交易信息,它是如何拿到后台的数据的?也就是下图是如何实现的:

由于它同时涉及到了前端和后端,所以我们同样把它分成了两个小问题:

  1. 前端是如何获取交易数据并显示出来的?

  2. 后端是如何找到交易数据的?

下面依次解决。

前端是如何获取交易数据并显示出来的?

我们先在比原的前端代码库中寻找。由于这个功能是"列表分页"显示,这让我想起了前面有一个类似的功能是分页显示余额,那里用的是src/features/shared/components/BaseList提供的通用组件,所以这边应该也是一样。

经过查看,果然在src/features/transactions/components/下面发现了List.jsx文件,且目录结构跟src/features/balances/components/非常相似,再大略看一下代码,就能确定使用了一样的方法。

但是如果回想一下,过程似乎还是有点不同。在显示余额那里,是我们手动点击了左侧栏的菜单,使得前端的路由转到了/balances,然后由

src/features/shared/routes.js#L5-L44

const makeRoutes = (store, type, List, New, Show, options = {}) => {  // ...  return {    path: options.path || type + 's',    component: RoutingContainer,    name: options.name || humanize(type + 's'),    name_zh: options.name_zh,    indexRoute: {      component: List,      onEnter: (nextState, replace) => {        loadPage(nextState, replace)      },      onChange: (_, nextState, replace) => { loadPage(nextState, replace) }    },    childRoutes: childRoutes  }}

中的onEnter或者onChange触发了loadPage,最后一步步调用了后台接口/list-balances。而这次在本文的例子中,它是在提交了"提交交易"表单成功后,自动转到了"列表显示交易"的页面,会不会同样触发onEnter或者onChange呢?

答案是会的,因为在前文中,当submitForm执行后,向后台的最后一个请求/submit-transaction成功以后,会调用dealSignSubmitResp这个函数,而它的定义是:

src/features/transactions/actions.js#L120-L132

const dealSignSubmitResp = resp => {  // ...  dispatch(push({    pathname: '/transactions',    state: {      preserveFlash: true    }  }))}

可以看到,它最后也会切换前端路由到/transactions,跟显示余额那里就是完全一样的路线了。所以按照那边的经验,到最后一定会访问后台的/list-transactions接口。

这过程中的推导就不再详说,需要的话可以看前面讲解"比原是如何显示余额的"那篇文章。

最后拿到了后台返回的数据如何以表格形式显示出来,在那篇文章中也提到,这里也跳过。

后端是如何找到交易数据的?

当我们知道了前端会访问后台的/list-transactions接口后,我们就很容易的在比原的主项目仓库中找到下面的代码:

api/api.go#L164-L244

func (a *API) buildHandler() {    // ...    if a.wallet != nil {        // ...        m.Handle("/list-transactions", jsonHandler(a.listTransactions))        // ...}

可以看到,list-transactions对应的handler是a.listTransactions:

api/query.go#L83-L107

func (a *API) listTransactions(ctx context.Context, filter struct {    ID        string `json:"id"`    AccountID string `json:"account_id"`    Detail    bool   `json:"detail"`}) Response {    transactions := []*query.AnnotatedTx{}    var err error    // 1.     if filter.AccountID != "" {        transactions, err = a.wallet.GetTransactionsByAccountID(filter.AccountID)    } else {        transactions, err = a.wallet.GetTransactionsByTxID(filter.ID)    }    // ...    // 2.    if filter.Detail == false {        txSummary := a.wallet.GetTransactionsSummary(transactions)        return NewSuccessResponse(txSummary)    }    return NewSuccessResponse(transactions)}

从这个方法的参数可以看到,前端是可以传过来id,account_id和detail这三个参数的。而在本文的例子中,因为是直接跳转到/transactions的路由,所以什么参数也没有传上来。

我把代码分成了两块,一些错误处理的部分被我省略了。依次讲解:

  1. 第1处是想根据参数来获取transactions。如果account_id有值,则拿它去取,即某个帐户拥有的交易;否则的话,用id去取,这个id指的是交易的id。如果这两个都没有值,应该是在第二个分支中处理,即a.wallet.GetTransactionsByTxID应该也可以处理参数为空字符串的情况

  2. 第2处代码,如果detail为false(如果前端没传值,也应该是默认值false,则将前面拿到的transactions变成摘要,只返回部分信息;否则的话,返回完整信息。

我们先进第1处代码中的a.wallet.GetTransactionsByAccountID:

wallet/indexer.go#L505-L523

func (w *Wallet) GetTransactionsByAccountID(accountID string) ([]*query.AnnotatedTx, error) {    annotatedTxs := []*query.AnnotatedTx{}    // 1.    txIter := w.DB.IteratorPrefix([]byte(TxPrefix))    defer txIter.Release()    // 2.    for txIter.Next() {        // 3.        annotatedTx := &query.AnnotatedTx{}        if err := json.Unmarshal(txIter.Value(), &annotatedTx); err != nil {            return nil, err        }        // 4.        if findTransactionsByAccount(annotatedTx, accountID) {            annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})            annotatedTxs = append(annotatedTxs, annotatedTx)        }    }    return annotatedTxs, nil}

这里把代码分成了4块:

  1. 第1处代码是遍历数据库中以TxPrefix为前缀的值,其中TxPrefix为TXS:,下面要进行遍历。这里的DB无疑是wallet这个leveldb

  2. 第2处开始进行遍历

  3. 第3处是把每一个元素的Value拿出来,它是JSON格式的,把它转成AnnotatedTx对象。txIter的每一个元素是一个key-pair,有Key(),也有Value(),这里只用到了Value()

  4. 第4处是在当前的这个annotatedTx对象中寻找,如果它的input或者output中包含的帐户的id等于accountId,则它是我们需要的。后面再使用annotateTxsAsset方法把annotatedTx对象中的asset相关的属性(比如alias等)补全。

其中AnnotatedTx的定义值得一看:

blockchain/query/annotated.go#L12-L22

type AnnotatedTx struct {    ID                     bc.Hash            `json:"tx_id"`    Timestamp              uint64             `json:"block_time"`    BlockID                bc.Hash            `json:"block_hash"`    BlockHeight            uint64             `json:"block_height"`    Position               uint32             `json:"block_index"`    BlockTransactionsCount uint32             `json:"block_transactions_count,omitempty"`    Inputs                 []*AnnotatedInput  `json:"inputs"`    Outputs                []*AnnotatedOutput `json:"outputs"`    StatusFail             bool               `json:"status_fail"`}

它其实就是为了持有最后返回给前端的数据,通过给每个字段添加JSON相关的annotation方便转换成JSON。

如果前端没有传account_id参数,则会进入另一个分支,对应a.wallet.GetTransactionsByTxID(filter.ID):

wallet/indexer.go#L426-L450

func (w *Wallet) GetTransactionsByTxID(txID string) ([]*query.AnnotatedTx, error) {    annotatedTxs := []*query.AnnotatedTx{}    formatKey := ""    if txID != "" {        rawFormatKey := w.DB.Get(calcTxIndexKey(txID))        if rawFormatKey == nil {            return nil, fmt.Errorf("No transaction(txid=%s) ", txID)        }        formatKey = string(rawFormatKey)    }    txIter := w.DB.IteratorPrefix(calcAnnotatedKey(formatKey))    defer txIter.Release()    for txIter.Next() {        annotatedTx := &query.AnnotatedTx{}        if err := json.Unmarshal(txIter.Value(), annotatedTx); err != nil {            return nil, err        }        annotateTxsAsset(w, []*query.AnnotatedTx{annotatedTx})        annotatedTxs = append([]*query.AnnotatedTx{annotatedTx}, annotatedTxs...)    }    return annotatedTxs, nil}

这个方法看起来挺长,实际上逻辑比较简单。如果前端传了txID,则会在wallet中寻找指定的id的交易对象;否则的话,取出全部(也就是本文的情况)。其中calcTxIndexKey(txID)的定义是:

wallet/indexer.go#L61-L63

func calcTxIndexKey(txID string) []byte {    return []byte(TxIndexPrefix + txID)}

其中TxIndexPrefix是TID:。

calcAnnotatedKey(formatKey)的定义是:

wallet/indexer.go#L53-L55

func calcAnnotatedKey(formatKey string) []byte {    return []byte(TxPrefix + formatKey)}

其中TxPrefix的值是TXS:。

我们再进入listTransactions的第2处,即detail那里。如果detail为false,则只需要摘要,所以会调用a.wallet.GetTransactionsSummary(transactions):

wallet/indexer.go#L453-L486

func (w *Wallet) GetTransactionsSummary(transactions []*query.AnnotatedTx) []TxSummary {    Txs := []TxSummary{}    for _, annotatedTx := range transactions {        tmpTxSummary := TxSummary{            Inputs:    make([]Summary, len(annotatedTx.Inputs)),            Outputs:   make([]Summary, len(annotatedTx.Outputs)),            ID:        annotatedTx.ID,            Timestamp: annotatedTx.Timestamp,        }        for i, input := range annotatedTx.Inputs {            tmpTxSummary.Inputs[i].Type = input.Type            tmpTxSummary.Inputs[i].AccountID = input.AccountID            tmpTxSummary.Inputs[i].AccountAlias = input.AccountAlias            tmpTxSummary.Inputs[i].AssetID = input.AssetID            tmpTxSummary.Inputs[i].AssetAlias = input.AssetAlias            tmpTxSummary.Inputs[i].Amount = input.Amount            tmpTxSummary.Inputs[i].Arbitrary = input.Arbitrary        }        for j, output := range annotatedTx.Outputs {            tmpTxSummary.Outputs[j].Type = output.Type            tmpTxSummary.Outputs[j].AccountID = output.AccountID            tmpTxSummary.Outputs[j].AccountAlias = output.AccountAlias            tmpTxSummary.Outputs[j].AssetID = output.AssetID            tmpTxSummary.Outputs[j].AssetAlias = output.AssetAlias            tmpTxSummary.Outputs[j].Amount = output.Amount        }        Txs = append(Txs, tmpTxSummary)    }    return Txs}

这一段的代码相当直白,就是从transactions的元素中取出部分比较重要的信息,组成新的TxSummary对象,返回过去。最后,这些对象再变成JSON返回给前端。

那么今天的这个小问题就算解决了,由于有之前的经验可以利用,所以感觉就比较简单了。

感谢各位的阅读,以上就是"比原是怎么通过list-transactions显示交易信息的"的内容了,经过本文的学习后,相信大家对比原是怎么通过list-transactions显示交易信息的这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

交易 前端 代码 信息 数据 参数 后台 对象 余额 方法 问题 成功 元素 就是 情况 接口 是在 篇文章 路由 部分 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 广州紫鲸互联网科技有限公司 新华互联网科技学校 互联网与科技发展 汇久创客互联网科技 顺玮阁互联网科技 互联网科技有限公司 翻译 互联网科技公司的市场部门 银川新华互联网科技学校地址 青岛中康互联网医疗科技有限公司 随着互联网科技发展 互联网科技项目计划书材料类 宝灵互联网科技有限公司 厦门千工互联网科技有限公司 互联网科技大奖都有哪些公司 华中科技大学互联网的牛人 互联网金融科技公司的优势 广州富喜互联网络科技 江苏互联网智能科技软件 广州互联网高科技取餐柜 宁波允成互联网科技有限公司 广州淘业互联网络科技 猪兼强互联网科技 亿账通互联网科技 互联网科技汛 佰商互联网科技有限公司 美国互联网前沿科技 互联网还是高科技吗 互联网科技公司实习生 朱啸虎曾投资哪些互联网科技企业 六子台科技互联网展会
0