[Day30] 第三十 - 总结技能交换系统(整合Laravel以及Express的Microservices)

前言(心得)

昨天在写Code的时候一不注意时间就超过了
/images/emoticon/emoticon02.gif

其实我本来是很懒惰容易放弃的人
在之前的参赛中我也是写了几篇就放弃了
但是这次希望可以坚持完成

完成一件事情不只是技术磨练及进步
而是对一个自我认同跟坚持的展现!!

真的很感谢IT邦跟永丰的机会
在我参赛的期间
我除了学习到了金流系统的交易(从未碰过)
我也加深了对Laravel的技能
之前我也是看教学影片而已
但是在实际做系统时才会发现许多细节要调整~
(Laravel 的MVC还有资料库设计)

好啦我的心得分享差不多就到此结束了我们来完成最後的部分吧!
虽然有许多遗珠之憾
不过这个系统是我有兴趣的
我希望以後可以把这个side project完成!!

目标

  1. Laravel blade layout的用法(类似切割component)
  2. Laravel 多表查询join的应用
  3. 不同服务的沟通laravel与Express沟通
  4. 增加技能树
  5. 正式完成金流部分

今天的目标有点多!
我们来把它完成吧

我在写文章的时候也会遇到失败的地方
我希望可以把这些纪录忠实呈现
除了可以提醒自我需要加强的地方外
也想跟大家分享
工程师没有一开始就做出完美的系统
随者patch的update我们才会与生活的改变进步!!

/images/emoticon/emoticon08.gif

实作

1. blade 修改layout

先上成果给大家看吧
https://ithelp.ithome.com.tw/upload/images/20211015/201210523qnNDUTy6O.png

我们首先要先制作修改首页
在这之前我们先制作一个名为layout的样板
也就是我们上方的navbar为主要样式
其他的组件都可以套用上方列表
这样我们就不用在每页都制作导览列(这也是许多现代前端框架会用的方法)

Layout.blade.php

<head>
    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}" defer></script>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"> </script>
    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>

<div class="container">
    <ul class="nav justify-content-center">
        <li class="nav-item">
            <a class="nav-link active" href="{{ url('/') }}">
                <H2>首页</H2>
            </a>
        </li>
        <li class="nav-item">
            <a class="nav-link active" href="{{ url('/skills') }}">
                <H2>全技能列表</H2>
            </a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="{{ url('profile') }}">
                <H2>个人档案</H2>
            </a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="{{ url('/points')}}">
                <h2>评分系统</h2>
            </a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="{{ url('/trades/record')}}">
                <h2>订单编号</h2>
            </a>
        </li>
        <li class=" nav-item">
            <a class="nav-link" href="{{ url('/skills/tree')}}">
                <h2>技能树</h2>
            </a>
        </li>
    </ul>
    <div class="container">
        @yield('content')
    </div>
</div>

这边的样板代表我可以把导览列建立好作为我系统的主要样板

yeild函数就是可以给其他blade填充的页面
里面的变数就是要填聪在哪个相对应的变数里 -> 我这边叫做content

再来大家看我简单的首页吧就会比较理解我说什麽了
Home.blade.php

@extends('layout')


@section('content')
<div class="jumbotron jumbotron-fluid">
    <div class="container">
        <h1 class="display-4">技能交换平台</h1>
        <p class="lead">交换你的技能是放潜能吧!!</p>
    </div>
</div>

@endsection

@extend 是代表我要延伸哪个样板 我这边选择的是名叫layout的样板
layout是我的 Layout.blade.php的名字喔 blade.php可以不加上去
接者使用 @section 跟@endsection包起来 变数就使用contnent包进去
聪民的你应该发现了!这样的作法 我们可以用不同变数包在不同区域
比如说footer或是sidebar等等~~~

有了这样的概念後我们把其他叶面都补上这个layout搂

user/profile.blade.php

@extends('layout')

<head>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"> </script>
    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    <style>
        .box {
            display: flex;
            /* align-items: center; */
            justify-content: center;
            height: 100%;
        }
    </style>
</head>

@section('content')
<div class="box">
    <div>
        <table class="table">
            <thead class="thead-dark">
                <tr>
                    <th scope="col">id</th>
                    <th scope="col">姓名</th>
                    <th scope="col">email</th>
                    <th scope="col">会员建立时间</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>
                        {{$user->id}}
                    </td>
                    <td>
                        {{$user->name}}
                    </td>
                    <td>
                        {{$user->email}}
                    </td>
                    <td>
                        {{$user->created_at}}
                    </td>
                </tr>

            </tbody>
        </table>
    </div>
</div>
@endsection

以此类推

