基于 web3.js ipfs 实现的dapp

创建项目
1.truffle unbox webpack
创建完成后
app文件夹用来存放 前台页面
contracts 用来存放合约代码
migrations 用来部署项目
node_modules 依赖文件
test 测试
sha3 跟改为 keccak256
keccak256(abi.encodePacked(_amount, _secret));
1通过 truffle framework和Solidity实现合约代码,并将其部署到truffle develop 自带的测试网络中,摒弃在truffle console中自由交互
使用ipfs通过命令安装并与之交互
后端实现完成后,构建web前端与合约喝ipfs进行交互
安装mongodb 并设计数据库结构来存储参评
数据库启动后 实现监听合约事件的nodejs 服务端代码,将请求记录到控制台,然后执行将产品插入数据库
我们将更新我的们的前端,从数据库而不是区块链中查找产品
实现托管合同和相应的前端,参与者可以向买方/卖方发放或者退款
在contracts 目录下创建 EcommerceStore.sol文件
pragma solidity ^0.4.17;
pragma solidity ^0.4.17;

contract EcommerceStroe {
enum ProductStatus{
// 开始买
Open,
//结束
Sold,
//没人买
Unsold
}
//产品
enum ProductCondition{
New,
Used
}

uint public productIndex;
//通过卖家的钱包地址找到卖家的详细信息
mapping(address =>mapping(uint => Product)) stores;
//卖家的钱包地址
mapping(uint => address ) productIdInStore;
//存取投标的相关信息
struct Bid{
address bidder;
uint productId;
uint value;
bool revealed;

}
struct Product{
uint id; // 产品id
string name; // 产品名字
string category;// 分类
string imageLink;//图片的hash
string descLink;//图片描述信息的hash
uint auctionStartTime;//开始竞标的时间
uint auctionEndTime;//竞标结束的时间
uint startPrice;//拍卖价格
address highestBidder;//赢家的钱包地址
uint highestBid;//赢家竞标的价格
uint secondHighestBid;//第二高的这个人的地址
uint totalBids;//一共有多少人参与竞标
ProductStatus status; //产品的状态
ProductCondition condition;// 是新的还是旧的
//存取投标人的信息
mapping(address =>mapping(bytes32 => Bid)) bids;
}
//构造函数
function EcommerceStore() public{
productIndex = 0;
}
/* 添加产品到区块链/
function addProductToStore(string _name, string _category, string _imageLink, string _descLink, uint _auctionStartTime, uint _auctionEndTime, uint _startPrice, uint _productCondition) public {
//断言 其实时间是否小于竞标时间
require(_auctionStartTime < _auctionEndTime);
//产品id直接让他自+1
productIndex += 1;
Product memory product = Product(productIndex, _name, _category, _imageLink, _descLink, _auctionStartTime, _auctionEndTime, _startPrice, 0, 0, 0, 0, ProductStatus.Open, ProductCondition(_productCondition));
stores[msg.sender][productIndex] = product;
productIdInStore[productIndex] = msg.sender;
}
/
通过产品ID读取产品信息 */
function getProduct(uint _productId)view public returns(uint, string, string, string, string, uint, uint, uint, ProductStatus, ProductCondition) {
Product memory product = stores[productIdInStore[_productId]][_productId];
//元组
return (product.id, product.name, product.category, product.imageLink, product.descLink, product.auctionStartTime, product.auctionEndTime, product.startPrice, product.status, product.condition);
}
//投奔
function bid(uint _productId, bytes32 _bid) payable public returns(bool) {
Product storage product = stores[productIdInStore[_productId]][_productId];
//当前时间必须大于产品开始竞标的时间
require(now >= product.auctionStartTime);
//当前时间必须小于产品结束竞标的时间
require(now <= product.auctionEndTime);
require(msg.value > product.startPrice);
//投奔的时候信息为‘’
require(product.bids[msg.sender][_bid].bidder == 0);
//存储竞标人的信息msg.value 用于迷惑别人的价格
product.bids[msg.sender][_bid] = Bid(msg.sender, _productId, msg.value, false);
//统计投标人的数量
product.totalBids += 1;
return true;
}
//投标公告
function revealBid(uint _productId, string _amount, string _secret) public {
Product storage product = stores[productIdInStore[_productId]][_productId];
require(now > product.auctionEndTime);
bytes32 sealedBid = sha3(_amount,_secret);
//msg.sender 投标人的钱包地址
Bid memory bidInfo = product.bids[msg.sender][sealedBid];
require(bidInfo.bidder > 0);
require(bidInfo.revealed == false);

 uint refund;//退款
 //将字符串转化成uint 类型
 uint amount = stringToUint(_amount);

if (bidInfo.value < amount) {

 refund = bidInfo.value;

} else {

 if (address(product.highestBidder) == 0) {
   product.highestBidder = msg.sender;
   product.highestBid = amount;
   product.secondHighestBid = product.startPrice;
   refund = bidInfo.value - amount;
 } else {
   if (amount > product.highestBid) {
     product.secondHighestBid = product.highestBid;
     // 退款
     product.highestBidder.transfer(product.highestBid);
     product.highestBidder = msg.sender;
     product.highestBid = amount;
     refund = bidInfo.value - amount;
   } else if (amount > product.secondHighestBid) {
     product.secondHighestBid = amount;
     refund = amount;
   } else {
     refund = amount;
   }
 }
 if (refund > 0) {
   msg.sender.transfer(refund);
   //表示公告成功
   product.bids[msg.sender][sealedBid].revealed = true;
 }

}
}

function highestBidderInfo(uint _productId) view public returns(address, uint, uint) {
Product memory product = stores[productIdInStore[_productId]][_productId];
return (product.highestBidder, product.highestBid, product.secondHighestBid);
}
//查询当前产品投标人的数量
function totalBids(uint _productId) view public returns(uint) {
Product memory product = stores[productIdInStore[_productId]][_productId];
return product.totalBids;
}
// 将字符串转换成uint类型
function stringToUint(string s) pure private returns(uint) {
bytes memory b = bytes(s);
uint result = 0;
for (uint i = 0; i < b.length; i++) {
if (b[i] >= 48 && b[i] <= 57) {
result = result * 10 + (uint(b[i]) - 48);
}
}
return result;
}
}

