React+Koa全棧開發手記

項目功能

最近在作一箇舊書交易網站,本屬於B/S體系結構的課程做業,但因爲採用了新的框架因此躍躍欲試想都記錄下來。css

實現一箇舊書交易網站,基本功能以下:html

  1. 實現用戶註冊、登陸功能,用戶註冊時須要填寫必要的信息並驗證,如用戶名、密碼要求在6字節以上,email的格式驗證,並保證用戶名和email在系統中惟一。
  2. 用戶登陸後能夠發佈要交易的書籍,須要編輯相關信息,包括書名、原價、出售價、類別和內容介紹等信息、外觀照片等,能夠經過ISBN和書名連接到外部系統(如Amazon/京東/噹噹等網站)的詳細介紹頁面。
  3. 根據用戶發佈的書籍聚合生成首頁,能夠分類檢索。
  4. 用戶能夠設置交易模式爲寄送仍是線下交易,生成訂單時錄入不一樣內容。
  5. 集成一個消息系統,買家和賣家之間能夠通訊。
  6. 提供求購模塊,用戶能夠發佈本身想要的書籍。
  7. 界面樣式須要適配PC和手機的瀏覽器。
  8. 實現一個Android或iphone客戶端軟件,功能同網站,額外支持定位功能,發佈時記錄位置,能夠根據用戶的位置匹配最近的待售書籍。消息和訂單支持推送。

技術選型

數據庫

數據庫使用MySQL進行開發,由於環境以前都已經配好了( ̄▽ ̄)"前端

後端

Image result for koa.js

通過Express和Koa比對,最終選擇Koa做爲基於Node.js的Web開發框架。Koa是一個新的web框架,由Express幕後原班人馬打造,語法上也使用了ES6新的語法(例如丟棄了回調函數而使用async解決異步調用問題),看起來十分優雅o( ̄▽ ̄)onode

前端

採用React+Semantic UI,因爲以前對React有足夠多的實踐,所以本次重點仍是放在後端開發及先後端鏈接上……mysql

開發過程

參考教程

Vue+Koa全棧開發react

Koa框架教程 - 阮一峯webpack

Koa框架搭建

初始化

  1. 命令行輸入ios

    npm init -y
    npm i koa koa-json
    npm i -D nodemon
  2. 更改package.json內容,將scripts中的內容更改成"start":"nodemon app.js"

    1560958230615

  3. 根目錄下新建app.jsgit

    const Koa = require("koa");
    const json = require("koa-json");
    const logger = require("koa-logger");
    const KoaRouter = require("koa-router");
    const parser = require("koa-bodyparser");
    
    const app = new Koa();
    const router = new KoaRouter();
    
    // Json Prettier Middleware
    app.use(json());
    app.use(parser());
    app.use(logger());
    
    // Simple Middleware Example
    // app.use(async ctx => (ctx.body = { msg: "Hello world" }));
    
    app.listen(4113, () => console.log("----------Server Started----------"));
    
    module.exports = app;
  4. 命令行輸入node app.js,瀏覽器打開localhost:3000查看返回數據

    1560958508499

