Doge Emporium 🐕 – 多格商场🐕区块链毕设代写

区块链毕设代写本文提供国外最新区块链项目源码下载,包括solidity,eth,fabric等blockchain区块链,Doge Emporium 🐕 – 多格商场🐕区块链毕设代写 是一篇很好的国外资料

Doge Emporium 🐕

Doge Emporium is an expanded DApp based on Truffle’s Pet Shop Tutorial, where the shop is offering a special “Buy one and get your second free” discount.

To purchase a doge, the user needs to connect their MetaMask Wallet. Once connected, the user’s current wallet address will display below the shop logo. The owner of the shop can also revert all transactions, which allows customers to withdraw amounts they spent int the store. Only the owner can reset shop transactions and only customers who previously made purchases may withdraw ETH.

Installation 📁

There are a few technical requirements before we start. Please install the following:

  • Node.js v8+ LTS and npm (comes with Node)
  • Git

Once we have those installed, we only need one command to install Truffle:

npm install -g truffle

Clone this repo to your local machine:

git clone https://github.com/jun-sung/doge-emporium.git cd doge-emporium

Then install all the project dependencies:

npm install

Compiling, migrating, and testing the smart contract 💻

Solidity is a compiled language, meaning we need to compile our Solidity to bytecode for the Ethereum Virtual Machine (EVM) to execute. Since our DApp is setup as a Truffle project, we’ll be utilizing Truffle’s command tools to compile, migrate, and test our smart contract on a local blockchain.

Compilation

Within the project directory, run the following commands in the terminal:

truffle compile

You should see output similar to the following:
Doge Emporium 🐕 - 多格商场🐕

Migration

If there are no errors, we’ve successfully compiled our contracts and now we can migrate them to the blockchain!

Before we can migrate our contract to the blockchain, we need to have a blockchain running. In this walkthrough, we’ll use Ganache, a personal blockchain for Ethereum development you can use to deploy contracts, develop applications, and run tests. If you haven’t already, download Ganache and double click the icon to launch the application.

Doge Emporium 🐕 - 多格商场🐕

You can also run the command line version, ganache-cli via npm:

npm install -g ganache-cli

By default, Ganache should be running on port 7545. The details can be seen in the RPC Server.

Doge Emporium 🐕 - 多格商场🐕

Our truffle-config.js file is currently set to run development on Port 8545.

