微服務的編程測評系統6-管理員登錄前端-前端路由優化

提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔

文章目錄

  • 前言
  • 1. 管理員登錄前端
    • 1.1 測試
    • 1.2 同源策略
    • 1.3 修改前端端口號
    • 1.4 跨域問題
    • 1.5 接收響應數據
    • 1.6 js-cookie
    • 1.7 錯誤消息提示
    • 1.8 優化
    • 1.9 響應攔截器
    • 1.10 @用法
  • 2. 后臺管理-布局
    • 2.1 點擊跳轉不同頁面
  • 3. 獲取當前用戶信息
    • 3.1 數據庫修改
    • 3.2 設計
    • 3.3 開發后端
    • 3.4 開發前端
  • 4. 退出登錄
    • 4.1 業務分析
    • 4.2 后端開發
    • 4.3 前端開發
  • 5. 前端路由優化
    • 5.1 重定向
    • 5.2 全局前置守衛
    • 5.3 token過期處理
  • 總結


前言

1. 管理員登錄前端

1.1 測試

在這里插入圖片描述
測試一下發現報了這個錯
這個主要是因為我們配置的前置url沒有含有http協議,所以瀏覽器就會自動加上靜態資源的url

const service = axios.create({baseURL: "http://127.0.0.1:19090/system",timeout: 1000,
})

這樣就Ok了
在這里插入圖片描述
但是又出了一個新的問題
這個就是跨域問題

1.2 同源策略

在這里插入圖片描述

1.3 修改前端端口號

在vite.config.js里面添加

  server: {port: 5555,}

這樣就可以了

在這里插入圖片描述

1.4 跨域問題

我們可以用一個代理服務器來處理
瀏覽器前端先請求同源的代理服務器,然后代理服務器把請求轉發到后端

因為瀏覽器有同源策略的約束
但是代理服務器是沒有同源策略的約束的

  server: {proxy: {"/dev-api": {target: "http://127.0.0.1:19090/system",rewrite: (p) => p.replace(/^\/dev-api/, ""),},},},

還是在vite.config.js里面,這樣設置就可以了
這個就是對代理規則的配置

然后修改request.js里面的前置url

const service = axios.create({baseURL: "/dev-api",timeout: 1000,
})

如果沒有加協議的話,瀏覽器會把前端的url拼接到baseURL上
所以請求地址為
http://localhost:5173/dev-api/sysUser/login
這個不會發生跨域問題
這個是同源的
會報404,找不到嗎
不會
因為配置了代理規則
代理規則就是前綴包含/dev-api的時候,就會把請求轉發到http://127.0.0.1:19090/system

  rewrite: (p) => p.replace(/^\/dev-api/, ""),

這個就是把/dev-api變為空的字符串
所以http://localhost:5173/dev-api/sysUser/login就會變為
http://127.0.0.1:19090/system/sysUser/login
先把http://localhost:5173替換為http://127.0.0.1:19090/system,然后去掉/dev-api,就OK了

在這里插入圖片描述
補充一下,axios可以自動轉換JSON數據,所以那樣寫沒有問題

測試一下也是沒有問題的

1.5 接收響應數據

但是axios這個調用接口的過程是一個異步的過程
往往不那么好拿到返回結果
所以要用await去獲取異步操作結果
但是對應的調用他的函數也要為async

async function  loginFun() {const res = await loginService(userAccount.value, password.value);if(res.data.code === 1000){console.log("登錄成功:" , res.data);}else{console.log(res.data.msg)}
}

async 表示這個函數要使用await,await表示調用這個方法接口的時候用異步的方式
console.log(“登錄成功:” , res.data);這里,如果是 console.log(“登錄成功:” +res.data);

那么res.data打印出的內容是object
如果要打印出類的詳細數據的話,還是得用逗號隔開
在這里插入圖片描述
登錄成功要跳轉頁面
我們用router的push方法就可以了

在這里插入圖片描述

    {path: '/oj/system',name: 'system',component: () => import('../views/System.vue')}

記得還要配置路由

import router from '@/router'
router.push("/oj/system")

這樣就可以跳轉了

1.6 js-cookie