sequelize鏈接數據庫

  1. 安裝包github

    npm install sequelize-auto -g
    npm install tedious -g
    npm install mysql -g
  2. 進入src目錄,輸入sequelize-auto -o "./schema" -d bookiezilla -h 127.0.0.1 -u root -p 3306 -x XXXXX -e mysql,(其中 -o 參數後面的是輸出的文件夾目錄, -d 參數後面的是數據庫名, -h 參數後面是數據庫地址, -u 參數後面是數據庫用戶名, -p 參數後面是端口號, -x 參數後面是數據庫密碼 -e 參數後面指定數據庫爲mysql)

    此時schema文件夾下會自動生成三個表的文件,例如:

    /* jshint indent: 2 */
    
    module.exports = function(sequelize, DataTypes) {
      return sequelize.define(
        "book",
        {
          BookID: {
            type: DataTypes.INTEGER(11),
            allowNull: false,
            primaryKey: true
          },
          BookName: {
            type: DataTypes.STRING(45),
            allowNull: true
          },
          BookCostPrice: {
            type: "DOUBLE",
            allowNull: true
          },
          BookSalePrice: {
            type: "DOUBLE",
            allowNull: true
          },
          BookCategory: {
            type: DataTypes.STRING(45),
            allowNull: true
          },
          BookPhoto: {
            type: DataTypes.STRING(45),
            allowNull: true
          },
          BookContent: {
            type: DataTypes.STRING(45),
            allowNull: true
          },
          BookISBN: {
            type: DataTypes.STRING(45),
            allowNull: true
          }
        },
        {
          tableName: "book"
        }
      );
    };
  3. server\src\config下新建文件database.js,用於初始化Sequelize和數據庫的鏈接。

    const Sequelize = require("sequelize");
    
    // 使用url鏈接的形式進行鏈接,注意將root: 後面的XXXX改爲本身數據庫的密碼
    const BookieZilla = new Sequelize(
      "mysql://root:XXXXX@localhost/bookiezilla",
      {
        define: {
          timestamps: false// 取消Sequelzie自動給數據表加入時間戳(createdAt以及updatedAt),不然進行增刪改查操做時可能會報錯
        }
      }
    );
    
    module.exports = {
      BookieZilla // 將BookieZilla暴露出接口方便Model調用
    };
  4. 爲方便以後根據用戶id查詢信息,可先在數據庫中隨意增長一條數據。

    1561275142511

  5. server\src\models下新建文件userModel.js,數據庫和表結構文件鏈接起來。

    const db = require("../config/database.js");
    const userModel = "../schema/user.js";// 引入user的表結構
    const BookieZilla = db.BookieZilla;// 引入數據庫
    
    const User = BookieZilla.import(userModel);// 用sequelize的import方法引入表結構,實例化了User。
    
    const getUserById = async function(id) {
      const userInfo = await User.findOne({
        where: {
          UserID: id
        }
      });
      return userInfo;
    };
    
    module.exports = {
      getUserById,
      getUserByEmail
    };
  6. server\src\controllers下新建文件userController.js,來執行這個方法,並返回結果。

    Koa 提供一個 Context 對象,表示一次對話的上下文(包括 HTTP 請求和 HTTP 回覆)。經過加工這個對象,就能夠控制返回給用戶的內容。
    const user = require("../models/userModel.js");
    
    const getUserInfo = async function(ctx) {
      const id = ctx.params.id;// 獲取url裏傳過來的參數裏的id
      const result = await user.getUserById(id);
      ctx.body = result;// 將請求的結果放到response的body裏返回
    };
    
    module.exports = {
      getUserInfo,
      vertifyUserLogin
    };
  7. server\src\routes下新建文件auth.js,用於規劃auth下的路由規則。

    const auth = require("../controllers/userController.js");
    const router = require("koa-router")();
    
    router.get("/user/:id", auth.getUserInfo);
    
    module.exports = router;
  8. 回到根目錄下的app.js,將這個路由規則「掛載」到Koa上去。

    const Koa = require("koa");
    const json = require("koa-json");
    const logger = require("koa-logger");
    const KoaRouter = require("koa-router");
    const parser = require("koa-bodyparser");
    const auth = require("./src/routes/auth.js");// 引入auth
    
    const app = new Koa();
    const router = new KoaRouter();
    
    // Json Prettier Middleware
    app.use(json());
    app.use(parser());
    app.use(logger());
    
    // Simple Middleware Example
    // app.use(async ctx => (ctx.body = { msg: "Hello world" }));
    
    // Router Middleware
    router.use("/auth", auth.routes());// 掛載到koa-router上,同時會讓全部的auth的請求路徑前面加上'/auth'的請求路徑。
    
    app.use(router.routes()).use(router.allowedMethods());// 將路由規則掛載到Koa上。
    
    app.listen(4113, () => console.log("----------Server Started----------"));
    
    module.exports = app;
  9. API Test

    1561275745455

    SUCCESS!!!

先後端數據傳遞