2. 建立Trade的 Model,Controller,Resource,Route

依照我前面的习惯
我们先建立Model跟migration吧

php artisan make:Trade -m

create_trades_table.php
接者在mirgration里面增加这些栏位

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateTradesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('trades', function (Blueprint $table) {
            $table->id();
            $table->string('target_user_id'); // 交换者的User ID
            $table->integer('skill_id'); // 对方的技能ID
            $table->integer('user_id'); // 自己的User ID
            $table->string('my_skill_id'); // 自己的技能ID
            $table->string('shopNo'); // 建立订单的编号
            $table->integer('amount'); // 价钱 (前方说的技能相减分数*1000)
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('trades');
    }
}

建立这些栏位的解释麻烦帮我看注解喔
接者我们定义route吧!

Route::resource('trades', 'TradeController');

Trade Model fillable也要修改一下喔

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Trade extends Model
{
    use HasFactory;
    protected $fillable = [
        'target_user_id',
        'user_id', 'skill_id',
        'my_skill_id', 'shopNo', 'amount'
    ];
}

3. 修改之前评分列表 我们新增可以交换的按钮

为了让大家了解我的系统流程
先让大家看图吧!

https://ithelp.ithome.com.tw/upload/images/20211015/20121052RuCm5NZIEt.png
在评分表里我们除了秀出个人对该技能的评分以外
我们也多了交易的按钮
我们来看一下接下来会跳到哪个画面喽!

https://ithelp.ithome.com.tw/upload/images/20211015/20121052daC7rNPABy.png
接者跳到这画面

说明 我现在是admin的使用者 我目标是roni的微积分作技能交换
那们作为交换我目前只有化学可以选
点击过去就可以做交换搂!!

4. point的blade.view修改

@extends('layout')


@section('content')
<div>
  <table class="table table-bordered">
    <thead>
      <tr>
        <th>ID</th>
        <th>标题</th>
        <th>使用者ID</th>
        <th>技能评分</th>
        <th>分数</th>
        <th>交换技能</th>
      </tr>
    </thead>
    <tbody>
      @foreach($skills as $skill )
      <tr>
        <td>{{$skill->id}}</td>
        <td>{{$skill->title}}</td>
        <td>{{$skill->user_id}}</td>
        <td><button type="button" class="btn btn-primary" onclick="myFunction('{{$skill->id}}','{{$skill->user_id}}')">进入评分</button></td>
        <td>{{$skill->point}}</td>
        <td><button class="btn btn-primary" onclick="goTrade('{{$skill->id}}','{{$skill->user_id}}','{{$my_id}}')">进行技能交换</button></td>
      </tr>
      @endforeach
    </tbody>
  </table>
  <!-- {{$skills}} -->
  <!-- {{$my_id}} -->
</div>


<script>
  function myFunction(skill_id, user_id) {
    var data = {
      skill_id,
      user_id
    }
    $.ajax({
        method: "POST",
        url: "/points",
        dataType: 'json',
        data
      })
      .done(function(msg) {
        console.log(msg)
      });
  }

  function goTrade(skill_id, user_id, my_id) {
    var data = {
      skill_id,
      user_id: my_id,
      target_user_id: user_id
    }
    window.location.replace(`/trades?skill_id=${data.skill_id}&user_id=${data.user_id}&target_user_id=${data.target_user_id}`)
  }
</script>

@endsection

我们在评分上面goTrade function上动手角
把一些使用者参数跟技能ID
对方ID带过去
那麽为了要带到
交换的选择页面我们参数可以用query string的方式导向别的页面

query string就是网址後面? 接变数然後用&接起来
像是youtube影片 可以接?t=10m20s 这些参数可以让页面读取到指定的参数做api请求
或是js操作

5. 我们先改controller

在Controller/TradeController.php里的index function

    public function index(Request $request)
    {
        //
        $data = $request->all();
        $user = Auth::user();
        $skill_id = $request->query('skill_id');
        $user_id = $request->query('user_id');
        $target_user_id = $request->query('target_user_id');
        $myskills = Skill::where('user_id', $user->id)->get();
        // dd($myskills);
        return view('trade.index', [
            'skill_id' => $skill_id,
            'user_id' => $user_id,
            'target_user_id' => $target_user_id,
            'myskills' => $myskills
        ]);
    }

我们可以用Auth::user先把登入者也就是自己抓出来
然後把query()抓取query string的参数
然後我们要把自己的技能抓出来
在丢回去

自己的技能丢回去是要给作为交换的技能
才会有我们看到的这个画面
https://ithelp.ithome.com.tw/upload/images/20211015/20121052wecjAr4zSj.png