登錄成功以后要存儲token
怎么存儲呢
存儲方式有很多種
比如cookie和local-storige
我們這里使用cookie存儲
js-cookie就是來操作cookie的

npm install js-cookie

在utils下創建cookie.js

import Cookies from "js-cookie";
const TokenKey = "Admin-Oj-b-Token";
export function getToken() {return Cookies.get(TokenKey);
}
export function setToken(token) {return Cookies.set(TokenKey, token);
}
export function removeToken() {return Cookies.remove(TokenKey);
}

這樣就可以了

import { setToken } from '@/utils/cookie'
setToken(res.data.data)

在這里插入圖片描述
在appication.cookies那里就可以看到我們設置的cookie了

1.7 錯誤消息提示

import { ElMessage } from 'element-plus'
ElMessage.error(res.data.msg)

1.8 優化

          <el-input v-model="password"  type="password" show-password placeholder="請輸入密碼" />

給按鈕加上 type=“password”
就可以把密碼隱藏起來了
show-password就是顯示是不是顯示小眼睛

1.9 響應攔截器

我們在request.js里面設置

service.interceptors.response.use((res) => {// 未設置狀態碼則默認成功狀態const code = res.data.code;const msg = res.data.msg;if (code !== 1000) {ElMessage.error(msg);return Promise.reject(new Error(msg));} else {return Promise.resolve(res.data);}},(error) => {return Promise.reject(error);}
);

為什么要用響應攔截器呢
因為我們可以用響應攔截器對響應進行攔截,,可以直接返回后端返回的result數據
現在我們就在登錄成功和失敗的情況下分別測試一下

  const res = await loginService(userAccount.value, password.value);console.log("登錄成功:" , res.data);

在這里插入圖片描述
在這里插入圖片描述
我們可以看出登錄成功返回的數據就是后端返回的result,沒有進行分裝了
這個就是Promise.resolve(res.data)的作用
而Promise.reject(new Error(msg))就是相當于返回一個異常了
直接報錯了

    (error) => {return Promise.reject(error);}

這里是屬于錯誤返回,其他的都是正常返回
我們控制臺肯定不能這樣打印的,因為這樣打印就是相當于出錯了
所以我們還要捕獲異常
就和后端捕獲異常是一樣的寫法

async function  loginFun() {try{const res = await loginService(userAccount.value, password.value);setToken(res.data.data)router.push("/oj/system")console.log("登錄成功:" , res.data);}catch(err){console.log("登錄失敗:" , err);}
}

這樣就可以了

1.10 @用法

import { setToken } from '@/utils/cookie'

這里的@是什么意思呢

  resolve: {alias: {'@': fileURLToPath(new URL('./src', import.meta.url))},},

其實在vite.config.js里面就配置過了
@就是./src,所以很方便使用
在這里插入圖片描述
相應的router這里的兩點我們也可以改為@

在這里插入圖片描述

2. 后臺管理-布局

創建一個布局的文件
Layout.vue

<template><el-container class="layout-container"><el-header class="el-header"><el-dropdown><span class="el-dropdown__box"><div><strong>當前用戶:</strong>超級管理員</div><el-icon><ArrowDownBold /></el-icon><!-- <el-icon><Lock /></el-icon> --></span><template #dropdown><el-dropdown-menu><el-dropdown-item @click="logout" :icon="SwitchButton">退出登錄</el-dropdown-item></el-dropdown-menu></template></el-dropdown></el-header><el-main class="layout-bottom-box"><div class="left"><el-aside width="200px" class="el-aside"><el-menu class="el-menu" router><el-menu-item index="/oj/layout/cuser"><el-icon><Management /></el-icon><span>用戶管理</span></el-menu-item><el-menu-item index="/oj/layout/question"><el-icon><Management /></el-icon><span>題目管理</span></el-menu-item><el-menu-item index="/oj/layout/exam"><el-icon><Management /></el-icon><span>競賽管理</span></el-menu-item></el-menu></el-aside></div><div class="right"><RouterView /></div></el-main></el-container>
</template><script setup>
import {Management,ArrowDownBold,Lock,SwitchButton
} from '@element-plus/icons-vue'
</script><style lang="scss" scoped>
.layout-container {height: 100vh;background: #f7f7f7;.layout-bottom-box {display: flex;justify-content: space-between;height: calc(100vh - 100px);overflow: hidden;.left {margin-right: 20px;background: #fff;display: flex;:deep(.el-menu) {flex: 1;.el-menu-item.is-active {color: #32c5ff;}.el-menu-item:hover {background: #fff;color: #32c5ff;}}}.right {flex: 1;overflow-y: auto;background: #fff;padding: 20px;}}.el-aside {background-color: #fff;&__logo {height: 120px;// background: url('@/assets/logo.png') no-repeat center / 120px auto;}.el-menu {border-right: none;}}.el-header {background-color: #fff;display: flex;align-items: center;justify-content: flex-end;height: 40px;.el-dropdown__box {display: flex;align-items: center;.el-icon {color: #4c4141;margin-left: 20px;}&:active,&:focus {outline: none;}}}.el-footer {display: flex;align-items: center;justify-content: center;font-size: 14px;color: #666;}
}
</style>

然后配置路由

    {path: '/oj/layout',name: 'layout',component: () => import('@/views/Layout.vue')}

在這里插入圖片描述
這樣就可以了
現在分析一下
分成了三個部分
在這里插入圖片描述
我們用的是是這個container布局容器

  <el-container class="layout-container"><el-header class="el-header"></el-header><el-main class="layout-bottom-box"><div class="left"><el-aside width="200px" class="el-aside"></el-aside></div><div class="right"><RouterView /></div></el-main></el-container>

這個就是布局
整體結構

dropdown是一個下拉菜單

在這里插入圖片描述
在這里插入圖片描述

ArrowDownBold是圖標
在這里插入圖片描述
在這里插入圖片描述

import {Management,ArrowDownBold,Lock,SwitchButton
} from '@element-plus/icons-vue'

但是使用圖標要在js里面import

SwitchButton也是圖標

el-aside我們用的是el-menu來寫的

2.1 點擊跳轉不同頁面

      <div class="right"><RouterView /></div>

這里就是根據不同url,渲染不同頁面,就是主要的頁面內容
先創建對應的vue文件

在這里插入圖片描述
然后是配置router
在這里插入圖片描述
怎么實現點擊切換不url呢

官網的menu組件有一個router屬性
在這里插入圖片描述
這樣就可以實現點擊切換url了

首先先加上屬性router
因為默認為false,不啟用,加上就啟動了

          <el-menu class="el-menu" router>

在el-menu這里加上router屬性,表示啟動router
然后還不行,因為點擊要跳轉到哪里呢,
這下就要設置index了

在這里插入圖片描述
所以index設置為路徑就可以了

            <el-menu-item index="/oj/question">

這樣設置就可以了
在這里插入圖片描述
但是點擊跳轉直接跳轉到新的頁面了,而不是在那個主頁面展示
為什么會這樣呢
因為我們配置的路徑是/oj/question,與/oj/layout是同一級的
這個路由發生改變之后
會觸發routerview,但是這個觸發的routerview是app.vue那里的

    {path: '/oj/layout',name: 'layout',component: () => import('@/views/Layout.vue')},{path: '/oj/cuser',name: 'cuser',component: () => import('@/views/Cuser.vue')},

這里的配置路徑就是同一級的,所以改變路徑的時候就是觸發的同一個routerview,因為這兩個的路徑的配置是類似的,所以layout能觸發那個routerview,為什么cuser不行呢
所以cuser也是用的app.vue的routerview
所以我們需要把cuser的路由配置到layout里面去
因為cuser是在layout內部進行的頁面渲染
因為渲染的順序就是先渲染layout,然后是點擊在layout里面渲染cuser
所以路徑的配置,就必須在layout里面進行配置
所以cuser的路由就是layout下的路由
路由提供了一個child的屬性就可以配置了,這個是數組

    {path: '/oj/layout',name: 'layout',component: () => import('@/views/Layout.vue'),children: [{path: '/cuser',name: 'cuser',component: () => import('@/views/Cuser.vue')},{path: '/exam',name: 'exam',component: () => import('@/views/Exam.vue')},{path: '/question',name: 'question',component: () => import('@/views/Question.vue')},]},

這樣就可以了
其中cuser的路徑就是/oj/layout/cuser
會自動加上父組件的路徑的

      router.push("/oj/layout")

然后登錄成功的跳轉也要改了

 <el-menu-item index="/oj/layout/cuser">

然后這里也要改
在這里插入圖片描述

但是還是不行

      <div class="right"><RouterView /></div>

這里的routerview是二級目錄
這里跳轉顯示的是二級路由,而app.vue里面顯示跳轉的是一級路由
這里的情況是在一級小的頁面里面有二級頁面,所以對應也要在app.vuede的routerview里面在嵌套一個routerview
因為頁面有嵌套關系
所以routerview也要有嵌套關系,路由配置也要有嵌套關系

    {path: '/oj/layout',name: 'layout',component: () => import('@/views/Layout.vue'),children: [{path: 'cuser',name: 'cuser',component: () => import('@/views/Cuser.vue')},{path: 'exam',name: 'exam',component: () => import('@/views/Exam.vue')},{path: 'question',name: 'question',component: () => import('@/views/Question.vue')},]},

注意這里的cuser的路徑前面就不要加上/了,因為這樣可能表示是以/cser開頭的,是絕對路徑,如果是二級路徑,就最前面不要加/了
子路由 path 不加 /:路徑會自動拼接父路由路徑,保持嵌套關系(正確用法)。
子路由 path 加 /:路徑被視為絕對路徑,脫離父路由,成為獨立的一級路由(不符合二級路由的設計意圖)。

3. 獲取當前用戶信息

3.1 數據庫修改

給數據庫添加用戶昵稱字段

    nick_name varchar(20) not null comment '昵稱',

要么重新創建數據庫
要么用alter

alter table  tb_sys_user add nick_name varchar(20)  null  after user_account ;
update tb_sys_user set nick_name = '超級管理員' where user_account = 'aaa'
[HY000][1366] Incorrect string value: '\xE8\xB6\x85\xE7\xBA\xA7...' for column 'nick_name' at row 1

在update的時候報錯了,這個是因為不支持中文的原因
就是編碼出問題
改一下配置文件就可以了

在這里插入圖片描述
找到etc/my.cnf
加上配置

character-set-server=utf8mb4
collation-server = utf8mb4_general_ci

保存一下,然后重啟容器生效

但是就算這樣修改了執行還是不行,因為這個表提前就創建好了,編碼已經確定了
所以不行
所以我們要重新創建一個表
但是就算創建一個新的表還是有編碼問題
怎么回事呢
因為數據庫的編碼沒有變
得創建一個新的庫才可以
我們先用root用戶創建新的庫
所以說改了配置文件以后,刪除以前的庫才可以,或者創建新的庫才可以生效
右鍵表的數據,然后生成sql,生成insertSQL就可以保存數據了
在這里插入圖片描述
這樣就成功了

3.2 設計

在這里插入圖片描述

3.3 開發后端

    @GetMapping("/info")public R<LoginUserVO> info(@RequestHeader(HttpConstants.AUTHENTICATIO) String token){return sysUserService.info(token);}

我們的token直接從header里面獲取就可以了,用的是RequestHeader注解

@Data
public class LoginUserVO {private String nickName;
}
@Data
public class LoginUser {//存儲在redis中的用戶信息private Integer identity;private String nickName;
}

這里也要完善一下,存儲到redis中的基本數據,還有數據庫對應的類也要增加字段,記得還要修改對應的代碼,登錄存儲基本用戶數據的時候記得修改代碼,存儲昵稱
在這里插入圖片描述

tokenService里面分裝方法

    public LoginUser getLoginUser(String token, String secret   ) {String userKey = getUserKey(token, secret);if(userKey == null){return null;}String tokenKey = getTokenKey(userKey);return redisService.getCacheObject(tokenKey, LoginUser.class);}private String getTokenKey(String userKey) {return CacheConstants.LOGIN_TOKEN_KEY + userKey;}private String getUserKey(String token, String secret) {Claims claims;try {claims = JwtUtils.parseToken(token, secret); //獲取令牌中信息 解析payload中信息if (claims == null) {log.error("令牌已過期或驗證不正確!");return null;}} catch (Exception e) {log.error("令牌已過期或驗證不正確!e:",e);return null;}return JwtUtils.getUserKey(claims); //獲取jwt中的key}
    @Overridepublic R<LoginUserVO> info(String token) {if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);}LoginUser loginUser = tokenService.getLoginUser(token,secret);if(loginUser == null){return R.fail();}LoginUserVO loginUserVO = new LoginUserVO();loginUserVO.setNickName(loginUser.getNickName());return R.ok(loginUserVO);}

然后測試一下

del+key可以刪除redis數據

在這里插入圖片描述
在這里插入圖片描述
這樣就成功了
這個是根據登錄設置的header自動進行查詢的,不用傳json
然后測試一下延長redis時間的接口也是沒有問題的

3.4 開發前端

export function getUserInfoService(){return service({url: '/sysUser/info',method: 'get'})
}
import { reactive } from 'vue';
import { getUserInfoService } from '@/apis/suser';const loginUser = reactive({nickName: ''
})async function getUserInfo(){const userInfo = await getUserInfoService();loginUser.nickName = userInfo.data.nickName;
}await getUserInfo();

然后再request.js里面定義請求攔截器

//請求攔截器
service.interceptors.request.use((config) => {if (getToken()) {config.headers["Authorization"] = "Bearer " + getToken();}return config;},(error) => {console.log(error)Promise.reject(error);}
);

這個請求攔截器就是攔截每個給后端發起的請求,然后判斷是否有token,有的話,就在請求頭中加上token

          <div><strong>當前用戶:</strong>{{loginUser.nickName}}</div>

注意登錄的前端接口寫錯了
改一下為這個樣子

async function  loginFun() {try{const res = await loginService(userAccount.value, password.value);setToken(res.data)router.push("/oj/layout")console.log("登錄成功:" , res.data);}catch(err){console.log("登錄失敗:" , err);}
}

這樣就可以了
在這里插入圖片描述

4. 退出登錄

4.1 業務分析

就是讓token不為空,然后解析一下,解析出來不能執行正常業務了。是可以進行解析的
所以點擊退出登錄,讓redis中的數據不存在就可以了
在這里插入圖片描述
這樣就可以避免多次登錄,redis中的數據增多了
后端返回請求以后,如果是成功的,前端就清楚存儲的token
所以就是后端清楚redis,前端清楚token
在這里插入圖片描述

4.2 后端開發

    @DeleteMapping("/logout")@Operation(summary = "退出登錄", description = "退出登錄")public R<Void> logout(@RequestHeader(HttpConstants.AUTHENTICATION) String token){log.info("退出登錄...,token:{}", token);return toR(sysUserService.logout(token));}

因為退出登錄后端會刪除redis所以是DeleteMapping

    @Overridepublic boolean logout(String token) {if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);}return tokenService.deleteLoginUser(token,secret);}
    public boolean deleteLoginUser(String token, String secret) {String userKey = getUserKey(token, secret);if(userKey == null){return false;}String tokenKey = getTokenKey(userKey);return redisService.deleteObject(tokenKey);}