因爲本項目採用的是先後端分離的架構,所以須要經過json來傳遞數據,以實現登陸功能爲例來闡述實現的具體步驟。

後端驗證登陸

  1. server\src\models\userModel.js增長方法,用於經過郵箱查找用戶。

    // ...
    const getUserByEmail = async function(email) {
      const userInfo = await User.findOne({
        where: {
          UserEmail: email
        }
      });
      return userInfo;
    };
    
    module.exports = {
      getUserById,
      getUserByEmail
    };
  2. server\src\controller\userController.js增長方法,用於驗證登陸信息並將結果以json形式返回給前端。

    注意此處實際上應用了JSON-WEB-TOKEN實現無狀態請求,關於jwt的原理和實現方法請參考這篇文章這篇文章

    簡單來講,運用了JSON-WEB-TOKEN的登陸系統應該是這樣的:

    • 用戶在登陸頁輸入帳號密碼,將帳號密碼(密碼進行md5加密)發送請求給後端
    • 後端驗證一下用戶的帳號和密碼的信息,若是符合,就下發一個TOKEN返回給客戶端。若是不符合就不發送TOKEN回去,返回驗證錯誤信息。
    • 若是登陸成功,客戶端將TOKEN用某種方式存下來(SessionStorage、LocalStorage),以後要請求其餘資源的時候,在請求頭(Header)裏帶上這個TOKEN進行請求。
    • 後端收到請求信息,先驗證一下TOKEN是否有效,有效則下發請求的資源,無效則返回驗證錯誤。

使用前須要安裝相應庫:

npm i koa-jwt jsonwebtoken util -s

此外,爲保證安全性,後端數據庫的密碼不能採用明文保存,此處使用bcrypt的加密方式。

npm i bcryptjs -s
const user = require("../models/userModel.js");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcryptjs");

const getUserInfo = async function(ctx) {
  const id = ctx.params.id;
  const result = await user.getUserById(id);
  ctx.body = result;
};