对了这边有小提醒!我们在开发时php可以用dd()把变数印出来
前端也可以找个位置用{{}}花括号印在旁边来参考

接者我们有了这些参数後
选择化学跟roni的微积分进行技能交换吧

6.交换的Controller API

在看code之前我要提醒大家一件事
我们除了存进资料库外还有一些是要做
比如说我们要新建一笔订单
那麽我们要先跟nodejs的服务沟通
在跟永丰API 沟通完建立完成後
如果有成功我们再把这笔交易存进资料库里

为了沟通我们先使用这个Class吧

use Illuminate\Support\Facades\Http;

Controller/TradeController.php

    public function store(Request $request)
    {

        $targetPoint = Point::find($request->point_id);
        $myskillsPoint = Point::query()->where('skill_id', $request->my_skill_id)->get()->first();
        $ScoreGap = abs($myskillsPoint->point - $targetPoint['point']);
// 这边是要 计算分数差
        $datenow = date('YmdHis');
        $OrderNo = "A$datenow" . "1";
        $Amount = $ScoreGap * 1000;
// 这边data是php内建函数 可以抓现在时间 我把这当作订单编号
// 最後前面补上A字串最後加上1防止订单错误 (永丰尾数是9会模拟交易失败)
// . 点 是php在接字串的方法 跟javascript  + 一样
// amount 的分数差 乘上 一千代表要交易的钱 

        $result = Http::post('http://localhost:8888/createOrder', [
            'OrderNo' => $OrderNo,
            'Amount' => $Amount * 100,
        ]);
  // 对了这边补上100是因为永丰会把尾数去掉两个0
 // 我们这边把 可以在laravel做post
 // 这个是我们nodejs的建立订单的api
 
 // 最後我们简单判断如果result有回传version == 1.0.0代表我们有建立订单成功
 // 接者再把订单存进资料库的动作
        if ($result['Version'] == "1.0.0") {
            $trade = Trade::create([
                'target_user_id' => $targetPoint['user_id'],
                'user_id' => $myskillsPoint->user_id,
                'skill_id' => $targetPoint['skill_id'],
                'my_skill_id' => $myskillsPoint->skill_id, 'shopNo' => $OrderNo, 'amount' => $Amount
            ]);
            return response(['trade' => $trade, 'nonce' =>  $result['Nonce'], 'msg' => $result['Message']]);
            // return response($response);
        }
        // dd($response);
        return "交易失败";
    }

补充
https://ithelp.ithome.com.tw/upload/images/20211015/20121052BrblIdKrQx.png
我们可以观察这个表
透过这两个分数表的技能分数相减
可以得到2喔
(roni 的微积分 跟admin的化学)

好啦我们来看结果吧!!

https://ithelp.ithome.com.tw/upload/images/20211015/20121052iGgBdIQxbZ.png

画面有点长XD
但是为了表现有成功
把network教出来给大家看

小提醒: Chrome开发工具打开network可以帮助我们看APi 是否有叫成功喔!!

接者使用者就可以进到付款画面喽
https://ithelp.ithome.com.tw/upload/images/20211015/20121052AJzZwOL5mZ.png

7. 接者我们来做本人所有交易的纪录吧

我们先新建一个纪录个人所有交易的路由

Route::get('trades/record', 'TradeController@record');

接者去调整controller

    public function record(Request $request)
    {
        if (!Auth::check()) {
            return view('user.login');
        }
        $user = Auth::user();

        $myresult = DB::table('trades')
            ->join('users', 'trades.target_user_id', '=', 'users.id')
            ->join('skills', 'skills.id', '=', 'trades.skill_id')
            ->where('trades.user_id', $user->id)->select('trades')
            ->select(
                'users.name as target_user_name',
                'skills.title as tatget_skill_title',
                'ShopNo',
                'amount',
                'trades.created_at'
            )
            ->get();
        // $target_user_id = $myresult['target_user_id'];

        // $target_user = User::find($target_user_id);

        return view('trade.record', ['result' => $myresult]);
    }

这边用query builder的功能把sql串在一起
我们要把对方使用者名称跟技能做联表查询

回传的名称可以用as就可以变更名称
最後再把订单编号、价钱、订单日期回传回来
我们可以先用dd来看回传资料
https://ithelp.ithome.com.tw/upload/images/20211015/20121052fxh6aBoLg7.png

给大家看对照比较好懂

接者我们就把index画面补上吧

@extends('layout')