部署项目
在migrations下 2_deploy_contracts.js 修改代码
var EcommerceStroe = artifacts.require('./EcommerceStroe.sol')
module.exports = function (deployer) {
deployer.deploy(EcommerceStroe)
}
启动命令行到根目录
truffle deveplop
进行编译
compile
部署合约
migrate
添加启始价格
amt_1 =web3.toWei(1,'ether')
获取当前时间
current_time = Math.round(new Date() /1000);
EcommerceStore.deployed().then(function(i){i.addProductToStore('iphone 6s','Cell Phones & Accessories','imagelink','desclink',current_time,current_time +200, amt_1,0).then(function(f) {console.log(f)})});
查看添加的合约
var c
EcommerceStore.deployed().then((i)=>{ c=i});
//查看值
c.productIndex()
查看产品信息
EcommerceStore.deployed().then(function(i){i.getProduct.call(1).then(functino(f){console.log(f)})})
安装依赖包 对文件进行加密
插件 安装
第一种:npm install --save-dev ethereumjs-util
安装依赖包 对文件进行加密
找到package.json
在“webpack-dev-server” 下添加
第二种:"webpack-dev-server":"^2.3.0"
打开控制台
当前目录下 truffle develop
进行编译
compile
进行部署
migrate --reset
产品集成ipfs
找到package.json
在“webpack-dev-server” 下添加
"ipfs-api":"14.1.2"
把ipfs集成到项目里
在app/javascripts
import "../stylesheets/app.css";

import {
default as Web3
} from 'web3';
import {
default as contract
} from 'truffle-contract'
import ecommerce_store_artifacts from '../../build/contracts/EcommerceStore.json'

var EcommerceStore = contract(ecommerce_store_artifacts);

const ipfsAPI = require('ipfs-api');
const ethUtil = require('ethereumjs-util');

const ipfs = ipfsAPI({
host: 'localhost',
port: '5001',
protocol: 'http'
});