const vertifyUserLogin = async function(ctx) {
  const data = ctx.request.body; // post過來的數據存在request.body裏
  const userInfo = await user.getUserByEmail(data.email);

  if (userInfo != null) { // 若是查無此用戶會返回null
    if (!bcrypt.compareSync(data.psw, userInfo.UserPsw) {
      ctx.body = {
        status: false,
        msg: "Wrong password"
      };
    } else { // 若是密碼正確
      const userToken = {
        id: userInfo.UserID,
        email: userInfo.UserEmail
      };
      const secret = "react-koa-bookiezilla"; // 指定密鑰,這是以後用來判斷token合法性的標誌
      const token = jwt.sign(userToken, secret); // 簽發token
      ctx.body = {
        status: true,
        token: token // 返回token
      };
    }
  } else {
    ctx.body = {
      status: false,
      msg: "User doesn't exist"
    };
  }
};

module.exports = {
  getUserInfo,
  vertifyUserLogin
};
  1. 更新server\src\routes\auth.js中的路由規則。

    const auth = require("../controllers/userController.js");
    const router = require("koa-router")();
    
    router.get("/user/:id", auth.getUserInfo);
    router.post("/login", auth.vertifyUserLogin);
    
    module.exports = router;

前端校驗數據併發送請求

前端主要使用了react-router進行路由跳轉,使用semantic-ui做爲UI組件庫,使用axios發送請求,Login.js代碼以下:

import React, { Component } from "react";
import {
  Button,
  Form,
  Grid,
  Header,
  Image,
  Message,
  Segment,
  Loader
} from "semantic-ui-react";
import { NavLink, withRouter } from "react-router-dom";
import axios from "axios";
import Logo from "../images/logo.png";

class Login extends Component {
  state = {
    email: "",
    psw: "",
    alert: false,
    load: false
  };

  vertifyFormat = () => {
    var pattern = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
    return pattern.test(this.state.email) && this.state.psw.length >= 6;
  };

  sendLoginRequest = () => {
    if (this.vertifyFormat()) {
      this.setState({
        alert: false,
        load: true
      });
      axios
        .post("/auth/login", {
          email: this.state.email,
          psw: this.state.psw
        })
        .then(res => {
          console.log(res);
        })
        .catch(err => {
          console.log(err);
        });
    } else {
      this.setState({
        alert: true
      });
    }
  };

  render() {
    var alert =
      this.state.alert === false ? (
        <div />
      ) : (
        <Message
          error
          header="Could you check something!"
          list={[
            "Email format must conform to the specification.",
            "Password must be at least six characters."
          ]}
        />
      );
    var load = this.state.load === false ? <div /> : <Loader />;
    return (
      <Grid
        textAlign="center"
        style={{ height: "100vh", background: "#f6f6e9" }}
        verticalAlign="middle"
      >
        <Grid.Column style={{ maxWidth: 450 }}>
          <Header as="h2" color="teal" textAlign="center">
            <Image src={Logo} />
            Log-in to your B::kzilla
          </Header>
          <Form size="large" error active>
            <Segment>
              <Form.Input
                fluid
                icon="user"
                iconPosition="left"
                placeholder="E-mail address"
                onChange={event => {
                  this.setState({
                    email: event.target.value
                  });
                }}
              />
              <Form.Input
                fluid
                icon="lock"
                iconPosition="left"
                placeholder="Password"
                type="password"
                onChange={event => {
                  this.setState({
                    psw: event.target.value
                  });
                }}
              />
              {alert}
              {load}
              <Button
                color="teal"
                fluid
                size="large"
                onClick={this.sendLoginRequest}
              >
                Login
              </Button>
            </Segment>
          </Form>
          <Message>
            New to us?
            <NavLink to="/signup">
              <a href="#"> Sign Up</a>
            </NavLink>
          </Message>
        </Grid.Column>
      </Grid>
    );
  }
}

export default withRouter(Login);

React配置代理

  1. 安裝http-proxy-middleware中間件。

    npm install http-proxy-middleware -s
  2. create-react-app初始化的項目須要eject,使基本配置暴露出來。

    npm run eject
  3. client\src下新建文件setupProxy.js,配置代理轉發信息。

    const proxy = require("http-proxy-middleware");
    
    module.exports = function(app) {
      app.use(
        proxy("/api", {
          target: "http://localhost:4113",
          changeOrigin: true
        })
      );
      app.use(
        proxy("/auth", {
          target: "http://localhost:4113",
          changeOrigin: true
        })
      );
    };
  4. client\scripts\start.js中進行配置,在const devServer = new WebpackDevServer(compiler, serverConfig);後添加語句require("../src/setupProxy")(devServer);

    1561279219148

  5. 發送請求格式以下:

    axios
      .post("/auth/login", {
        email: this.state.email,
        psw: this.state.psw
      })
      .then(res => {
        console.log(res);
      })
      .catch(err => {
        console.log(err);
      });
  6. 喜聞樂見的測試環節!

    1561279449368

    1561289529393

設計原理

數據庫

User

*UserID UserName UserPsw *UserEmail
INT VARCHAR(45) VARCHAR(45) VARCHAR(45)
CREATE TABLE `bookiezilla`.`user` (
  `UserID` INT NOT NULL,
  `UserName` VARCHAR(45) NULL,
  `UserPsw` VARCHAR(45) NULL,
  `UserEmail` VARCHAR(45) NOT NULL,
  PRIMARY KEY (`UserID`, `UserEmail`));

Book

*BookID BookName BookCostPrice BookSalePrice BookCategory BookPhoto BookContent BookISBN BookRefs
INT VARCHAR(45) DOUBLE DOUBLE VARCHAR(45) VARCHAR(45) VARCHAR(45) VARCHAR(45) VARCHAR(45)
CREATE TABLE `bookiezilla`.`book` (
  `BookID` INT NOT NULL,
  `BookName` VARCHAR(45) NULL,
  `BookCostPrice` DOUBLE NULL,
  `BookSalePrice` DOUBLE NULL,
  `BookCategory` VARCHAR(45) NULL,
  `BookPhoto` VARCHAR(45) NULL,
  `BookContent` VARCHAR(45) NULL,
  `BookISBN` VARCHAR(45) NULL,
  PRIMARY KEY (`BookID`));

Order

*OrderID *UserID *BookID TradeMethod TradeStatus TradeParty TraderID
INT INT INT VARCHAR(45) VARCHAR(45) VARCHAR(45) INT
CREATE TABLE `bookiezilla`.`order` (
  `OrderID` INT NOT NULL,
  `UserID` INT NOT NULL,
  `BookID` INT NOT NULL,
  `TradeMethod` VARCHAR(45) NULL,
  `TradeStatus` VARCHAR(45) NULL,
  `TraderID` INT NULL,
  PRIMARY KEY (`OrderID`));

前端

目錄結構

.
│  .gitignore
│  package-lock.json
│  package.json
│  README.md
│  yarn.lock
│
├─config // 基本配置文件
│  │  env.js
│  │  modules.js
│  │  paths.js
│  │  pnpTs.js
│  │  webpack.config.js
│  │  webpackDevServer.config.js
│  │
│  └─jest
│          cssTransform.js
│          fileTransform.js
│
├─public
│      favicon.ico
│      index.html
│      manifest.json
│
├─scripts // eject後生成的文件配置
│      build.js
│      start.js
│      test.js
│
└─src // 主要頁面及組件部分
    │  App.css
    │  App.js
    │  index.css
    │  index.js
    │  serviceWorker.js
    │  setupProxy.js // 設置代理轉發,解決跨域問題
    │
    ├─actions // react-redux須要定義的actions
    │      UpdateActions.js
    │
    ├─components // 頁面的組件部分
    │      BookList.jsx
    │      BookMarket.jsx
    │      FeedBack.jsx
    │      OrderInfo.jsx
    │      PublishForm.jsx
    │      SearchBar.jsx
    │      SideMenu.jsx
    │      StatisticData.jsx
    │      StepFlow.jsx
    │
    ├─images // 項目中使用的圖片資源
    │      logo.png
    │      matthew.png
    │
    ├─pages // 頁面部分
    │      Home.jsx
    │      Login.jsx
    │      Market.jsx
    │      Message.jsx
    │      Publish.jsx
    │      Signup.jsx
    │
    └─reducers // react-redux須要定義的reducers
            rootReducer.js

實現細節

React-router

項目中使用了react-router來控制路由,基本原理以下:

  1. App.js中引入路由對應的頁面或組件,並引入react-router-dom中的BrowserRouterRouteSwitch組件進行定義。

    // App.jsx
    
    import React, { Component } from "react";
    import { BrowserRouter, Route, Switch } from "react-router-dom";
    
    import SideMenu from "./components/SideMenu";
    import Login from "./pages/Login";
    import Signup from "./pages/Signup";
    import Home from "./pages/Home";
    import Market from "./pages/Market";
    import Publish from "./pages/Publish";
    import Message from "./pages/Message";
    import OrderInfo from "./components/OrderInfo";
    
    class App extends Component {
      render() {
        return (
          <BrowserRouter>
            <div className="App">
              <Switch>
                <Route exact path="/" component={Login} />
                <Route path="/signup" component={Signup} />
                <div>
                  <div>
                    <SideMenu />
                  </div>
                  <div style={{ margin: "10px 10px 10px 160px" }}>
                    {/* Only match one */}
                    <Route path="/home" component={Home} />
                    <Route path="/market" component={Market} />
                    <Route path="/publish" component={Publish} />
                    <Route path="/message" component={Message} />
                    <Route path="/books/:book_id" component={OrderInfo} />
                  </div>
                </div>
              </Switch>
            </div>
          </BrowserRouter>
        );
      }
    }
    
    export default App;
  2. 當項目頁面中須要進行頁面跳轉時,可以使用react-router-dom中的withRouter將組件包裹起來,再使用NavLink進行跳轉。

    // Login.jsx
    
    import { NavLink, withRouter } from "react-router-dom";
    
    class Login extends Component {
      .....
      sendLoginRequest = () => {
        ......
        this.props.history.push("/home");
        render(){
           ......
        }
      };
    
    export default withRouter(Login);
React-redux

Redux

Redussx

本項目中採用了react-redux進行狀態管理,redux的主要做用是容許狀態在不一樣分支的組件中進行傳遞,從而避免了使用原始方法(如this.props)致使的不一樣分支組件之間數據沒法傳遞、子組件沒法修改父組件狀態等問題。具體使用方法以下:

  1. src\reducers下新建文件rootReducer.js用於更新中心狀態樹中的信息。

    // rootReducer.js
    
    const initState = {
      id: null,
      token: null
    };
    
    const rootReducer = (state = initState, action) => {
      if (action.type === "UPDATE_ID") {
        return {
          ...state,
          id: action.id
        };
      }
      if (action.type === "UPDATE_TOKEN") {
        return {
          ...state,
          token: action.token
        };
      }
      return state;
    };
    
    export default rootReducer;
  2. src\actions中新建文件UpdateActions.js用於定義行爲。

    // UpdateActions.js
    
    export const updateId = id => {
      return {
        type: "UPDATE_ID",
        id: id
      };
    };
    
    export const updateToken = token => {
      return {
        type: "UPDATE_TOKEN",
        token: token
      };
    };
  3. src\index.js中使用react-redux中的組件對項目入口文件進行包裹,並在全局範圍內創建狀態樹。

    // index.js
    
    import React from "react";
    import ReactDOM from "react-dom";
    import "./index.css";
    import App from "./App";
    import * as serviceWorker from "./serviceWorker";
    import "semantic-ui-css/semantic.min.css";
    import { createStore } from "redux";
    import { Provider } from "react-redux";
    import rootReducer from "./reducers/rootReducer";
    
    const store = createStore(rootReducer);
    
    ReactDOM.render(
      <Provider store={store}>
        <App />,
      </Provider>,
      document.getElementById("root")
    );
    
    // If you want your app to work offline and load faster, you can change
    // unregister() to register() below. Note this comes with some pitfalls.
    // Learn more about service workers: https://bit.ly/CRA-PWA
    serviceWorker.unregister();
  4. 當須要更新狀態樹中的信息時,使用引入的action做爲函數進行更新。

    // Login.jsx
    
    import { connect } from "react-redux";
    import { updateId, updateToken } from "../actions/UpdateActions";
    
    class Login extends Component {
      ......  
      sendLoginRequest = () => {
        ......
        this.props.updateId(res.data.id);
        this.props.updateToken(res.data.token);
        ......
      };  
    }
    
    const mapStateToProps = state => {
      return {};
    };
    
    const mapDispatchToProps = dispatch => {
      return {
        updateToken: token => {
          dispatch(updateToken(token));
        },
        updateId: id => {
          dispatch(updateId(id));
        }
      };
    };
    
    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(withRouter(Login));
  5. 當須要使用狀態樹中的信息時,先調用react-redux中的connect包裹組件,再使用this.props直接調用便可。

    // PublishForm.jsx
    
    import { connect } from "react-redux";
    
    class PublishForm extends Component {
        ......
        var UserID = this.props.id;
        var UserToken = this.props.token;
        ......
    }
        
    const mapStateToProps = state => {
      return {
        id: state.id,
        token: state.token
      };
    };
    
    export default connect(mapStateToProps)(PublishForm);

後端

目錄結構

.
│  app.js
│  package-lock.json
│  package.json
│
└─src
    ├─config // 數據庫配置
    │      database.js
    │
    ├─controllers // 控制器,獲取請求數據並調用models中的方法進行處理並返回結果
    │      apiController.js
    │      msgController.js
    │      userController.js
    │
    ├─models // 實例模型,主要使用Sequelize定義的方法對數據庫進行增刪改查
    │      bookModel.js
    │      CommentModel.js
    │      orderModel.js
    │      userModel.js
    │
    ├─routes // 路由,不一樣文件對應不一樣類型的api接口,分別與受權、功能實現、信息傳遞有關
    │      api.js
    │      auth.js
    │      msg.js
    │
    └─schema // 數據庫表結構,可以使用Sequelize自動生成
            book.js
            comment.js
            order.js
            user.js

實現細節

路由掛載

當Koa後端監聽的端口接收到請求時,會根據app.js中的路由規則進行處理,咱們將不一樣類型的接口定義在不一樣文件中,再經過router.use()進行調用,避免發生接口冗亂複雜的狀況。

// app.js

const Koa = require("koa");
const json = require("koa-json");
const logger = require("koa-logger");
const KoaRouter = require("koa-router");
const parser = require("koa-bodyparser");
const auth = require("./src/routes/auth.js");
const api = require("./src/routes/api.js");
const msg = require("./src/routes/msg.js");

const app = new Koa();
const router = new KoaRouter();

// Json Prettier Middleware
app.use(json());
app.use(parser());
app.use(logger());

// Simple Middleware Example
// app.use(async ctx => (ctx.body = { msg: "Hello world" }));

// Router Middleware
router.use("/auth", auth.routes());
router.use("/msg", msg.routes());
router.use("/api", api.routes());

app.use(router.routes()).use(router.allowedMethods());

app.listen(4113, () => console.log("----------Server Started----------"));

module.exports = app;
// auth.js

const auth = require("../controllers/userController.js");
const router = require("koa-router")();

router.get("/user/:id", auth.getUserInfo);
router.post("/login", auth.vertifyUserLogin);
router.post("/signup", auth.signupNewUser);

module.exports = router;
// api.js

const api = require("../controllers/apiController.js");
const router = require("koa-router")();

router.get("/getbooks", api.getAllBooks);
router.get("/getorder/:id", api.getOrderInfo);
router.post("/searchbooks", api.searchBooks);
router.post("/publish", api.publishNewBook);
router.post("/confirmorder", api.updateOrderOfTrade);

module.exports = router;
// msg.js

const msg = require("../controllers/msgController.js");
const router = require("koa-router")();

router.get("/getcomments", msg.getAllComments);
router.post("/newcomment", msg.publishNewComment);

module.exports = router;

項目成果

登陸註冊

Bookizilla可以實現用戶註冊、用戶登陸功能,其中對用戶註冊時須要的數據作了格式處理(如驗證Email格式、保證兩次密碼輸入數據相符且不小於6字節等)。若是用戶在註冊過程當中出現錯誤,則會出現相應提示以指導用戶進行正確輸入。

Login.jsx

1561970436476

1561972776319

Signup.jsx

1561971875221

1561972744681

我的主頁

Bookiezilla的主頁呈現的是與該用戶有關的信息數據(如FAVES、VIEWS等,但因爲目先後端並未儲存相關數據因此暫用了mocks)及該用戶所發佈的全部書籍。

Home.jsx

1561972875578

書籍市場

Bookiezilla的書籍市場呈現了全部用戶發佈的全部書籍,用戶可使用上方的搜索框輸入關鍵詞(如書名、標籤 、ISBN等)。用戶還可點擊圖書下方按鈕以查看具體信息,進而決定是否達成交易,也可點擊連接在Amazon中查看書籍的詳細介紹。

Market.jsx

1561973962198

1561974279890

1561974222054

書籍發佈

Bookiezilla容許用戶發佈書籍,並設置訂單的關鍵信息(如書籍基本信息、交易模式、尋求買家或賣家等)。須要注意的是,因爲書籍發佈和書籍求購很大一部份內容是重合的,因此此處將兩者合併而且給出TradeParty選項來使用戶選擇是想要發佈書籍仍是求購書籍。

Publish.jsx

1561973513473

1561973494678

信息發佈

Bookiezilla設置了信息發佈面板,用於用戶之間的溝通交流、信息發佈等。用戶可直接發佈評論或回覆他人的評論,從而進行持續性的交流。

Message.jsx

1561974428691

https://github.com/Sylvie-Hsu...

相關文章
相關標籤/搜索