@section('content')
<div>
  <table class="table table-bordered">
    <thead>
      <tr>
        <th>交易对象</th>
        <th>交易技能名称</th>
        <th>订单编号</th>
        <th>价钱</th>
        <th>交易时间</th>
      </tr>
    </thead>
    <tbody>
      @foreach($result as $res)
      <tr>
        <td>{{$res->target_user_name}}</td>
        <td>{{$res->tatget_skill_title}}</td>
        <td>{{$res->ShopNo}}</td>
        <td>{{$res->amount}}</td>
        <td>{{$res->created_at}}</td>
      </tr>
      @endforeach
    </tbody>
  </table>
</div>

@endsection

接者就是成果拉
https://ithelp.ithome.com.tw/upload/images/20211015/20121052f7hwYIVDxJ.png

8.遗珠之憾 技能树

接者让大家看一下
我本来是希望可以把自己有的技能以技能树呈现出来给跟别人交换
但是案於时间关系没有把它串完
可以让大家看一下用d3js做的图
https://ithelp.ithome.com.tw/upload/images/20211015/20121052dfZ3ALCwxE.png

使用d3js可以把既能呈现成这种形状并且无限延伸

总结

好啦
今天总结突然爆了一堆程序码
相信大家也看得头昏眼花了
先给大家程序码联结有兴趣的可以玩看看喔
Laravel 的程序码

Nodejs Microservices永丰API微服务

这边有一些地方可以跟大家分享
今天你得主服务可能是laravel可能是spring
但是其他小的服务比如说machine用python我们使用flask接
或是使用nodejs来串

那这些小型的微服务好处是把耦合性分离
不会因为一个service坏了整个系统
也很好把逻辑分开
但缺点是需要有人去维护处理
还有沟通之间的资安风险喔!!

最後如果要看每一天的篇章可以回到首篇来参考喔感谢大家!!

感谢这几天有follow我的观众们!

看完我整包专案
应该可以对以下概念有所了解

我把一些重点篇章整理出来可以
由下方参考找到篇章喔
/images/emoticon/emoticon08.gif

  1. laravel 专案建置流程(先建立migration,model,再来定义路由跟controller最後呈现的画面) 详细说明可以看我前面说明喔 第八章的laravel建置习惯
  2. php artisan 指令(这边有快速建立model跟migration还有Controller --resource对应route的应用)
  3. 资料库的设计(纪录id 还有laravel惯例用法可以操作相当轻松)
  4. 一对一级一对多的设计 第十六章 技能skill服务
  5. Blade的应用 我们可以使用laravel blade快速建立前端操作起来也相当便利
  6. Nodejs (Express服务建置) 可以看我第一篇章到第五篇章的教学
  7. Postman的操作 测试微服务的好工具
  8. aes加密的方法 透过nodejs的方法制作永丰需求的签章及加密过程
  9. passport套件 虽然不只有laravel才有 但是我这边说明如何使用passport做验证动作 可以参考 十一到十三 第十一篇章 安装passport
  10. JWT 解释了JWT的用途 第十三章JWT 应用在登入上
  11. User Story概念 我认为对工程师分析软件架构来说相当重要是订一一个软件架构的好方法第十章 user story
  12. Bootstrap套入laravel使用 可以参考19,20章 第十九篇章 开双B blade跟 bootstrap
  13. Ajax应用 对网页工程师而言 这项技术是不可或缺的沟通桥梁不管是前端对後端,还是後端对其他服务都是很重要的 详细可以参考 第21,22篇章喔 使用Ajax来做登入API界接
  14. query build 我觉得好的query可以增加效能跟减少跟资料库要的loading 第二十四章 query builder应用
  15. 整合工作 最後就是前後端、资料库、路由、controller的整并工作这些可以参考我最後的五章喔

最後真的很感谢大家观看
最後一篇章1万5千多字的超长文XD
有兴趣陪我一起技术可以透过我github找我信箱写信喔!

如果喜欢我的分享可以帮我按赞喔XD


<<:  Angular 深入浅出三十天:表单与测试 Day30 - 表单原理

>>:  Vaadin 工具 / 後记 - day30

Day12:今天来聊一下Parrot Security的OpenVAS

Vulnerability assessment tools用於保护组织的系统或网络 我们可以使用这...

Day20-Props

前言 昨日我们介绍函式组件如何使用,并且学会了JSX灵活的操作。 今天我们来学习很重要的传递资料Pr...

Day21 jQuery 基本教学(一)

JQuery是由JavaScript所开发的开源函式库,主要有以下功能大受好评而列入主流。 被简化的...

第8天~

上偏加入字串空的 String all =""; 这里多了餐选的,饮料选的,全部...

前端工程日记 25日 Flex 并排选单

附codepen网址: https://codepen.io/pwbzvqja/pen/GRWNV...