window.App = {
start: function() {
var self = this;
EcommerceStore.setProvider(web3.currentProvider);
renderStore();

var reader;

$("#product-image").change(function(event) {
  console.log("选择图片");
  const file = event.target.files[0]
  reader = new window.FileReader()
  reader.readAsArrayBuffer(file)
});

$("#add-item-to-store").submit(function(event) {
  console.log("保存数据到ipfs和区块链");
  const req = $("#add-item-to-store").serialize();
  let params = JSON.parse('{"' + req.replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}');
  let decodedParams = {}
  Object.keys(params).forEach(function(v) {
    decodedParams[v] = decodeURIComponent(decodeURI(params[v]));
  });
  saveProduct(reader, decodedParams);
  event.preventDefault();
});

if ($("#product-details").length > 0) {
  console.log("window.location");
  console.log(window.location);
  console.log("Search Params = " + new URLSearchParams(window.location))
  let productId = new URLSearchParams(window.location.search).get('Id');
  console.log(productId);
  renderProductDetails(productId);
}

$("#bidding").submit(function(event) {
  $("#msg").hide();
  let amount = $("#bid-amount").val();
  let sendAmount = $("#bid-send-amount").val();
  let secretText = $("#secret-text").val();
  let sealedBid = '0x' + ethUtil.sha3(web3.toWei(amount, 'ether') + secretText).toString('hex');
  let productId = $("#product-id").val();
  console.log(sealedBid + " for " + productId);
  EcommerceStore.deployed().then(function(i) {
    i.bid(parseInt(productId), sealedBid, {
      value: web3.toWei(sendAmount),
      from: web3.eth.accounts[0],
      gas: 440000
    }).then(
      function(f) {
        $("#msg").html("Your bid has been successfully submitted!");
        $("#msg").show();
        console.log(f)
      }
    )
  });
  event.preventDefault();
});

$("#revealing").submit(function(event) {
  $("#msg").hide();
  let amount = $("#actual-amount").val();
  let secretText = $("#reveal-secret-text").val();
  let productId = $("#product-id").val();
  EcommerceStore.deployed().then(function(i) {
    i.revealBid(parseInt(productId), web3.toWei(amount).toString(), secretText, {
      from: web3.eth.accounts[0],
      gas: 440000
    }).then(
      function(f) {
        $("#msg").show();
        $("#msg").html("Your bid has been successfully revealed!");
        console.log(f)
      }
    )
  });
  event.preventDefault();
});

},
};

function renderProductDetails(productId) {
console.log("renderProductDetails");
console.log(productId);
EcommerceStore.deployed().then(function(i) {
i.getProduct(productId).then(function(p) {
console.log("getProduct");
console.log(p[4]);
let content = "";
ipfs.cat(p[4]).then((stream) => {

    console.log(stream);
    let content = Utf8ArrayToStr(stream);
    $("#product-desc").append("<div>" + content + "</div>");

  });

  $("#product-image").append("<img src='http://localhost:8080/ipfs/" + p[3] + "' width='250px' />");
  $("#product-price").html(displayPrice(p[7]));
  $("#product-name").html(p[1].name);
  $("#product-auction-end").html(displayEndHours(p[6]));
  $("#product-id").val(p[0]);
  $("#revealing, #bidding").hide();
  let currentTime = getCurrentTimeInSeconds();
  if (currentTime < p[6]) {
    $("#bidding").show();
  } else if (currentTime - (60) < p[6]) {
    $("#revealing").show();
  }
})

})
}

function Utf8ArrayToStr(array) {
var out, i, len, c;
var char2, char3;

out = "";
len = array.length;
i = 0;
while (i < len) {
c = array[i++];
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
// 0xxxxxxx
out += String.fromCharCode(c);
break;
case 12:
case 13:
// 110x xxxx 10xx xxxx
char2 = array[i++];
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
break;
case 14:
// 1110 xxxx 10xx xxxx 10xx xxxx
char2 = array[i++];
char3 = array[i++];
out += String.fromCharCode(((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) |
((char3 & 0x3F) << 0));
break;
default:
break;
}
}

return out;
}