networks: {     development: {       host: "127.0.0.1",       port: 8545,       network_id: "*" // Match any network id     }, }

So change the Port Number to 8545 under the Server tab in Settings.

Back in our terminal, migrate the contract to the blockchain.

truffle migrate

You should see output similar to the following:

Doge Emporium 🐕 - 多格商场🐕

Testing

If migration is successful, we can now run the pre-written Solidity tests:

truffle test

If all the tests pass, you’ll see console output similar to this:

Doge Emporium 🐕 - 多格商场🐕

Interacting with the DApp in a browser 🌐

Now we’re ready to use our DApp!

Installing and configuring MetaMask

The easiest way to interact with our dapp in a browser is through MetaMask, a browser extension for both Chrome and Firefox.

  1. Install MetaMask in your browser.
  2. Once installed, a tab in your browser should open displaying the following:

Doge Emporium 🐕 - 多格商场🐕

  1. After clicking Getting Started, you should see the initial MetaMask screen. Click Import Wallet.

Doge Emporium 🐕 - 多格商场🐕

  1. Next, you should see a screen requesting anonymous analytics. Choose to decline or agree.

Doge Emporium 🐕 - 多格商场🐕

  1. In the box marked Wallet Seed, enter the mnemonic that is displayed in Ganache.

Warning: Do not use this mnemonic on the main Ethereum network (mainnet). If you send ETH to any account generated from this mnemonic, you will lose it all!

Enter a password below and click Import.

Doge Emporium 🐕 - 多格商场🐕

  1. If all goes well, MetaMask should display the following screen. Click All Done.

  2. Now we need to connect MetaMask to the blockchain created by Ganache. Click the menu that shows “Main Network” and select Custom RPC.

Doge Emporium 🐕 - 多格商场🐕

  1. In the box titled “Network Name” enter Localhost 8545, in “New RPC URL” enter http://127.0.0.1:8545, in “Chain ID” enter 1337, and click Save.

Doge Emporium 🐕 - 多格商场🐕

  1. Click the top-right X to close out of Settings and return to the Accounts page.

Each account created by Ganache is given 100 ether. You’ll notice it’s slightly less on the first account because some gas was used when the contract itself was deployed and when the tests were run. You should also see this correspond with the display on Ganache GUI.

Doge Emporium 🐕 - 多格商场🐕

Configuration is now complete.

Using the DApp

  1. Start the local web server:
npm run dev

The dev server will launch and automatically open a new browser tab containing your DApp.

  1. A MetaMask pop-up should also appear requesting your approval to allow Jun’s Doge Emporium to connect to your MetaMask wallet. Without explicit approval, you will be unable to interact with the dapp. Click Connect.

Doge Emporium 🐕 - 多格商场🐕

The active wallet address will now be displayed under the store name (Shopping as: 0x…).

  1. To use the DApp, click the Purchase button on the doge of your choice. Each doge is priced at 1 ETH.
  2. You’ll be automatically prompted to approve the transaction costing 1 ETH (excluding gas fees) by MetaMask. Click Confirm to approve the transaction.

Doge Emporium 🐕 - 多格商场🐕

  1. The page should refresh automatically and you’ll now see the button for the chosen doge display “Purchased (No longer available)” and become disabled.

Doge Emporium 🐕 - 多格商场🐕

And in MetaMask, you’ll see the transaction listed:

Doge Emporium 🐕 - 多格商场🐕

You’ll also see the same transaction listed in Ganache under the “Transactions” section and new ETH balance for the corresponding address.

  1. In order to use the shop discount, select another doge. MetaMask should now prompt you with a transaction costing 0 ETH.

Doge Emporium 🐕 - 多格商场🐕

  1. The wallet address that established the contract is the owner of the shop. Only the owner has access to the Reset button. The Reset button reverts all previous transactions at the shop and costs 0 ETH. Once reset, a Withdraw button becomes available. Only customers who have spent ETH before the reset will have access to the Withdraw button.

Doge Emporium 🐕 - 多格商场🐕

Here’s a condensed version of step 7 where the owner purchases 2 doges with the shop discount, resets the store and withdraws the 1 ETH previously spent:

Doge Emporium 🐕 - 多格商场🐕

The wallet has virtually returned to the original balance of 100 ETH minus gas fees from contract migration and transactions.

Appendix 🔗

Here’s a video walkthrough of the DApp in action.

Technical Documentation

File Description
design_pattern_decisions.md Smart contract design rationale
avoiding_common_attacks.md Security measures against hacks
deployed_addresses.txt Ropsten testnet deployment info

To-Dos

  • Add onlyOwner withdraw function
function withdraw() external onlyOwner {   address payable _owner = address(uint160(owner()));   _owner.transfer(address(this).balance); } 
  • Reconfigure previous withdraw function as claimRefund
  • Add one or more tests to TestPurchase.sol and add corresponding events in Purchase.sol
  • Update DogeEmporium.js with tests
  • Implement ERC721.sol for tokenizing and allowing customers to swap doges
  • Test and finalize web3/MetaMask boilerplate in index.html
  • Simplify truffle-config.js and README.md to use default Port 7545
  • Add different types of dogs
  • Stylize buttons
  • Display doges purchased by customer in index.html (in another window or pop-up alert)
function displayDoges(ids) {   $("#doges").empty();   for (id of ids) {     // Look up doge details from our contract. Returns a `doge` object     getDogeDetails(id)     .then(function(doge) {       // Using ES6's "template literals" to inject variables into the HTML.       // Append each one to our #doges div       $("#doges").append(`<div class="doge">         <ul>           <li>Name: ${doge.name}</li>           <li>Breed: ${doge.breed}</li>           <li>Age: ${doge.age}</li>           <li>Location: ${doge.location}</li>         </ul>       </div>`);     });   } } 
// Create struct in Purchase.sol   struct doge {     string name;     string breed;     uint age;     string location;   } 
  • Reflect doge purchase by displaying on dapp front-end via an event listener
// Create event in Purchase.sol event Purchased(uint dogeId, string name, string breed); // dogeId = tokenId? 
// Subscribe to events in index.html // Add this code at the end of the startApp function to make sure the dogeEmporium contract has been initialized before adding an event listener  dogeEmporium.events.Purchased() .on("data", function(event) {   let doge = event.returnValues;   // We can access this event's 3 return values on the `event.returnValues` object:   console.log(name + " was successfully purchased!", doge.dogeId, doge.name, doge.breed); }).on("error", console.error);  // Use `filter` to only fire this code when `_to` equals `userAccount` dogeEmporium.events.Transfer({ filter: { _to: userAccount } }) // Transfer is located in ERC721.sol .on("data", function(event) {   let data = event.returnValues;   // The current user just received a doge!   // Do something here to update the UI to show it   getDogesByOwner(userAccount).then(displayDoges); }).on("error", console.error);  // Query past events | Using events as cheaper form of storage dogeEmporium.getPastEvents("DogePurchased", { fromBlock: 0, toBlock: "latest" }) .then(function(events) {   // `events` is an array of `event` objects that we can iterate, like we did above   // This code will get us a list of every doge that was ever purchased }); 

多格商场🐕

多格商场是一个扩大的DApp的基础上松露的宠物店教程,在那里的商店是提供一个特别的“买一个,得到你的第二次免费”折扣。

要购买狗,用户需要连接MetaMask钱包。连接后,用户当前的钱包地址将显示在商店徽标下方。店主还可以恢复所有交易,这允许客户提取他们在商店中花费的金额。只有店主可以重置店铺交易,只有以前购买过的顾客可以取款。

安装📁 编译、迁移和测试智能合约💻

在我们开始之前有一些技术要求。请安装以下程序:

  • Git
  • 在浏览器中安装MetaMask。

一旦我们安装了这些程序,我们只需要一个命令即可安装Truffle:

npm install -g truffle

将此repo克隆到本地计算机:

git clone https://github.com/jun-sung/doge-emporium.git cd doge-emporium

然后安装所有项目依赖项:

npm install

编译

Solidity是一种编译语言,这意味着我们需要将我们的稳定度编译成字节码,以太坊eth虚拟机(EVM)才能执行。由于我们的DApp被设置为一个Truffle项目,我们将利用Truffle的命令工具在本地区块链blockchain上编译、迁移和测试我们的智能合约。

迁移

在项目目录中,在终端中运行以下命令:

truffle compile

您应该会看到类似于以下内容的输出:
Doge Emporium 🐕 - 多格商场🐕

测试

如果没有错误,我们已经成功地编译了合同,现在我们可以将它们迁移到区块链blockchain

在我们将合同迁移到区块链blockchain之前,我们需要运行一个区块链blockchain。在本演练中,我们将使用Ganache,这是一个用于以太坊eth开发的个人区块链blockchain,您可以使用它来部署合同、开发应用程序和运行测试。如果还没有,请下载Ganache并双击图标以启动应用程序。

Doge Emporium 🐕 - 多格商场🐕

您还可以通过npm运行命令行版本ganache cli:

npm install -g ganache-cli

默认情况下,ganache应该在端口7545上运行。详细信息可以在RPC服务器中看到。我们的松露-配置.js文件当前设置为在端口8545上运行开发。

因此在“设置”中的“服务器”选项卡下,将端口号更改为8545。

回到我们的终端,将合同迁移到区块链blockchain

networks: {     development: {       host: "127.0.0.1",       port: 8545,       network_id: "*" // Match any network id     }, }

您将看到类似于以下内容的输出:

Doge Emporium 🐕 - 多格商场🐕

truffle migrate

如果迁移成功,我们现在可以运行预写的坚固性测试:

如果所有测试都通过,您将看到类似于以下内容的控制台输出:

在浏览器中与DApp交互🌐

现在我们可以使用我们的DApp了!

truffle test

在浏览器中与我们的dapp交互的最简单方法是通过MetaMask,它是Chrome和Firefox的浏览器扩展。

Doge Emporium 🐕 - 多格商场🐕

使用DApp安装和配置MetaMask

Doge Emporium 🐕 - 多格商场🐕

🔗

技术文档节点.jsv8+LTS和npm(随Node提供)
  • Git
  • 在浏览器中安装MetaMask。
  • 安装后,浏览器中的选项卡将打开,显示以下内容:
  • 单击“开始”后,您将看到初始元掩码屏幕。单击导入钱包。
  • 接下来,您将看到一个请求匿名分析的屏幕。选择拒绝或同意。
  • 在标有Wallet Seed的框中,输入Ganache中显示的助记符。
  • 如果一切顺利,MetaMask将显示以下屏幕。单击“全部完成”。
  • 现在我们需要将MetaMask连接到由Ganache创建的区块链blockchain。单击显示“主网络”的菜单并选择Custom RPC。
  • 在标题为“网络名称”的框中输入Localhost 8545,在“新RPC URL”中输入http://127.0.0.1:8545,在“链ID”中输入1337,然后单击“保存”。
  • 单击右上角的X关闭设置并返回“帐户”页。
  • 启动本地web服务器:
  • 还会出现一个MetaMask弹出窗口,请求您批准,以允许Jun’s Doge商场连接到您的MetaMask钱包。如果没有明确的批准,您将无法与dapp进行交互。单击“连接”。
  • 要使用DApp,请单击所选doge上的“购买”按钮。每只狗的价格是1 ETH。
  • 系统将自动提示您通过MetaMask批准成本为1 ETH(不包括煤气费)的交易。单击“确认”以批准交易记录。
  • 页面应自动刷新,您现在将看到所选数据显示的按钮“已购买(不再提供)”并被禁用。
  • 要使用商店折扣,请选择另一个道具。元掩码现在应该提示您一个成本为0 ETH的事务。
  • 订立合同的钱包地址是商店的所有者。只有所有者可以访问重置按钮。“重置”按钮可还原商店以前的所有交易,成本为0 ETH。一旦重置,一个退出按钮将变为可用。只有在重置前消费过ETH的客户才能使用“撤消”按钮。
  • 仅添加所有者撤消功能
  • 将以前的撤消功能重新配置为索赔退款
  • 将一个或多个测试添加到测试采购.sol并在中添加相应的事件采购.sol
  • 更新DogeEmporium.js公司通过测试
  • 实现ERC721.sol进行标记化并允许客户交换狗
  • 在中测试并最终确定web3/MetaMask样板文件索引.html简化松露-配置.js以及自述文件.md要使用默认端口7545,请添加不同类型的狗
  • 样式化按钮
  • 显示客户在中购买的狗索引.html(在另一个窗口或弹出的警报中)
  • 反映doge购买方式通过事件侦听器在dapp前端显示_决策.md智能合约设计原理_攻击.md已部署针对黑客的安全措施_地址.txt Ropsten testnet部署信息
  • 警告:不要在主以太坊eth网络(mainnet)上使用此助记符。如果你把ETH发送到任何一个由这个助记符生成的账户,你将失去所有!

    1. 安装后,浏览器中的选项卡将打开,显示以下内容:
    2. 单击“开始”后,您将看到初始元掩码屏幕。单击导入钱包。

    在下面输入密码,然后单击导入。

    1. 接下来,您将看到一个请求匿名分析的屏幕。选择拒绝或同意。

    Doge Emporium 🐕 - 多格商场🐕

    1. 在标有Wallet Seed的框中,输入Ganache中显示的助记符。

    如果一切顺利,MetaMask应显示以下屏幕。单击“全部完成”。

    1. 如果一切顺利,MetaMask将显示以下屏幕。单击“全部完成”。

    现在我们需要将MetaMask连接到由Ganache创建的区块链blockchain。单击显示“主网络”的菜单并选择Custom RPC。

    Doge Emporium 🐕 - 多格商场🐕

    Doge Emporium 🐕 - 多格商场🐕

    1. 现在我们需要将MetaMask连接到由Ganache创建的区块链blockchain。单击显示“主网络”的菜单并选择Custom RPC。
    2. 在标题为“网络名称”的框中输入Localhost 8545,在“新RPC URL”中输入http://127.0.0.1:8545,在“链ID”中输入1337,然后单击“保存”。

    配置现在完成。

    1. 单击右上角的X关闭设置并返回“帐户”页。

    开发人员服务器将启动并自动打开包含DApp的新浏览器选项卡。

    1. 启动本地web服务器:

    Doge Emporium 🐕 - 多格商场🐕

    活动钱包地址现在将显示在商店名称下(购物方式:0x…)。

    Doge Emporium 🐕 - 多格商场🐕

    技术文档节点.jsv8+LTS和npm(随Node提供)
  • Git
  • 在浏览器中安装MetaMask。
  • 安装后,浏览器中的选项卡将打开,显示以下内容:
  • 单击“开始”后,您将看到初始元掩码屏幕。单击导入钱包。
  • 接下来,您将看到一个请求匿名分析的屏幕。选择拒绝或同意。
  • 在标有Wallet Seed的框中,输入Ganache中显示的助记符。
  • 如果一切顺利,MetaMask将显示以下屏幕。单击“全部完成”。
  • 现在我们需要将MetaMask连接到由Ganache创建的区块链blockchain。单击显示“主网络”的菜单并选择Custom RPC。
  • 在标题为“网络名称”的框中输入Localhost 8545,在“新RPC URL”中输入http://127.0.0.1:8545,在“链ID”中输入1337,然后单击“保存”。
  • 单击右上角的X关闭设置并返回“帐户”页。
  • 启动本地web服务器:
  • 还会出现一个MetaMask弹出窗口,请求您批准,以允许Jun’s Doge商场连接到您的MetaMask钱包。如果没有明确的批准,您将无法与dapp进行交互。单击“连接”。
  • 要使用DApp,请单击所选doge上的“购买”按钮。每只狗的价格是1 ETH。
  • 系统将自动提示您通过MetaMask批准成本为1 ETH(不包括煤气费)的交易。单击“确认”以批准交易记录。
  • 页面应自动刷新,您现在将看到所选数据显示的按钮“已购买(不再提供)”并被禁用。
  • 要使用商店折扣,请选择另一个道具。元掩码现在应该提示您一个成本为0 ETH的事务。
  • 订立合同的钱包地址是商店的所有者。只有所有者可以访问重置按钮。“重置”按钮可还原商店以前的所有交易,成本为0 ETH。一旦重置,一个退出按钮将变为可用。只有在重置前消费过ETH的客户才能使用“撤消”按钮。
  • 仅添加所有者撤消功能
  • 将以前的撤消功能重新配置为索赔退款
  • 将一个或多个测试添加到测试采购.sol并在中添加相应的事件采购.sol
  • 更新DogeEmporium.js公司通过测试
  • 实现ERC721.sol进行标记化并允许客户交换狗
  • 在中测试并最终确定web3/MetaMask样板文件索引.html简化松露-配置.js以及自述文件.md要使用默认端口7545,请添加不同类型的狗
  • 样式化按钮
  • 显示客户在中购买的狗索引.html(在另一个窗口或弹出的警报中)
  • 反映doge购买方式通过事件侦听器在dapp前端显示_决策.md智能合约设计原理_攻击.md已部署针对黑客的安全措施_地址.txt Ropsten testnet部署信息
    1. 还会出现一个MetaMask弹出窗口,请求您批准,以允许Jun’s Doge商场连接到您的MetaMask钱包。如果没有明确的批准,您将无法与dapp进行交互。单击“连接”。
    npm run dev

    Doge Emporium 🐕 - 多格商场🐕

    1. 要使用DApp,请单击所选doge上的“购买”按钮。每只狗的价格是1 ETH。

    在MetaMask中,您将看到列出的事务:

    Doge Emporium 🐕 - 多格商场🐕

    1. 系统将自动提示您通过MetaMask批准成本为1 ETH(不包括煤气费)的交易。单击“确认”以批准交易记录。
    2. 页面应自动刷新,您现在将看到所选数据显示的按钮“已购买(不再提供)”并被禁用。

    您还将看到在“事务”部分的Ganache中列出的相同事务以及相应地址的新ETH余额。

    1. 要使用商店折扣,请选择另一个道具。元掩码现在应该提示您一个成本为0 ETH的事务。

    Doge Emporium 🐕 - 多格商场🐕

    Doge Emporium 🐕 - 多格商场🐕

    这是第7步的精简版本,主人购买2只狗,享受商店折扣,重置商店并提取之前花费的1 ETH:

    Doge Emporium 🐕 - 多格商场🐕

    1. 订立合同的钱包地址是商店的所有者。只有所有者可以访问重置按钮。“重置”按钮可还原商店以前的所有交易,成本为0 ETH。一旦重置,一个退出按钮将变为可用。只有在重置前消费过ETH的客户才能使用“撤消”按钮。

    钱包实际上已恢复到100 ETH的原始余额减去合同迁移和交易产生的天然气费用。

    1. 仅添加所有者撤消功能

    以下是DApp的视频演示。

    Here’s a condensed version of step 7 where the owner purchases 2 doges with the shop discount, resets the store and withdraws the 1 ETH previously spent:

    Doge Emporium 🐕 - 多格商场🐕

    The wallet has virtually returned to the original balance of 100 ETH minus gas fees from contract migration and transactions.

    Appendix 🔗

    Here’s a video walkthrough of the DApp in action.

    Technical Documentation

    File Description
    Ropsten testnet部署信息 Smart contract design rationale
    avoiding_common_attacks.md Security measures against hacks
    deployed_addresses.txt Ropsten testnet deployment info

    To-Dos

    • 将以前的撤消功能重新配置为索赔退款
    function withdraw() external onlyOwner {   address payable _owner = address(uint160(owner()));   _owner.transfer(address(this).balance); } 
    • 将一个或多个测试添加到测试采购.sol并在中添加相应的事件采购.sol
    • 更新DogeEmporium.js公司通过测试
    • 实现ERC721.sol进行标记化并允许客户交换狗
    • 在中测试并最终确定web3/MetaMask样板文件索引.html简化松露-配置.js以及自述文件.md要使用默认端口7545,请添加不同类型的狗
    • 样式化按钮
    • 显示客户在中购买的狗索引.html(在另一个窗口或弹出的警报中)
    • 反映doge购买方式通过事件侦听器在dapp前端显示_决策.md智能合约设计原理_攻击.md已部署针对黑客的安全措施_地址.txt Ropsten testnet部署信息
    • Stylize buttons
    • Display doges purchased by customer in index.html (in another window or pop-up alert)
    function displayDoges(ids) {   $("#doges").empty();   for (id of ids) {     // Look up doge details from our contract. Returns a `doge` object     getDogeDetails(id)     .then(function(doge) {       // Using ES6's "template literals" to inject variables into the HTML.       // Append each one to our #doges div       $("#doges").append(`<div class="doge">         <ul>           <li>Name: ${doge.name}</li>           <li>Breed: ${doge.breed}</li>           <li>Age: ${doge.age}</li>           <li>Location: ${doge.location}</li>         </ul>       </div>`);     });   } } 
    // Create struct in Purchase.sol   struct doge {     string name;     string breed;     uint age;     string location;   } 
    • Reflect doge purchase by displaying on dapp front-end via an event listener
    // Create event in Purchase.sol event Purchased(uint dogeId, string name, string breed); // dogeId = tokenId? 
    // Subscribe to events in index.html // Add this code at the end of the startApp function to make sure the dogeEmporium contract has been initialized before adding an event listener  dogeEmporium.events.Purchased() .on("data", function(event) {   let doge = event.returnValues;   // We can access this event's 3 return values on the `event.returnValues` object:   console.log(name + " was successfully purchased!", doge.dogeId, doge.name, doge.breed); }).on("error", console.error);  // Use `filter` to only fire this code when `_to` equals `userAccount` dogeEmporium.events.Transfer({ filter: { _to: userAccount } }) // Transfer is located in ERC721.sol .on("data", function(event) {   let data = event.returnValues;   // The current user just received a doge!   // Do something here to update the UI to show it   getDogesByOwner(userAccount).then(displayDoges); }).on("error", console.error);  // Query past events | Using events as cheaper form of storage dogeEmporium.getPastEvents("DogePurchased", { fromBlock: 0, toBlock: "latest" }) .then(function(events) {   // `events` is an array of `event` objects that we can iterate, like we did above   // This code will get us a list of every doge that was ever purchased }); 

    部分转自网络,侵权联系删除区块链源码网

    www.interchains.cc

    https://www.interchains.cc/20247.html

    区块链毕设网(www.interchains.cc)全网最靠谱的原创区块链毕设代做网站 部分资料来自网络,侵权联系删除! 最全最大的区块链源码站 ! QQ3039046426
    区块链知识分享网, 以太坊dapp资源网, 区块链教程, fabric教程下载, 区块链书籍下载, 区块链资料下载, 区块链视频教程下载, 区块链基础教程, 区块链入门教程, 区块链资源 » Doge Emporium 🐕 – 多格商场🐕区块链毕设代写

    提供最优质的资源集合

    立即查看 了解详情