然后測試一下

4.3 前端開發

點擊退出登錄的時候會有一個消息彈窗
我們用的就是elementplus的消息彈窗
在這里插入圖片描述
在這里插入圖片描述
觀察一下我們可以發現
ElMessageBox.confirm 方法返回一個 Promise 對象。當用戶點擊確認按鈕時,Promise 會進入 resolved 狀態,此時會執行 .then() 中的回調函數;而當用戶點擊取消按鈕或者關閉對話框時,Promise 會進入 rejected 狀態,這時就會執行 .catch() 中的回調函數。
所以說用戶點擊取消按鈕或者關閉對話框時,就是相當于拋出了一個異常,所以彈窗如果后面還有代碼就不會執行了
而點擊了確定按鈕的話(其實點擊什么都是返回Promise ),會返回一個 Promise 對象,返回這個對象是一個異步的過程,所以要await,不然異步的話,就去判斷Promise 對象,可能會判斷失誤
所以說點擊了確定按鈕的話,就會執行彈窗后面的代碼了

export function logoutService(){return service({url: '/sysUser/logout',method: 'delete'})
}

如果是函數拋出異常,也會結束后面代碼執行

async function logout(){await ElMessageBox.confirm('退出登錄','溫馨提示',{confirmButtonText: '確認',cancelButtonText: '取消',type: 'warning',})await logoutService();removeToken();router.push('/oj/login');
}