function getCurrentTimeInSeconds() {
return Math.round(new Date() / 1000);
}

function displayPrice(amt) {
return 'Ξ' + web3.fromWei(amt, 'ether');
}

function displayEndHours(seconds) {
let current_time = getCurrentTimeInSeconds()
let remaining_seconds = seconds - current_time;

if (remaining_seconds <= 0) {
return "Auction has ended";
}

let days = Math.trunc(remaining_seconds / (24 * 60 * 60));

remaining_seconds -= days * 24 * 60 * 60
let hours = Math.trunc(remaining_seconds / (60 * 60));

remaining_seconds -= hours * 60 * 60

let minutes = Math.trunc(remaining_seconds / 60);

if (days > 0) {
return "Auction ends in " + days + " days, " + hours + ", hours, " + minutes + " minutes";
} else if (hours > 0) {
return "Auction ends in " + hours + " hours, " + minutes + " minutes ";
} else if (minutes > 0) {
return "Auction ends in " + minutes + " minutes ";
} else {
return "Auction ends in " + remaining_seconds + " seconds";
}
}

function saveProduct(reader, decodedParams) {
let imageId, descId;
saveImageOnIpfs(reader).then(function(id) {
imageId = id;
saveTextBlobOnIpfs(decodedParams["product-description"]).then(function(id) {
descId = id;
saveProductToBlockchain(decodedParams, imageId, descId);
})
})
}

function saveProductToBlockchain(params, imageId, descId) {
console.log(params);
let auctionStartTime = Date.parse(params["product-auction-start"]) / 1000;
let auctionEndTime = auctionStartTime + parseInt(params["product-auction-end"]) * 24 * 60 * 60

EcommerceStore.deployed().then(function(i) {
i.addProductToStore(params["product-name"], params["product-category"], imageId, descId, auctionStartTime,
auctionEndTime, web3.toWei(params["product-price"], 'ether'), parseInt(params["product-condition"]), {
from: web3.eth.accounts[0],
gas: 440000
}).then(function(f) {
console.log(f);
("#msg").show();("#msg").html("Your product was successfully added to your store!");
})
});
}

function saveImageOnIpfs(reader) {
return new Promise(function(resolve, reject) {
const buffer = Buffer.from(reader.result);
ipfs.add(buffer)
.then((response) => {
console.log(response)
resolve(response[0].hash);
}).catch((err) => {
console.error(err)
reject(err);
})
})
}

function saveTextBlobOnIpfs(blob) {
return new Promise(function(resolve, reject) {
const descBuffer = Buffer.from(blob, 'utf-8');
ipfs.add(descBuffer)
.then((response) => {
console.log(response)
resolve(response[0].hash);
}).catch((err) => {
console.error(err)
reject(err);
})
})
}

function renderStore() {
EcommerceStore.deployed().then(function(i) {
i.getProduct.call(1).then(function(p) {
("#product-list").append(buildProduct(p, 1)); }); i.getProduct.call(2).then(function(p) {("#product-list").append(buildProduct(p, 2));
});
i.getProduct.call(3).then(function(p) {
("#product-list").append(buildProduct(p, 3)); }); i.getProduct.call(4).then(function(p) {("#product-list").append(buildProduct(p, 4));
});
i.getProduct.call(5).then(function(p) {
("#product-list").append(buildProduct(p, 5)); }); i.getProduct.call(6).then(function(p) {("#product-list").append(buildProduct(p, 6));
});
i.getProduct.call(7).then(function(p) {
$("#product-list").append(buildProduct(p, 7));
});
});
}

function buildProduct(product, id) {
console.log("buildProduct");
console.log(id);
let node = $("<div/>");
node.addClass("col-sm-3 text-center col-margin-bottom-1");
node.append("<img src='https://ipfs.io/ipfs/" + product[3] + "' width='150px' />");
node.append("<div>" + product[1] + "</div>");
node.append("<div>" + product[2] + "</div>");
node.append("<div>" + new Date(product[5] * 1000) + "</div>");
node.append("<div>" + new Date(product[6] * 1000) + "</div>");
node.append("<div>Ether " + product[7] + "</div>");
node.append("<a href='product.html?Id=" + id + "'class='btn btn-primary'>Show</a>")
return node;
};

