Luffy--------------(鍥?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luffy--------------(鍥?相关的知识,希望对你有一定的参考价值。

鏍囩锛?a href='http://www.mamicode.com/so/1/elf' title='elf'>elf   expand   tom   sys   between   hold   ble   鐩稿悓   dex   

涓€.棰戠巼闄愬埗鐭俊鎺ュ彛

settings/dev.py:

# drf閰嶇疆
REST_FRAMEWORK = {
    # Throttling
    鈥?/span>DEFAULT_THROTTLE_RATES鈥?/span>: {
        鈥?/span>user鈥?/span>: None,
        鈥?/span>anon鈥?/span>: None,
        鈥?/span>sms鈥?/span>: 鈥?/span>1/min鈥?/span>
    },
}

apps/user/throttles.py:

from rest_framework.throttling import SimpleRateThrottle

class SMSRateThrottle(SimpleRateThrottle):
    scope = 鈥?/span>sms鈥?/span>
    def get_cache_key(self, request, view):
        # 閽堝鎵嬫満鍙疯繘琛岄檺鍒?/span>
        mobile = request.data.get(鈥?/span>mobile鈥?/span>) or request.query_params.get(鈥?/span>mobile鈥?/span>)
        if not mobile:
            return None
        return self.cache_format % {
            鈥?/span>scope鈥?/span>: self.scope,
            鈥?/span>ident鈥?/span>: mobile
        }

user/views.py:

from libs.txm import get_code, send_sms
from django.core.cache import cache
from .throttles import SMSRateThrottle
from . import models, serializers
from settings.const import SMS_CODE_EXC

class SMSAPIView(APIView):
    throttle_classes = [SMSRateThrottle]
    def post(self, request, *args, **kwargs):
        # 鎷垮埌鍓嶇鐨勬墜鏈哄彿
        mobile = request.data.get(鈥?/span>mobile鈥?/span>)
        # 瀹屾垚鎵嬫満鍙风殑鏍¢獙锛堟槸鍚﹀悎娉曪級
        if not mobile or not re.match(r鈥?/span>^1[3-9][0-9]{9}$鈥?/span>, mobile):
            return APIResponse(1, 鈥?/span>鎵嬫満鍙锋湁璇?/span>鈥?/span>)
        # 浜х敓楠岃瘉鐮?/span>
        code = get_code()
        # 閫氱煡绗笁鏂瑰彂閫佺煭淇?/span>
        result = send_sms(mobile, code, SMS_CODE_EXC // 60)

        # 澶辫触锛氬搷搴斿墠鍙板彂閫佸け璐?/span>
        if not result:
            return APIResponse(1, 鈥?/span>鐭俊鍙戦€佸け璐?/span>鈥?/span>)

        # 鎴愬姛锛氱紦瀛橀獙璇佺爜(鏍¢獙澶囩敤)锛屽搷搴斿墠鍙板彂閫佹垚鍔?/span>
        cache.set(鈥?/span>%s_code鈥?/span> % mobile, code, SMS_CODE_EXC)

        # print(cache.get(鈥?s_code鈥?% mobile))
        return APIResponse(0, 鈥?/span>鐭俊鍙戦€佹垚鍔?/span>鈥?/span>)

 浜?鎵嬫満鍙烽獙璇佹帴鍙?/strong>

鍓嶅彴鎵嬫満鍙锋牎楠宺egister.vue:

 methods: {
            checkMobile() {
                if (this.mobile.length < 1) {
                    return false;
                }

                // 鎵嬫満鍙风爜鏍煎紡鏄惁姝g‘
                if (!/^1[3-9]d{9}$/.test(this.mobile)) {
                    this.$message({
                        message: "瀵逛笉璧凤紒鎵嬫満鍙风爜鏍煎紡鏈夎锛?/span>"
                    });
                    return false;
                }

                // 楠岃瘉鎵嬫満鍙风爜鏄惁宸茬粡娉ㄥ唽浜?
                this.$axios({
                    url: this.$settings.base_url + 鈥?/span>/user/mobile/鈥?/span>,
                    method: 鈥?/span>get鈥?/span>,
                    params: {
                        mobile: this.mobile
                    }
                }).then(response => {
                    let data = response.data;
                    // window.console.log(data);
                    if (data.status != 0) {
                        this.$message({
                            message: "瀵逛笉璧凤紒鎵嬫満鍙风爜宸茬粡琚敞鍐岋紒",
                            duration: 1000
                        });
                        return false;
                    } else {
                        this.$message({
                            message: "鏈熷緟鎮ㄥ姞鍏ユ垜浠紒",
                            duration: 1000,
                            // onClose: () => {
                            //     console.log(鈥?/span>淇℃伅妗嗗叧闂簡鈥?/span>)
                            // }
                        });
                    }
                }).catch(error => {
                    let data = error.response.data;
                    this.$message({
                        message: data.message
                    })
                })

            },
            ...

鍚庡彴鎵嬫満鍙锋牎楠?

          user/urls

from django.urls import path

from . import views
urlpatterns = [
    path(鈥?/span>sms/鈥?/span>, views.SMSAPIView.as_view()),
    path(鈥榤obile/鈥?/span>, views.MobileAPIView.as_view()),
]

        user/views.py

class MobileAPIView(APIView):
    def get(self, request, *args, **kwargs):
        # 鑾峰彇鎵嬫満鍙?/span>
        mobile = request.query_params.get(鈥?/span>mobile鈥?/span>)
        if not mobile:
            return APIResponse(1, 鈥?/span>鎵嬫満鍙峰繀椤绘彁渚?/span>鈥?/span>)
        # 鏍¢獙鎵嬫満鍙锋槸鍚﹀悎娉?/span>
        if not re.match(r鈥?/span>^1[3-9][0-9]{9}$鈥?/span>, mobile):
            return APIResponse(1, 鈥?/span>鎵嬫満鍙锋湁璇?/span>鈥?/span>)
        # 鏍¢獙鎵嬫満鍙锋槸鍚﹀瓨鍦?/span>
        try:
            models.User.objects.get(mobile=mobile)
            return APIResponse(1, 鈥?/span>鎵嬫満鍙峰凡娉ㄥ唽鈥?/span>)
        except:
            return APIResponse(0, 鈥?/span>鎵嬫満鍙锋湭娉ㄥ唽鈥?/span>)

涓?娉ㄥ唽鎺ュ彛瀹炵幇(鐢ㄦ埛鍚嶆槸鎵嬫満鍙?

    settings/const.py:

# 楠岃瘉鐮佽繃鏈熸椂闂达紙s锛?鏇挎崲杩囨湡鏃堕棿
SMS_CODE_EXC = 300000

    鍓嶅彴鍙戦€佺煭淇′慨璁egister.vue:

send_sms() {
                // 鍙戦€佺煭淇?
                if (!/^1[3-9]d{9}$/.test(this.mobile)) {
                    this.$message({
                        message: "瀵逛笉璧凤紒鎵嬫満鍙风爜鏍煎紡鏈夎锛?/span>"
                    });
                    return false;
                }

                // 鍒ゆ柇鏄惁鍦?0s鍐呭彂閫佽繃鐭俊
                if (this.is_send) {
                    this.$message({
                        message: "瀵逛笉璧?涓嶈兘棰戠箒鍙戦€佺煭淇¢獙璇侊紒"
                    });
                    return false;
                }

                // 璇锋眰鍙戦€佺煭淇?
                this.$axios({
                    url: this.$settings.base_url + 鈥?/span>/user/sms/鈥?/span>,
                    method: 鈥?/span>post鈥?/span>,
                    data: {
                        mobile: this.mobile
                    }
                }).then(response => {
                    this.$message({
                        message: response.data.msg,
                    });
                    // 淇敼鐭俊鐨勫彂閫佺姸鎬?
                    this.is_send = true;

                    // 璁剧疆闂撮殧鏃堕棿60s
                    let sms_interval_time = 60;
                    // 璁剧疆鐭俊鍙戦€侀棿闅斿€掕鏃?.60s鍚庢妸is_send鏀规垚false
                    let timer = setInterval(() => {
                        if (sms_interval_time <= 1) {
                            clearInterval(timer);
                            this.sms_interval_tips = "鑾峰彇楠岃瘉鐮?/span>";
                            this.is_send = false; // 閲嶆柊鍥炲鐐瑰嚮鍙戦€佸姛鑳界殑鏉′欢
                        } else {
                            sms_interval_time -= 1;
                            this.sms_interval_tips = `${sms_interval_time}绉掑悗鍐嶆鑾峰彇`;
                        }
                    }, 1000);

                }).catch(error => {
                    this.$message({
                        message: error.response.data.result,
                    })
                });

            },

   鍚庡彴娉ㄥ唽鎺ュ彛:

      user/ url.py

from django.urls import path

from . import views
urlpatterns = [
    path(鈥?/span>sms/鈥?/span>, views.SMSAPIView.as_view()),
    path(鈥?/span>mobile/鈥?/span>, views.MobileAPIView.as_view()),
    path(鈥榬egister/鈥?/span>, views.RegisterAPIView.as_view()),
    path(鈥?/span>login/鈥?/span>, views.LoginAPIView.as_view()),
    path(鈥?/span>login/mobile/鈥?/span>, views.LoginMobileAPIView.as_view()),
]

     views.py

class RegisterAPIView(APIView):
    def post(self, request, *args, **kwargs):
        # 灏嗚姹傛暟鎹?鎵嬫満瀵嗙爜楠岃瘉鐮?=> 璐﹀彿鎵嬫満瀵嗙爜楠岃瘉鐮?/span>
        request_data = request.data
        request_data[鈥?/span>username鈥?/span>] = request_data.get(鈥?/span>mobile鈥?/span>)
        # 鏍¢獙
        user_ser = serializers.UserModelSerializer(data=request_data)
        if user_ser.is_valid():
            user_ser.save()
            return APIResponse(0, 鈥?/span>娉ㄥ唽鎴愬姛鈥?/span>, results=user_ser.data)
        else:
            return APIResponse(1, 鈥?/span>娉ㄥ唽澶辫触鈥?/span>, results=user_ser.errors)

      搴忓垪鍖栫被user/serializers.py

from rest_framework import serializers
from . import models
from django.core.cache import cache
import re
class UserModelSerializer(serializers.ModelSerializer):
    code = serializers.CharField(write_only=True, min_length=4, max_length=4)
    class Meta:
        model = models.User
        fields = (鈥?/span>username鈥?/span>, 鈥?/span>password鈥?/span>, 鈥?/span>mobile鈥?/span>, 鈥?/span>code鈥?/span>)
        extra_kwargs = {
            鈥?/span>password鈥?/span>: {
                鈥?/span>write_only鈥?/span>: True
            }
        }

    def validate_mobile(self, value):
        if not re.match(r鈥?/span>^1[3-9][0-9]{9}$鈥?/span>, value):
            raise serializers.ValidationError(鈥?/span>鎵嬫満鍙锋湁璇?/span>鈥?/span>)
        return value

    def validate(self, attrs):
        code = attrs.pop(鈥?/span>code鈥?/span>)
        mobile = attrs.get(鈥?/span>mobile鈥?/span>)
        old_code = cache.get(鈥?/span>%s_code鈥?/span> % mobile)
        if code != old_code:
            raise serializers.ValidationError({鈥?/span>楠岃瘉鐮?/span>鈥?/span>: 鈥?/span>楠岃瘉鐮佹湁璇?/span>鈥?/span>})
        return attrs

    def create(self, validated_data):
        return models.User.objects.create_user(**validated_data)
#閲嶅啓create鏂规硶,瀵嗙爜涓哄瘑鏂?/span>

娉?鎵嬫満鍙锋敞鍐屼笉鑳界浉鍚?models.py涓皢mobile瀛楁鍔犱竴涓猽nique=true

娉ㄥ唽鍓嶅彴register.vue:

registerMobile() {
                // 娉ㄥ唽淇℃伅鎻愪氦
                if (!/^1[3-9]d{9}$/.test(this.mobile)) {
                    this.$message({
                        message: "瀵逛笉璧凤紒鎵嬫満鍙风爜鏍煎紡鏈夎锛?/span>"
                    });
                    return false;
                }

                if (this.sms.length < 1) {
                    this.$message({
                        message: "鐭俊楠岃瘉鐮佷笉鑳戒负绌猴紒"
                    });
                    return false;
                }

                if (this.password.length < 6 || this.password.length > 16) {
                    this.$message({
                        message: "瀵逛笉璧凤紝瀵嗙爜闀垮害蹇呴』鍦?-16涓瓧绗︿箣闂达紒"
                    });
                    return false;
                }

                this.$axios({
                    url: this.$settings.base_url + 鈥?/span>/user/register/鈥?/span>,
                    method: 鈥?/span>post鈥?/span>,
                    data: {
                        mobile: this.mobile,
                        password: this.password,
                        code: this.sms
                    }
                }).then(response => {
                    let status = response.data.status;
                    let msg = response.data.msg;
                    if (status == 0) {
                        this.$message({
                            message: msg,
                            duration: 1000,
                            onClose: () => {
                                this.$router.push(鈥?/span>/login鈥?/span>)
                            }
                        });
                    } else {
                        // 瀹為檯鏍规嵁閿欒淇℃伅锛屾彁绀洪敊璇殑鍏蜂綋杈撳叆妗?
                        this.mobile = 鈥樷€?/span>;
                        this.password = 鈥樷€?/span>;
                        this.sms = 鈥樷€?/span>;
                        this.$message({
                            message: msg,
                            duration: 1000,
                        });
                    }
                }).catch(error => {
                    this.$message({
                        message: error.response.data.result
                    });
                })

            }
        },

鍥?鐧诲綍鎺ュ彛鐨勫疄鐜?/strong>

涓ょ鐧诲綍璺敱:

from django.urls import path

from . import views
urlpatterns = [
    path(鈥?/span>sms/鈥?/span>, views.SMSAPIView.as_view()),
    path(鈥?/span>mobile/鈥?/span>, views.MobileAPIView.as_view()),
    path(鈥?/span>register/鈥?/span>, views.RegisterAPIView.as_view()),
    path(鈥榣ogin/鈥? views.LoginAPIView.as_view()),
    path(鈥榣ogin/mobile/鈥?/span>, views.LoginMobileAPIView.as_view()),
]

user/views

棣栧厛瀹夎jwt:  pip install djangorestframework-jwt

from rest_framework_jwt.serializers import jwt_encode_handler, jwt_payload_handler
class LoginAPIView(APIView):
    authentication_classes = ()
    permission_classes = ()
    def post(self, request, *args, **kwargs):
        # username鍙兘鎼哄甫鐨勪笉姝㈡槸鐢ㄦ埛鍚嶏紝鍙兘杩樻槸鐢ㄦ埛鐨勫叾瀹冨敮涓€鏍囪瘑 鎵嬫満鍙?閭
        username = request.data.get(鈥?/span>username鈥?/span>)
        password = request.data.get(鈥?/span>password鈥?/span>)

        if not (username and password):
            return APIResponse(1, 鈥?/span>璐﹀彿瀵嗙爜蹇呴』鈥?/span>)

        # 濡傛灉username鍖归厤涓婃墜鏈哄彿姝e垯 => 鍙兘鏄墜鏈虹櫥褰?/span>
        if re.match(r鈥?/span>^1[3-9][0-9]{9}$鈥?/span>, username):
            try:
                # 鎵嬪姩閫氳繃 user 绛惧彂 jwt-token
                user = models.User.objects.get(mobile=username)
            except:
                return APIResponse(1, 鈥?/span>璇ユ墜鏈烘湭娉ㄥ唽鈥?/span>)

        # 閭鐧诲綍 绛?/span>

        # 璐﹀彿鐧诲綍
        else:
            try:
                # 鎵嬪姩閫氳繃 user 绛惧彂 jwt-token
                user = models.User.objects.get(username=username)
            except:
                return APIResponse(1, 鈥?/span>璇ヨ处鍙锋湭娉ㄥ唽鈥?/span>)

        # 鑾峰緱鐢ㄦ埛鍚庯紝鏍¢獙瀵嗙爜骞剁鍙憈oken
        if not user.check_password(password):
            return APIResponse(1, 鈥?/span>瀵嗙爜閿欒鈥?/span>)
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        return APIResponse(0, 鈥?/span>ok鈥?/span>, results={
            鈥?/span>username鈥?/span>: user.username,
            鈥?/span>mobile鈥?/span>: user.mobile,
            鈥?/span>token鈥?/span>: token
        })

class LoginMobileAPIView(APIView):
    authentication_classes = ()
    permission_classes = ()
    def post(self, request, *args, **kwargs):
        # 鎵嬫満鍙疯幏鍙栧苟鏍¢獙
        mobile = request.data.get(鈥?/span>mobile鈥?/span>)
        code = request.data.get(鈥?/span>code鈥?/span>)
        if not (mobile and code):
            return APIResponse(1, 鈥?/span>鎵嬫満鍙蜂笌楠岃瘉鐮佸繀椤?/span>鈥?/span>)
        # 楠岃瘉鐮佽幏鍙栧苟鏍¢獙
        old_code = cache.get(鈥?/span>%s_code鈥?/span> % mobile)
        # 楠岃瘉鐮佸疄闄呭紑鍙戝彧鍙互浣跨敤涓€娆?/span>
        # cache.set(鈥?s_code鈥?% mobile, 鈥?000鈥? 1)
        if code != old_code:
            return APIResponse(1, 鈥?/span>楠岃瘉鐮侀敊璇?/span>鈥?/span>)
        # 閫氳繃鎵嬫満鍙疯幏鍙栫敤鎴?/span>
        try:
            user = models.User.objects.get(mobile=mobile)
        except:
            return APIResponse(1, 鈥?/span>璇ヨ处鍙锋湭娉ㄥ唽鈥?/span>)

        # 绛惧彂token
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        return APIResponse(0, 鈥?/span>ok鈥?/span>, results={
            鈥?/span>username鈥?/span>: user.username,
            鈥?/span>mobile鈥?/span>: user.mobile,
            鈥?/span>token鈥?/span>: token
        })

鐧诲綍鍓嶅彴login.vue:

<template>
    <div class="login box">
        <img src="@/assets/img/Loginbg.jpg" alt="">
        <div class="login">
            <div class="login-title">
                <img src="@/assets/img/Logotitle.png" alt="">
                <p>甯姪鏈夊織鍚戠殑骞磋交浜洪€氳繃鍔姏瀛︿範鑾峰緱浣撻潰鐨勫伐浣滃拰鐢熸椿!</p>
            </div>
            <div class="login_box">
                <div class="title">
                    <span :class="{active: a0}" @click="changeLogin(0)">瀵嗙爜鐧诲綍</span>
                    <span :class="{active: a1}" @click="changeLogin(1)">鐭俊鐧诲綍</span>
                </div>
                <div class="inp" v-if="login_type==0">
                    <input v-model="username" type="text" placeholder="鐢ㄦ埛鍚?/ 鎵嬫満鍙风爜" class="user">
                    <input v-model="password" type="password" name="" class="pwd" placeholder="瀵嗙爜">
                    <div id="geetest1"></div>
                    <div class="rember">
                        <p>
                            <input id="checkbox" type="checkbox" class="no" v-model="remember"/>
                            <span>璁颁綇瀵嗙爜</span>
                        </p>
                        <p>蹇樿瀵嗙爜</p>
                    </div>
                    <button class="login_btn" @click="loginAction">鐧诲綍</button>
                    <p class="go_login">娌℃湁璐﹀彿
                        <router-link to="/register">绔嬪嵆娉ㄥ唽</router-link>
                    </p>
                </div>
                <div class="inp" v-show="login_type==1">
                    <input v-model="mobile" type="text" placeholder="鎵嬫満鍙风爜" class="user">
                    <div class="sms">
                        <input v-model="sms" type="text" placeholder="杈撳叆楠岃瘉鐮?/span>" class="user">
                        <span class="sms_btn" @click="send_sms">{{sms_interval_tips}}</span>
                    </div>
                    <button class="login_btn" @click="loginMobile">鐧诲綍</button>
                    <p class="go_login">娌℃湁璐﹀彿
                        <router-link to="/register">绔嬪嵆娉ㄥ唽</router-link>
                    </p>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        name: 鈥?/span>Login鈥?/span>,
        data() {
            return {
                a0: 1,
                a1: 0,
                login_type: 0,
                username: "",
                password: "",
                remember: false,
                mobile: "",
                sms: "",
                is_send: false,  // 鏄惁鍦?0s鍐呭彂閫佷簡鐭俊
                sms_interval_tips: "鑾峰彇楠岃瘉鐮?/span>",
            }
        },
        created() {
            // 鎷︽埅宸茬櫥褰曟儏鍐?/span>
            let token = this.$cookies.get(鈥?/span>token鈥?/span>);
            if (token) {
                this.$message({
                    message: 鈥?/span>鏃犻渶閲嶅鐧诲綍鈥?/span>,
                    duration: 1000,
                    onClose: () => {
                        this.$router.go(-1)
                    }
                })
            }
        },
        methods: {
            changeLogin(i) {
                this.login_type = i;
                if (i) {
                    this.a0 = 0;
                    this.a1 = 1;
                } else {
                    this.a0 = 1;
                    this.a1 = 0;
                }
            },

            loginAction() {
                if (!this.username || !this.password) {
                    return
                }
                this.$axios({
                    url: this.$settings.base_url + 鈥?/span>/user/login/鈥?/span>,
                    method: 鈥?/span>post鈥?/span>,
                    data: {
                        鈥?/span>username鈥?/span>: this.username,
                        鈥?/span>password鈥?/span>: this.password
                    }
                }).then((response) => {
                    // 鍒ゆ柇鐢ㄦ埛鏄惁瑕佽浣忓瘑鐮?
                    if (this.remember) {  // 璁颁綇瀵嗙爜,姘稿瓨
                        sessionStorage.clear();
                        localStorage.token = response.data.results.token;
                        localStorage.username = response.data.results.username;
                        localStorage.mobile = response.data.results.mobile;
                    } else { /// 娌¤浣忓瘑鐮?鏆傚瓨
                        localStorage.clear();
                        sessionStorage.token = response.data.results.token;
                        sessionStorage.username = response.data.results.username;
                        sessionStorage.mobile = response.data.results.mobile;
                    }

                    if (response.data.status == 0) {
                        let token = response.data.results.token;
                        this.$cookies.set(鈥?/span>token鈥?/span>, token, 24 * 60 * 60);
                        let username = response.data.results.username;
                        this.$cookies.set(鈥?/span>username鈥?/span>, username, 24 * 60 * 60);

                        this.$alert("娆㈣繋鍥炴潵锛?/span>", "鐧诲綍鎴愬姛锛?/span>", {
                            confirmButtonText: 鈥?/span>纭畾鈥?/span>,
                            callback: () => {
                                // 鐧诲綍鎴愬姛鍘讳富椤?
                                this.$router.push("/");
                            }
                        })
                    } else {
                        this.username = 鈥樷€?/span>;
                        this.password = 鈥樷€?/span>;
                    }
                }).catch(() => {
                    this.$alert("妫€鏌ヨ处鍙峰瘑鐮侊紒", "鐧诲綍澶辫触锛?/span>", {
                        confirmButtonText: 鈥?/span>纭畾鈥?/span>,
                        callback: () => {
                            this.username = 鈥樷€?/span>;
                            this.password = 鈥樷€?/span>;
                        }
                    });

                })
            },

            send_sms() {
                // 鍙戦€佺煭淇?
                if (!/^1[3-9]d{9}$/.test(this.mobile)) {
                    this.$message({
                        message: "瀵逛笉璧凤紒鎵嬫満鍙风爜鏍煎紡鏈夎锛?/span>"
                    });
                    return false;
                }

                // 鍒ゆ柇鏄惁鍦?0s鍐呭彂閫佽繃鐭俊
                if (this.is_send) {
                    this.$message({
                        message: "瀵逛笉璧?涓嶈兘棰戠箒鍙戦€佺煭淇¢獙璇侊紒"
                    });
                    return false;
                }

                // 璇锋眰鍙戦€佺煭淇?
                this.$axios({
                    url: this.$settings.base_url + 鈥?/span>/user/sms/鈥?/span>,
                    method: 鈥?/span>post鈥?/span>,
                    data: {
                        mobile: this.mobile
                    }
                }).then(response => {
                    let msg = response.data.result;
                    this.$message({
                        message: msg,
                    });
                    if (msg === 鈥?/span>鐭俊鍙戦€佸け璐?/span>鈥?/span>) return;

                    // 淇敼鐭俊鐨勫彂閫佺姸鎬?
                    this.is_send = true;

                    // 璁剧疆闂撮殧鏃堕棿60s
                    let sms_interval_time = 60;
                    // 璁剧疆鐭俊鍙戦€侀棿闅斿€掕鏃?.60s鍚庢妸is_send鏀规垚false
                    let timer = setInterval(() => {
                        if (sms_interval_time <= 1) {
                            clearInterval(timer);
                            this.sms_interval_tips = "鑾峰彇楠岃瘉鐮?/span>";
                            this.is_send = false; // 閲嶆柊鍥炲鐐瑰嚮鍙戦€佸姛鑳界殑鏉′欢
                        } else {
                            sms_interval_time -= 1;
                            this.sms_interval_tips = `${sms_interval_time}绉掑悗鍐嶆鑾峰彇`;
                        }
                    }, 1000);

                }).catch(error => {
                    this.$message({
                        message: error.response.data.result,
                    })
                });

            },

            loginMobile() {
                // 娉ㄥ唽淇℃伅鎻愪氦
                if (!/^1[3-9]d{9}$/.test(this.mobile)) {
                    this.$message({
                        message: "瀵逛笉璧凤紒鎵嬫満鍙风爜鏍煎紡鏈夎锛?/span>"
                    });
                    return false;
                }

                if (this.sms.length < 1) {
                    this.$message({
                        message: "鐭俊楠岃瘉鐮佷笉鑳戒负绌猴紒"
                    });
                    return false;
                }

                this.$axios({
                    url: this.$settings.base_url + 鈥?/span>/user/login/mobile/鈥?/span>,
                    method: 鈥?/span>post鈥?/span>,
                    data: {
                        mobile: this.mobile,
                        code: this.sms
                    }
                }).then(response => {
                    let _this = this;
                    let status = response.data.status;
                    let msg = response.data.msg;
                    if (status == 0) {
                        // cookie瀛樺偍token锛屼繚瀛樼櫥褰曠姸鎬?/span>
                        let token = response.data.results.token;
                        _this.$cookies.set(鈥榯oken鈥? token, 24 * 60 * 60);
                        let username = response.data.results.username;
                        _this.$cookies.set(鈥榰sername鈥? username, 24 * 60 * 60);

                        _this.$message({
                            message: 鈥?/span>鐧诲綍鎴愬姛鈥?/span>,
                            duration: 1000,
                            onClose() {
                                // 淇濆瓨鐧诲綍鐘舵€?
                                sessionStorage.token = token;
                                sessionStorage.username = response.data.results.username;
                                sessionStorage.mobile = response.data.results.mobile;
                                // 璺宠浆鍒颁富椤?
                                _this.$router.push(鈥?/span>/鈥?/span>);
                            }
                        });
                    }
                    else {
                        _this.mobile = 鈥樷€?/span>;
                        _this.sms = 鈥樷€?/span>;
                        _this.$message({
                            message: msg,
                        });
                    }

                }).catch(error => {
                    this.mobile = 鈥樷€?/span>;
                    this.sms = 鈥樷€?/span>;
                    this.$message({
                        message: error.response.data.result
                    });
                })

            },
        },

    };
</script>

<style scoped>
    .box {
        width: 100%;
        height: 100%;
        position: relative;
        overflow: hidden;
    }

    .box img {
        width: 100%;
        min-height: 100%;
    }

    .box .login {
        position: absolute;
        width: 500px;
        height: 400px;
        left: 0;
        margin: auto;
        right: 0;
        bottom: 0;
        top: -338px;
    }

    .login .login-title {
        width: 100%;
        text-align: center;
        padding-top: 20px;
    }

    .login-title img {
        width: 190px;
        height: auto;
    }

    .login-title p {
        font-family: PingFangSC-Regular;
        font-size: 18px;
        color: #fff;
        letter-spacing: .29px;
        padding-top: 10px;
        padding-bottom: 50px;
    }

    .login_box {
        width: 400px;
        height: auto;
        background: #fff;
        box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .5);
        border-radius: 4px;
        margin: 0 auto;
        padding-bottom: 40px;
    }

    .login_box .title {
        font-size: 20px;
        color: #9b9b9b;
        letter-spacing: .32px;
        border-bottom: 1px solid #e6e6e6;
        display: flex;
        justify-content: space-around;
        padding: 50px 60px 0 60px;
        margin-bottom: 20px;
        cursor: pointer;
    }

    .login_box .title span.active {
        color: #4a4a4a;
        border-bottom: 2px solid #84cc39;
    }

    .inp {
        width: 350px;
        margin: 0 auto;
    }

    .inp input {
        outline: 0;
        width: 100%;
        height: 45px;
        border-radius: 4px;
        border: 1px solid #d9d9d9;
        text-indent: 20px;
        font-size: 14px;
        background: #fff !important;
    }

    .inp input.user {
        margin-bottom: 16px;
    }

    .inp .rember {
        display: flex;
        justify-content: space-between;
        align-items: center;
        position: relative;
        margin-top: 10px;
    }

    .inp .rember p:first-of-type {
        font-size: 12px;
        color: #4a4a4a;
        letter-spacing: .19px;
        margin-left: 22px;
        display: -ms-flexbox;
        display: flex;
        -ms-flex-align: center;
        align-items: center;
        /*position: relative;*/
    }

    .inp .rember p:nth-of-type(2) {
        font-size: 14px;
        color: #9b9b9b;
        letter-spacing: .19px;
        cursor: pointer;
    }

    .inp .rember input {
        outline: 0;
        width: 30px;
        height: 45px;
        border-radius: 4px;
        border: 1px solid #d9d9d9;
        text-indent: 20px;
        font-size: 14px;
        background: #fff !important;
    }

    .inp .rember p span {
        display: inline-block;
        font-size: 12px;
        width: 100px;
        /*position: absolute;*/
        /*left: 20px;*/

    }

    #geetest {
        margin-top: 20px;
    }

    .login_btn {
        width: 100%;
        height: 45px;
        background: #84cc39;
        border-radius: 5px;
        font-size: 16px;
        color: #fff;
        letter-spacing: .26px;
        margin-top: 30px;
    }

    .inp .go_login {
        text-align: center;
        font-size: 14px;
        color: #9b9b9b;
        letter-spacing: .26px;
        padding-top: 20px;
    }

    .inp .go_login a {
        color: #84cc39;
        cursor: pointer;
    }

    #get_code {
        border: 0;
        width: 120px;
        height: 30px;
        background-color: antiquewhite;
        outline: none;
    }

    #get_code:active {
        color: white;
    }

    #checkbox {
        width: 20px;
        height: 20px;
    }

    .sms {
        position: relative;
    }

    .sms .sms_btn {
        position: absolute;
        top: -12px;
        right: 0;
        bottom: 0;
        margin: auto;
        width: 130px;
        text-align: center;
        height: 24px;
        color: #ff7000;
        cursor: pointer;
        border-left: 1px solid #999;
    }