因為logoutService拋出的異常我們可以直接輸出錯誤,對于異常的情況沒有什么好處理的,就什么都不干就可以了,所以我們不用try和catch

5. 前端路由優化

5.1 重定向

這個的問題是什么呢
在這里插入圖片描述
就是我們點擊這個地址不用直接到login,而是要手動輸入地址才可以了
什么做到點擊這個http://localhost:5173/,就可以自動跳轉到login呢,這個就要使用重定向了

    {path: '/',redirect: '/oj/login'},

這樣配置就可以了

5.2 全局前置守衛

我們要求
未登錄要求不管點擊哪個頁面都要跳回登錄頁面
登錄過后,未過期,點回login自動跳轉功能頁面
登錄過后,點擊login,就直接不用登錄就可以使用功能了

就是要在路由跳轉之前進行判斷處理
誰來進行頁面跳轉呢,就是router,router在哪里呢,就是在index.js里面配置的,所以對router進行配置即可,就是對頁面跳轉之前進行的配置

router.beforeEach((to, from, next) => {if (getToken()) {  //已經登陸過/* has token*/if (to.path === '/oj/login') {next({ path: '/oj/layout/question' })} else {next()}} else {if (to.path !== '/oj/login') {next({path:'/oj/login'})} else {next()}}
})

這樣就可以了
to是目的路由
from是源路由
next是真正的目的路由是去哪里
next()就是和to一樣的

這樣我們在沒有登錄的情況下輸入http://localhost:5173/oj/layout
就會自動跳轉為http://localhost:5173/oj/login
登錄下輸入http://localhost:5173/oj/login
就會自動變為http://localhost:5173/oj/layout/question
但是如果登錄狀態過期呢
這個也是和沒有登錄是一樣的,怎么判斷呢,前端是無法判斷token是否過期的

5.3 token過期處理

        boolean isLogin = redisService.hasKey(getTokenKey(userKey));if (!isLogin) {return unauthorizedResponse(exchange, "登錄狀態已過期");}
    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, Stringmsg) {log.error("[鑒權異常處理]請求路徑:{}", exchange.getRequest().getPath());return webFluxResponseWriter(exchange.getResponse(), msg,ResultCode.FAILED_UNAUTHORIZED.getCode());}
    FAILED_UNAUTHORIZED (3001, "未授權"),