window.addEventListener('load', function() {

if (typeof web3 !== 'undefined') {
console.warn("Using web3 detected from external source. If you find that your accounts don't appear or you have 0 MetaCoin, ensure you've configured that source properly. If using MetaMask, see the following link. Feel free to delete this warning. :) http://truffleframework.com/tutorials/truffle-and-metamask")

window.web3 = new Web3(web3.currentProvider);

} else {
console.warn("No web3 detected. Falling back to http://localhost:8545. You should remove this fallback when you deploy live, as it's inherently insecure. Consider switching to Metamask for development. More info here: http://truffleframework.com/tutorials/truffle-and-metamask");
window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}

App.start();
});
在根目录创建种子文件seed.js
Eutil = require('ethereumjs-util');
EcommerceStore = artifacts.require("./EcommerceStore.sol");
module.exports = function(callback) {
current_time = Math.round(new Date() / 1000);
amt_1 = web3.toWei(1, 'ether');
EcommerceStore.deployed().then(function(i) {i.addProductToStore('iphone 5', 'Cell Phones & Accessories', 'QmNZWJ8dBQxWrk3YzjrHxkVpePaGajqASKCQQJLmaTVSy2', 'QmbLRFj5U6UGTy3o9Zt8jEnVDuAw2GKzvrrv3RED9wyGRk', current_time, current_time + 200, 2amt_1, 0).then(function(f) {console.log(f)})});
EcommerceStore.deployed().then(function(i) {i.addProductToStore('iphone 5s', 'Cell Phones & Accessories', 'QmNZWJ8dBQxWrk3YzjrHxkVpePaGajqASKCQQJLmaTVSy2', 'QmbLRFj5U6UGTy3o9Zt8jEnVDuAw2GKzvrrv3RED9wyGRk', current_time, current_time + 400, 3
amt_1, 1).then(function(f) {console.log(f)})});
EcommerceStore.deployed().then(function(i) {i.addProductToStore('iphone 6', 'Cell Phones & Accessories', 'QmNZWJ8dBQxWrk3YzjrHxkVpePaGajqASKCQQJLmaTVSy2', 'QmbLRFj5U6UGTy3o9Zt8jEnVDuAw2GKzvrrv3RED9wyGRk', current_time, current_time + 14, amt_1, 0).then(function(f) {console.log(f)})});
EcommerceStore.deployed().then(function(i) {i.addProductToStore('iphone 6s', 'Cell Phones & Accessories', 'QmNZWJ8dBQxWrk3YzjrHxkVpePaGajqASKCQQJLmaTVSy2', 'QmbLRFj5U6UGTy3o9Zt8jEnVDuAw2GKzvrrv3RED9wyGRk', current_time, current_time + 86400, 4amt_1, 1).then(function(f) {console.log(f)})});
EcommerceStore.deployed().then(function(i) {i.addProductToStore('iphone 7', 'Cell Phones & Accessories', 'QmNZWJ8dBQxWrk3YzjrHxkVpePaGajqASKCQQJLmaTVSy2', 'QmbLRFj5U6UGTy3o9Zt8jEnVDuAw2GKzvrrv3RED9wyGRk', current_time, current_time + 86400, 5
amt_1, 1).then(function(f) {console.log(f)})});
EcommerceStore.deployed().then(function(i) {i.addProductToStore('Jeans', 'Clothing, Shoes & Accessories', 'QmNZWJ8dBQxWrk3YzjrHxkVpePaGajqASKCQQJLmaTVSy2', 'QmbLRFj5U6UGTy3o9Zt8jEnVDuAw2GKzvrrv3RED9wyGRk', current_time, current_time + 86400 + 86400 + 86400, 5*amt_1, 1).then(function(f) {console.log(f)})});
EcommerceStore.deployed().then(function(i) {i.productIndex.call().then(function(f){console.log(f)})});
}

编译
compile
migrate
var c
c = EcommerceStore.deployed().then((i)=>{c = i});
c.productIndex();
//插入6条数据
exec seed.js
//再次查询数据
c.productIndex();
BigNumber { s: 1, e: 0, c: [ 6 ] }