</style>

浜?鍓嶅彴娉ㄩ攢

    鍚庡彴token杩囨湡鏃堕棿閰嶇疆dev.py:

# jwt閰嶇疆
import datetime
JWT_AUTH = {
    鈥?/span>JWT_EXPIRATION_DELTA鈥?/span>: datetime.timedelta(days=1),
}

  cookie鍦ㄧ櫥褰曟垚鍔熷悗宸蹭繚瀛?/strong>

   header.vue:

<template>
    <div class="header-box">
        <div class="header">
            <div class="content">
                <div class="logo full-left">
                    <router-link to="/"><img @click="jump(鈥?鈥?" src="@/assets/img/logo.svg" alt=""></router-link>
                </div>
                <ul class="nav full-left">
                    <li><span @click="jump(鈥?course鈥?" :class="this_nav==鈥?course鈥?鈥榯his鈥?鈥樷€?/span>">鍏嶈垂璇?lt;/span></li>
                    <li><span @click="jump(鈥?light-course鈥?" :class="this_nav==鈥?light-course鈥?鈥榯his鈥?鈥樷€?/span>">杞昏</span></li>
                    <li><span>瀛︿綅璇?lt;/span></li>
                    <li><span>棰樺簱</span></li>
                    <li><span>鑰佺敺瀛╂暀鑲?lt;/span></li>
                </ul>
                <div class="login-bar full-right">
                    <div class="shop-cart full-left">
                        <img src="@/assets/img/cart.svg" alt="">
                        <span><router-link to="/cart">璐墿杞?lt;/router-link></span>
                    </div>
                    <div class="login-box full-left">
                        <div v-if="!is_login">
                            <router-link to="/login">鐧诲綍</router-link>
                            &nbsp;|&nbsp;
                            <router-link to="/register">娉ㄥ唽</router-link>
                        </div>
                        <div v-else>
                            <router-link to="/user">{{ username }}</router-link>
                            &nbsp;|&nbsp;
                            <span @click="logoutAction">娉ㄩ攢</span>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        name: "Header",
        data() {
            return {
                this_nav: "",
                is_login: false,
                username: 鈥樷€?/span>,
            }
        },
        created() {
            this.this_nav = localStorage.this_nav;
            this.is_login = this.$cookies.get(鈥?/span>token鈥?/span>);
            this.username = this.$cookies.get(鈥?/span>username鈥?/span>);
        },
        methods: {
            jump(location) {
                localStorage.this_nav = location;
                // vue-router闄や簡鎻愪緵router-link鏍囩璺宠浆椤甸潰浠ュ锛岃繕鎻愪緵浜?js璺宠浆鐨勬柟寮?
                this.$router.push(location);
            },
            logoutAction() {
                this.is_login = false;
                this.username = 鈥樷€?/span>;
                this.$cookies.remove(鈥?/span>token鈥?/span>);
                this.$cookies.remove(鈥?/span>username鈥?/span>);
            }
        }
    }