我們可以這樣處理
因為后端對于token過期會報錯的,就在網關中,會報登錄狀態已過期,會報3001的錯誤,,,而且我們還有響應攔截器,所以就可以在響應攔截器中進行處理了,如果過期了就自動跳轉到login

所以說登錄狀態由瀏覽器的token和token是否過期一起決定
我們可以去redis中刪除數據,手動弄為過期
因為過期了,redis就會自動刪除數據,所以我們刪除它,就是模仿的過期

service.interceptors.response.use((res) => {// 未設置狀態碼則默認成功狀態const code = res.data.code;const msg = res.data.msg;if(code === 3001){ElMessage.error(msg);router.push('/oj/login')removeToken();return Promise.reject(new Error(msg));}else if (code !== 1000) {ElMessage.error(msg);return Promise.reject(new Error(msg));} else {return Promise.resolve(res.data);}},(error) => {return Promise.reject(error);}
);

為什么要removeToken呢,因為已經過期了,沒用了,但是不刪掉的話,就可以一直去layout頁面
注意如果是刷新的話,在全局前置守衛中,to就是自身url,而from則是根路徑

這樣就OK了
調試也沒有錯誤

總結

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/916068.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/916068.shtml
英文地址,請注明出處:http://en.pswp.cn/news/916068.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

南京銀行提前批金融科技面試記錄

問題1&#xff1a;自我介紹 問題2&#xff1a;為什么選擇南京銀行 問題3&#xff1a;為什么碩士是計算機專業&#xff0c;博士要轉到網絡安全專業 問題4&#xff1a;項目經歷中&#xff0c;你主要承擔什么工作 問題5&#xff1a;達夢數據庫的遷移&#xff0c;你具體做了什么 以…

STM32-第九節-ADC模數轉換

一、ADC簡介&#xff1a;1.名稱&#xff1a;ADC&#xff0c;Analog-Digital Converter&#xff0c;模擬數字轉換器2.用途&#xff1a;相當于電壓表&#xff0c;原本引腳只有兩種狀態&#xff0c;高電平和低電平&#xff0c;使用ADC后&#xff0c;可以將0-3.3V間的任一引腳電壓&…

nuxt更改頁面渲染的html,去除自定義屬性、

nuxt2 nuxt.config.js module.exports {// ...hooks: {render:route: (url, result) > {// 去除nuxt自定義屬性result.html result.html.replace(/\sdata-n-head".*?"/gi,).replace(/\sdata-hid".*?"/gi, ).replace(/<a(.*?)href"\//gi,…

如何將iPad中的視頻傳輸到電腦(6種簡單方法)

iPad是一款功能強大的平板電腦&#xff0c;不僅用于娛樂和工作&#xff0c;還可以用于拍攝和保存珍貴的視頻。然而&#xff0c;iPad的存儲容量是有限的&#xff0c;這意味著你可能會遇到需要將視頻從iPad傳輸到電腦的情況。無論你是想為iPad騰出空間&#xff0c;還是想在更大的…

UE5多人MOBA+GAS 28、創建資產類來管理GAS通用的資產、設置經驗表來升級以及用MMC計算升級添加的屬性值

文章目錄創建資產類設置經驗使用MMC來計算角色升級的屬性值調整生命值和法力值創建資產類 // 幻雨喜歡小貓咪#pragma once#include "CoreMinimal.h" #include "Abilities/GameplayAbility.h" #include "Engine/DataAsset.h" #include "PDA_…

隧道代理的動態IP切換機制與實現原理

目錄 一、動態IP切換的底層邏輯 1. 統一入口與動態出口的魔法 2. 云端IP池的智能調度 二、協議層的技術突破 1. 傳輸層隧道&#xff1a;IPsec與WireGuard的較量 2. 應用層隧道&#xff1a;HTTP/SOCKS5的進化 三、動態切換的觸發機制 1. 被動觸發&#xff1a;封禁檢測與應…

時序數據庫主流產品概覽

時序數據庫(Time Series Database, TSDB)是專為處理時間序列數據優化的數據庫系統&#xff0c;近年來隨著物聯網(IoT)、金融科技、工業互聯網等領域的快速發展而備受關注。本文將介紹當前主流的時序數據庫產品。一、時序數據庫概述時序數據是帶時間戳記錄的數據點序列&#xff…

圖機器學習(17)——基于文檔語料庫構建知識圖譜

圖機器學習&#xff08;17&#xff09;——基于文檔語料庫構建知識圖譜0. 前言1. 基于文檔語料庫構建知識圖譜2. 知識圖譜3. 文檔-實體二分圖0. 前言 文本數據的爆炸性增長&#xff0c;直接推動了自然語言處理 (Natural Language Processing, NLP) 領域的快速發展。在本節中&a…

【實時Linux實戰系列】實時文件系統的特性與優化

在實時系統中&#xff0c;文件系統的性能和可靠性對于系統的整體表現至關重要。實時文件系統需要在嚴格的時間約束內完成文件的讀寫操作&#xff0c;以確保系統的實時性。本文將介紹實時文件系統的基本特性和應用場景&#xff0c;并提供相關的實施和優化建議&#xff0c;以滿足…

Clickhouse源碼分析-副本數據同步

1 總體流程上圖說明了一條insert語句最后如何被副本同步到的流程&#xff08;圖中ck集群為單shard&#xff0c;雙副本&#xff09;。&#xff08;1&#xff09;從客戶端發出&#xff0c;寫入ck&#xff08;2&#xff09;ck提交LogEntry到Keeper&#xff08;3&#xff09;另外一…

Spring AI 系列之二十四 - ModerationModel

之前做個幾個大模型的應用&#xff0c;都是使用Python語言&#xff0c;后來有一個項目使用了Java&#xff0c;并使用了Spring AI框架。隨著Spring AI不斷地完善&#xff0c;最近它發布了1.0正式版&#xff0c;意味著它已經能很好的作為企業級生產環境的使用。對于Java開發者來說…

在 macOS 上 安裝最新 Python 和 pip

文章目錄方法一&#xff1a;使用 Homebrew&#xff08;推薦&#xff09;方法二&#xff1a;使用 pyenv&#xff08;管理多個 Python 版本&#xff09;方法三&#xff1a;從官網下載安裝包升級 pip驗證安裝方法一&#xff1a;使用 Homebrew&#xff08;推薦&#xff09; 1. 安裝…

新能源電池廠自動化應用:Modbus TCP轉DeviceNet實踐

一、項目背景在新能源電池廠的生產過程中&#xff0c;提升自動化水平對提高生產效率和產品質量至關重要。我們的生產線上&#xff0c;施耐德PLC負責整體的生產流程控制&#xff0c;采用Modbus TCP協議進行數據傳輸&#xff0c;它基于以太網&#xff0c;傳輸速度快、穩定性高&am…

Java進階3:Java集合框架、ArrayList、LinkedList、HashSet、HashMap和他們的迭代器

Java集合框架 集合框架被設計成的目標&#xff1a;高性能、高效 允許不同類型的結合&#xff0c;以類似的方式進行工作&#xff0c;有高度的互操作性 對一個集合的擴展和適應必須是簡單的兩種容器&#xff1a;集合Collection、圖Map 集合接口被分為了三種子類型&#xff1a;Lis…

筆記/使用Excel進行財務預測

文章目錄金融預測的決策與數據收集決定財務問題收集財務數據清理與合并財務數據解釋與應用預測結果使用excel進行財務回歸分析回歸預測的步驟解釋回歸結果在 Excel 中執行預測財務分析指標財務分析常用指標一覽表財務指標的相關性對競爭對手進行基準測試財務指標的趨勢分析持續…

力扣1287:有序數組中出現次數超過25%的元素

力扣1287:有序數組中出現次數超過25%的元素題目思路代碼題目 給你一個非遞減的 有序 整數數組&#xff0c;已知這個數組中恰好有一個整數&#xff0c;它的出現次數超過數組元素總數的 25%。 請你找到并返回這個整數 思路 哈希表秒了 代碼 class Solution { public:int fi…

如何用 Z.ai 生成PPT,一句話生成整套演示文檔

大家好&#xff0c;這里是K姐。 一個幫你追蹤最新AI應用的女子。 最近朋友給我分享了一個好玩的頁面截圖。 一眼看過去&#xff0c;就感覺這PPT的文字排版很有人工味。 我立馬就去試了一下&#xff0c;才發現它根本不是傳統的 PPT&#xff0c;而是一種網頁式的 Slides 。 做…

C/C++ 編程:掌握靜態庫與動態庫的編譯

在 C/C 項目開發中&#xff0c;理解并掌握如何編譯和使用庫文件是至關重要的一環。庫允許你將常用的函數和代碼模塊化&#xff0c;從而提高代碼重用性、簡化項目管理并縮短編譯時間。最常見的兩種庫類型是靜態庫 (.a) 和動態庫 (.so)。它們各有優缺點&#xff0c;適用于不同的開…

汽車安全 | 汽車安全入門

引言 汽車安全不僅僅是對汽車/車輛進行物理入侵。這只是很小且簡單的一部分。當你以攻擊者/對手的思維去看待一輛聯網汽車時&#xff0c;你關注的是整個車輛生態系統。這不僅包括它如何與外部實體通信&#xff0c;也包括它在車內如何運作。 汽車是主要的交通工具&#xff0c;…

CLIP與SIGLIP對比淺析

CLIP 和 SIGLIP 的核心區別在于損失函數的設計&#xff1a;CLIP 使用基于 softmax 的對比損失&#xff08;InfoNCE&#xff09;&#xff0c;強制正樣本在全局對比中壓倒所有負樣本&#xff0c;計算成本高且受限于負樣本數量&#xff1b;SIGLIP 改用基于 sigmoid 的二元分類損失…