前端页面
app/index.html
<!DOCTYPE html>
<html>
<head>
<title>Decentralized Ecommerce Store</title>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'>
<link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' rel='stylesheet' type='text/css'>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="./app.js"></script>
</head>
<body>
<div class="container-fluid">
<h1>Ecommerce Store</h1>
<div>Total Products: <span id="total-products"></span></div>
<a href="list-item.html" class="btn btn-primary">List Item</a>
<div class="row">
<div class="col-sm-2">
<h2>Categories</h2>
<div id="categories">
</div>
</div>
<div class="col-sm-10">
<div class="row">
<h2 class="text-center">Products To Buy</h2>
<div class="row">
<div class="row" id="product-list">
</div>
</div>
</div>
<div class="row">
<h2 class="text-center">Products In Reveal Stage</h2>
<div class="row">
<div class="row" id="product-reveal-list">
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

app/stylesheets/app.css
.col-margin-bottom-1 {
margin-bottom: 1em;
}

a {
cursor: pointer;
}

.navbar.main {
background: #5f6d7f;
color: white;
}

.navbar.main .list.nav-link {
background: #01bf86;
}

.navbar.main a {
color: white;
}

.navbar ul {
list-style: none;
}

.side-bar.col-sm-2 {
margin-left: 1em;
border-right: 1px solid #5f6d7f;
}

product-list.row {

margin-left: 0;
}

product-list .row,

product-finalize-list {

margin-top: 2em;
}

.row h2,
.categories a,

categories a {

color: #5f6d7f;
}

product-list a,

product-reveal-list a,

product-list .row > div {

color: #01bf86;
}

.container-fluid .row {
margin-left: 0px;
}

product-list img,

product-reveal-list img,

product-finalize-list img {

width: 150px;
height: 150px;
}

.container-fluid .title {
font-size: 1.2em;
font-weight: bold;
margin-top: 1em;
}

product-list .details,

.container-fluid .title,

product-finalize-list a {

color: #5f6d7f;
}

.product .details {
text-decoration: underline;
}

.time {
font-style: italic;
}

product-finalize-list .row > div{

color: #e76e6e;
}

product-details-page {

color: #5f6d7f;
background-color: #e9ebef;
}

product-details-page h4 {

margin-top: 0px;
font-size: 20px;
}

product-details-page h4.align {

margin-left: 15px;
font-style: italic;
}

product-details-page #product-price {

color: #01bf86;
}

product-details-page .bid-details,

product-details-page #product-desc {

padding: 2em;
}

product-details-page #product-image {

padding: 2em 1em;
/border: 1px solid #b2bcc8;/
border-radius: 10px;
background-color: white;
}

product-details-page h1 {

color: #5f6d7f;
}

product-details-page h2.product-name {

color: #01bf86;
margin-top: 0;
margin-bottom: 1em
/font-size: 1.5em;/
}

product-details-page #product-auction-end {

color: #e76e6e;
font-style: italic;
font-size: 17px;
}

product-details-page #product-reveal {

margin-top: 3em;
}

product-details-page button.form-submit {

background-color: #01bf86;
color: white;
}

product-details-page #product-desc {

padding: 2em;
}

npm run dev 启动项目

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容

  • "use strict";function _classCallCheck(e,t){if(!(e instanc...
    久些阅读 2,002评论 0 2
  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom阅读 2,583评论 0 3
  • 前言 最先接触编程的知识是在大学里面,大学里面学了一些基础的知识,c语言,java语言,单片机的汇编语言等;大学毕...
    oceanfive阅读 2,954评论 0 7
  • 昨晚看了《来自地球的男人》,起初是打算慢慢看,算一种减压形式,最终却是沉浸其中,难以自拔。 与其说是在质疑主人公J...
    草木吟阅读 654评论 0 0
  • 姓名:魏正君《六项精进》第270期感谢2组公司:绵阳大北农农牧科技有限公司【日精进打卡第207天】【知~学习】背诵...
    莫心莫肺阅读 194评论 0 0