</script>

 鍏?鎺ュ彛缂撳瓨

home/views.py

from rest_framework.generics import ListAPIView
from rest_framework.response import Response
from . import models, serializers

"""
class BannerListAPIView(ListAPIView):
    queryset = models.Banner.objects.filter(is_show=True, is_delete=False).order_by(鈥?orders鈥?
    serializer_class = serializers.BannerModelSerializer
"""
# 鎺ュ彛缂撳瓨
from django.core.cache import cache
class BannerListAPIView(ListAPIView):
    def get(self, request, *args, **kwargs):
        banner_list_data = cache.get(鈥?/span>api_banner_list_data鈥?/span>)
        if not banner_list_data:
            print(鈥?/span>鏌ヨ浜嗘暟鎹簱鈥?/span>)
            banner_query = models.Banner.objects.filter(is_show=True, is_delete=False).order_by(鈥?/span>-orders鈥?/span>)
            banner_list_data = serializers.BannerModelSerializer(banner_query, many=True).data
            # 寤虹珛鎺ュ彛缂撳瓨
            cache.set(鈥?/span>api_banner_list_data鈥?/span>, banner_list_data)
        return Response(banner_list_data)

涓嶈蛋ListAPIView鐨凴esponse,杩斿洖缁欏墠鍙扮殑鏁版嵁涓嶅甫鍓嶇紑,鎵€浠anner.vue闇€鍔犲墠缂€鎵嶈兘璁块棶:

<template>
    <div class="banner">
        <el-carousel height="520px" :interval="3000" arrow="always">
            <!--<el-carousel-item>-->
                <!--<img src="@/assets/img/banner1.png" alt="">-->
            <!--</el-carousel-item>-->
            <!--<el-carousel-item>-->
                <!--<img src="@/assets/img/banner2.png" alt="">-->
            <!--</el-carousel-item>-->
            <!--<el-carousel-item>-->
                <!--<img src="@/assets/img/banner3.png" alt="">-->
            <!--</el-carousel-item>-->
            <el-carousel-item v-for="banner in banner_list" :key="banner.image">
                <!--<router-link :to="banner.link">-->
                    <!--<img :src="banner.image" alt="">-->
                <!--</router-link>-->
                <a :href="banner.link">
                    <img :src="$settings.base_url + banner.image" >
                </a>
            </el-carousel-item>
        </el-carousel>
    </div>
</template>

璁块棶閲忚繃澶?缂撳瓨涔熸棤娉曞噺缂撴暟鎹簱鐨勮闂帇鍔?闇€瑕佸€熷姪celery

涓? Celery

瀹樻柟

Celery 瀹樼綉锛?span class="md-link">http://www.celeryproject.org/

Celery 瀹樻柟鏂囨。鑻辨枃鐗堬細http://docs.celeryproject.org/en/latest/index.html

Celery 瀹樻柟鏂囨。涓枃鐗堬細http://docs.jinkan.org/docs/celery/

 

Celery鏋舵瀯

Celery鐨勬灦鏋勭敱涓夐儴鍒嗙粍鎴愶紝娑堟伅涓棿浠讹紙message broker锛夈€佷换鍔℃墽琛屽崟鍏冿紙worker锛夊拰 浠诲姟鎵ц缁撴灉瀛樺偍锛坱ask result store锛夌粍鎴愩€?/span>

娑堟伅涓棿浠?/span>

Celery鏈韩涓嶆彁渚涙秷鎭湇鍔★紝浣嗘槸鍙互鏂逛究鐨勫拰绗笁鏂规彁渚涚殑娑堟伅涓棿浠堕泦鎴愩€傚寘鎷紝RabbitMQ, Redis绛夌瓑

浠诲姟鎵ц鍗曞厓

Worker鏄疌elery鎻愪緵鐨勪换鍔℃墽琛岀殑鍗曞厓锛寃orker骞跺彂鐨勮繍琛屽湪鍒嗗竷寮忕殑绯荤粺鑺傜偣涓€?/span>

浠诲姟缁撴灉瀛樺偍

Task result store鐢ㄦ潵瀛樺偍Worker鎵ц鐨勪换鍔$殑缁撴灉锛孋elery鏀寔浠ヤ笉鍚屾柟寮忓瓨鍌ㄤ换鍔$殑缁撴灉锛屽寘鎷珹MQP, redis绛?/span>

 

浣跨敤鍦烘櫙

寮傛浠诲姟锛氬皢鑰楁椂鎿嶄綔浠诲姟鎻愪氦缁機elery鍘诲紓姝ユ墽琛岋紝姣斿鍙戦€佺煭淇?閭欢銆佹秷鎭帹閫併€侀煶瑙嗛澶勭悊绛夌瓑

瀹氭椂浠诲姟锛氬畾鏃舵墽琛屾煇浠朵簨鎯咃紝姣斿姣忓ぉ鏁版嵁缁熻

 

Celery鐨勫畨瑁呴厤缃?/span>

pip install celery

娑堟伅涓棿浠讹細RabbitMQ/Redis

app=Celery(鈥樹换鍔″悕鈥? broker=鈥榵xx鈥? backend=鈥榵xx鈥?

 

Celery鎵ц寮傛浠诲姟

鍖呮灦鏋勫皝瑁?/span>

project
   鈹溾攢鈹€ celery_task # celery鍖?br />    鈹?  鈹溾攢鈹€ __init__.py # 鍖呮枃浠?br />    鈹?  鈹溾攢鈹€ celery.py   # celery杩炴帴鍜岄厤缃浉鍏虫枃浠讹紝涓斿悕瀛楀繀椤诲彨celery.py
   鈹?  鈹斺攢鈹€ tasks.py    # 鎵€鏈変换鍔″嚱鏁?br />    鈹溾攢鈹€ add_task.py # 娣诲姞浠诲姟
   鈹斺攢鈹€ get_result.py   # 鑾峰彇缁撴灉

 瀹夎:  pip install celery

celery.py:

# 1锛夊垱寤篴pp + 浠诲姟

# 2锛夊惎鍔╟elery(app)鏈嶅姟锛?/span>
# 闈瀢indows
# 鍛戒护锛歝elery worker -A celery_task -l info
# windows锛?/span>
# pip3 install eventlet
# celery worker -A celery_task -l info -P eventlet

# 3锛夋坊鍔犱换鍔★細鎵嬪姩娣诲姞锛岃鑷畾涔夋坊鍔犱换鍔$殑鑴氭湰锛屽彸閿墽琛岃剼鏈?/span>

# 4锛夎幏鍙栫粨鏋滐細鎵嬪姩鑾峰彇锛岃鑷畾涔夎幏鍙栦换鍔$殑鑴氭湰锛屽彸閿墽琛岃剼鏈?/span>

浠g爜:

from celery import Celery

"""
broker=瀛樺偍tasks鐨勪粨搴?
backend=瀛樺偍results鐨勪粨搴?
include=[浠诲姟鏂囦欢浠琞
"""
broker = 鈥?/span>redis://127.0.0.1:6379/11鈥?/span>
backend = 鈥?/span>redis://127.0.0.1:6379/12鈥?/span>
include = [鈥?/span>celery_task_1.task1鈥?/span>, 鈥?/span>celery_task_1.task2鈥?/span>]
app = Celery(broker=broker, backend=backend, include=include)

# 鍚姩celery鏈嶅姟鐨勫懡浠?/span>锛?/span>
# 鍓嶆彁锛氫竴瀹氳杩涘叆celery_task鎵€灞炵殑鏂囦欢澶?涓婁竴灞?
# celery worker -A celery_task_1 -l info -P eventlet

 

鍩烘湰浣跨敤(鍙啓澶氫釜task鏂囦欢,涔熷彲鎶婂涓嚱鏁板啓鍦ㄤ竴涓猼ask.py鏂囦欢涓?:

tasks.py

from .celery import app
import time, random
# 浠诲姟灏辨槸涓€涓釜鐨勫姛鑳藉嚱鏁帮紝鍔熻兘鍑芥暟鐨勮繑鍥炲€煎氨鏄换鍔$殑鎵ц缁撴灉
@app.task
def add(n, m):
    res = n + m
    # 妯℃嫙涓嶅浐瀹氳€楁椂鎿嶄綔
    time.sleep(random.randint(1, 5))
    print(res)
    return res

add_task.py:

# 鎵嬪姩娣诲姞浠诲姟鐨勬枃浠讹紝璇ユ枃浠朵笉鏄繀椤荤殑(鍙互鑷姩娣诲姞浠诲姟)
# 璇ユ枃浠朵篃涓嶅睘浜?celery task 鍖呯殑涓€閮ㄥ垎锛屽氨鏄敤鏉ュ疄鐜版墜鍔ㄦ坊鍔犱换鍔$殑

 # 灏哸dd娣诲姞鍒癰roker浠撳簱锛宑elery浼氬幓寮傛鎵ц

from celery_task import tasks

# 娣诲姞绔嬪嵆鎵ц浠诲姟
t1 = tasks.add.delay(10, 20)
t2 = tasks.low.delay(100, 50)
print(t1.id)


# 娣诲姞寤惰繜浠诲姟
from datetime import datetime, timedelta
def eta_second(second):
    ctime = datetime.now()
    utc_ctime = datetime.utcfromtimestamp(ctime.timestamp())
    time_delay = timedelta(seconds=second)
    return utc_ctime + time_delay

tasks.low.apply_async(args=(200, 50), eta=eta_second(10))

get_result.py:

from celery_task_1.celery import app

from celery.result import AsyncResult

id = 鈥?/span>4e249f2d-559a-4a3e-8b43-d498b3d6355e鈥?/span>
if __name__ == 鈥?/span>__main__鈥?/span>:
    async = AsyncResult(id=id, app=app)
    if async.successful():
        result = async.get()
        print(result)
    elif async.failed():
        print(鈥?/span>浠诲姟澶辫触鈥?/span>)
    elif async.status == 鈥?/span>PENDING鈥?/span>:
        print(鈥?/span>浠诲姟绛夊緟涓鎵ц鈥?/span>)
    elif async.status == 鈥?/span>RETRY鈥?/span>:
        print(鈥?/span>浠诲姟寮傚父鍚庢鍦ㄩ噸璇?/span>鈥?/span>)
    elif async.status == 鈥?/span>STARTED鈥?/span>:
        print(鈥?/span>浠诲姟宸茬粡寮€濮嬭鎵ц鈥?/span>)

鑷姩娣诲姞(娌℃湁add_task.py鏂囦欢):

celery.py

from celery import Celery

broker = 鈥?/span>redis://127.0.0.1:6379/11鈥?/span>
backend = 鈥?/span>redis://127.0.0.1:6379/12鈥?/span>
include = [鈥?/span>celery_task.tasks鈥?/span>,]
app = Celery(broker=broker, backend=backend, include=include)

# 鍚姩celery鏈嶅姟鐨勫懡浠わ細
# 鍓嶆彁锛氫竴瀹氳杩涘叆celery_task鎵€灞炵殑鏂囦欢澶?
# celery worker -A celery_task -l info -P eventlet


# 鑷姩娣诲姞浠诲姟
# 鏃跺尯
app.conf.timezone = 鈥?/span>Asia/Shanghai鈥?/span>
# 鏄惁浣跨敤UTC
app.conf.enable_utc = False

# 浠诲姟鐨勫畾鏃堕厤缃?/span>
from datetime import timedelta
from celery.schedules import crontab
app.conf.beat_schedule = {
    鈥?/span>low-task鈥?/span>: {
        鈥?/span>task鈥?/span>: 鈥?/span>celery_task.tasks.low鈥?/span>,
        鈥?/span>schedule鈥?/span>: timedelta(seconds=3),
        # 鈥榮chedule鈥? crontab(hour=8, day_of_week=1),  # 姣忓懆涓€鏃╁叓鐐?/span>
        鈥?/span>args鈥?/span>: (300, 150),
    },
    鈥?/span>my-add-task鈥?/span>: {
        鈥?/span>task鈥?/span>: 鈥?/span>celery_task.tasks.add鈥?/span>,
        鈥?/span>schedule鈥?/span>: timedelta(seconds=6),
        鈥?/span>args鈥?/span>: (300, 150),
    }
}

# 鍚姩 娣诲姞浠诲姟 鏈嶅姟鐨勫懡浠?涔熻杩涘叆celery_task鎵€灞炴枃浠跺す
# celery beat -A celery_task -l info

tasks.py:

from .celery import app
@app.task
def add(n, m):
    res = n + m
    print(res)
    return res

@app.task
def low(n, m):
    res = n - m
    print(res)
    return res

get_result.py鍚屼笂

 

celery寮傛澶勭悊django浠诲姟(鎶奵elery_task鏀惧湪澶uffy涓?:

celery.py

# 鍚姩django渚濊禆

# 灏哻elery鏈嶅姟妗嗘灦鏀惧湪椤圭洰鏍圭洰褰曚笅
# import sys
# sys.path.append(r鈥楥:UsersoldboyDesktopluffyluffyapi鈥?

import os, django
os.environ.setdefault(鈥?/span>DJANGO_SETTINGS_MODULE鈥?/span>, 鈥?/span>luffyapi.settings.dev鈥?/span>)
django.setup()


from celery import Celery

broker = 鈥?/span>redis://127.0.0.1:6379/11鈥?/span>
backend = 鈥?/span>redis://127.0.0.1:6379/12鈥?/span>
include = [鈥?/span>celery_task.tasks鈥?/span>,]
app = Celery(broker=broker, backend=backend, include=include)

# 鍚姩celery鏈嶅姟鐨勫懡浠わ細
# 鍓嶆彁锛氫竴瀹氳杩涘叆celery_task鎵€灞炵殑鏂囦欢澶?/span>
# celery worker -A celery_task -l info -P eventlet


# 鑷姩娣诲姞浠诲姟
# 鏃跺尯
app.conf.timezone = 鈥?/span>Asia/Shanghai鈥?/span>
# 鏄惁浣跨敤UTC
app.conf.enable_utc = False

# 浠诲姟鐨勫畾鏃堕厤缃?/span>
from datetime import timedelta
from celery.schedules import crontab
app.conf.beat_schedule = {
    鈥?/span>django-task鈥?/span>: {
        鈥?/span>task鈥?/span>: 鈥?/span>celery_task.tasks.django_task鈥?/span>,
        鈥?/span>schedule鈥?/span>: timedelta(seconds=10),
        # 鈥榮chedule鈥? crontab(hour=8, day_of_week=1),  # 姣忓懆涓€鏃╁叓鐐?/span>
        鈥?/span>args鈥?/span>: (),
    },
}

# 鍚姩 娣诲姞浠诲姟 鏈嶅姟鐨勫懡浠?/span>
# celery beat -A celery_task -l info

tasks.py:

from .celery import app

from django.core.cache import cache
from apps.home import models, serializers
@app.task
def django_task():
    banner_query = models.Banner.objects.filter(is_show=True, is_delete=False).order_by(鈥?/span>-orders鈥?/span>)
    banner_list_data = serializers.BannerModelSerializer(banner_query, many=True).data
    # 寤虹珛鎺ュ彛缂撳瓨
    cache.set(鈥?/span>api_banner_list_data鈥?/span>, banner_list_data)
    return 鈥?/span>杞挱鍥剧紦瀛樻洿鏂板畬姣?/span>鈥?/span>

 

以上是关于Luffy--------------(鍥?的主要内容,如果未能解决你的问题,请参考以下文章

Java楂樺苟鍙戠綉缁滅紪绋?鍥?Netty

[kuangbin甯︿綘椋瀅涓撻鍥?鏈€鐭矾缁冧範 F - Wormholes 锛堝垽鏂礋鐜級

luffy前台案例

luffy-city 基础环境搭建(至轮播图前后台交互实现)-步骤目录

luffy

luffy 那点事