使用FastAPI和React以及MongoDB构建全栈Web应用07 FastAPI实现经典三层架构
一、单文件简单实现
1.1 开发用户增删改查接口
main.py
from fastapi import FastAPI, Request, Query, HTTPException
from fastapi.responses import JSONResponse
from motor.motor_asyncio import AsyncIOMotorClient
from pydantic import BaseModel
from bson import ObjectId, errors as bson_errors
from fastapi.middleware.cors import CORSMiddleware
from typing import Optional, List# 创建FastAPI应用实例
app = FastAPI(title="用户管理API",description="提供用户的增删改查和分页功能",version="1.0.0"
)# MongoDB 连接配置
MONGODB_URL = "mongodb://zhangdapeng:zhangdapeng520@localhost:27017"
DATABASE_NAME = "blogdb"# 初始化MongoDB连接
client = AsyncIOMotorClient(MONGODB_URL)
db = client[DATABASE_NAME]
user_collection = db["users"]# 配置CORS中间件
app.add_middleware(CORSMiddleware,allow_origins=["*"], # 在生产环境中应该指定具体的域名allow_credentials=True,allow_methods=["*"],allow_headers=["*"],
)# 全局异常处理器
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):"""全局异常处理器,捕获所有未处理的异常并返回统一的错误响应"""error_msg = str(exc)if isinstance(exc, bson_errors.InvalidId):return JSONResponse(status_code=400,content={"detail": "无效的ID格式"})return JSONResponse(status_code=500,content={"detail": f"服务器内部错误: {error_msg}"})# 用户数据模型
class User(BaseModel):"""用户数据模型,定义用户的基本信息字段"""name: strage: intgender: strclass Config:json_schema_extra = {"example": {"name": "张三","age": 25,"gender": "男"}}# 用户分页响应模型
class UserResponse(BaseModel):"""用户列表分页响应模型"""total: intusers: List[dict]# API接口实现
@app.post("/users/", response_model=dict, summary="创建新用户")
async def create_user(user: User):"""创建新用户Args:user: 用户信息对象Returns:dict: 包含用户ID和信息的字典"""result = await user_collection.insert_one(user.dict())return {"_id": str(result.inserted_id), **user.dict()}@app.get("/users/", response_model=UserResponse, summary="获取用户列表")
async def get_users(page: int = Query(1, gt=0, description="页码"),page_size: int = Query(10, gt=0, le=100, description="每页数量")
):"""获取用户列表,支持分页查询Args:page: 当前页码page_size: 每页显示的数量Returns:UserResponse: 包含总数和用户列表的响应对象"""skip = (page - 1) * page_sizetotal = await user_collection.count_documents({})users = await user_collection.find().skip(skip).limit(page_size).to_list(length=page_size)# 转换ObjectId为字符串for user in users:user["_id"] = str(user["_id"])return {"total": total, "users": users}@app.put("/users/{user_id}", summary="更新用户信息")
async def update_user(user_id: str, user: User):"""更新指定用户的信息Args:user_id: 用户IDuser: 更新的用户信息Returns:dict: 更新结果信息"""result = await user_collection.update_one({"_id": ObjectId(user_id)},{"$set": user.dict()})if result.modified_count == 0:raise HTTPException(status_code=404, detail="用户不存在")return {"message": "用户更新成功"}@app.delete("/users/{user_id}", summary="删除用户")
async def delete_user(user_id: str):"""删除指定的用户Args:user_id: 要删除的用户IDReturns:dict: 删除结果信息"""result = await user_collection.delete_one({"_id": ObjectId(user_id)})if result.deleted_count == 0:raise HTTPException(status_code=404, detail="用户不存在")return {"message": "用户删除成功"}@app.get("/users/{user_id}", summary="获取单个用户信息")
async def get_user(user_id: str):"""获取指定用户的详细信息Args:user_id: 用户IDReturns:dict: 用户信息"""user = await user_collection.find_one({"_id": ObjectId(user_id)})if user is None:raise HTTPException(status_code=404, detail="用户不存在")user["_id"] = str(user["_id"])return user# 应用启动入口
if __name__ == '__main__':import uvicornuvicorn.run(app, host='0.0.0.0', port=8080)
1.2 创建React项目
package.json
{"name": "user_manager","private": true,"version": "0.0.0","type": "module","scripts": {"dev": "vite","build": "vite build","lint": "eslint .","preview": "vite preview"},"dependencies": {"react": "^18.3.1","react-dom": "^18.3.1","react-router-dom": "^7.6.0"},"devDependencies": {"@types/react": "^18.3.21","@types/react-dom": "^18.3.7","@vitejs/plugin-react": "^4.4.1","globals": "^15.15.0","vite": "^6.3.5"}
}
vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'export default defineConfig({plugins: [react()],
})
index.html
<!doctype html>
<html lang="en"><head><meta charset="UTF-8" /><link rel="icon" type="image/svg+xml" href="/vite.svg" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>FastAPI+React用户管理系统</title></head><body><div id="root"></div><script type="module" src="/src/main.jsx"></script></body>
</html>
src/main.jsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'createRoot(document.getElementById('root')).render(<StrictMode><App /></StrictMode>,
)
src/App.jsx
import {Route, BrowserRouter, Routes} from "react-router-dom";
import UserList from "./page/UserList.jsx";function App() {return (<BrowserRouter><Routes><Route path="/" element={<UserList/>}/></Routes></BrowserRouter>)
}export default App;
src/page/UserList.jsx
function UserList() {return (<div><h1>用户列表</h1><p>这里是用户列表的内容。</p></div>)
}export default UserList;
运行:
pnpm i
pnpm dev
1.3 引入zdpreact.css
src/zdpreact.css
/* 表格基础样式 */
.table {width: 100%; /* 表格宽度占满容器 */border-collapse: collapse; /* 合并表格边框 */margin-bottom: 20px; /* 底部外边距 */
}/* 表格头部单元格样式 */
.table th {background-color: #f2f2f2; /* 表头背景色 */padding: 12px; /* 内边距 */border: 1px solid #ddd; /* 边框样式 */text-align: left; /* 文字左对齐 */
}/* 表格数据单元格样式 */
.table td {padding: 12px; /* 内边距 */border: 1px solid #ddd; /* 边框样式 */
}/* 分页容器样式 */
.pagination {display: flex; /* 弹性布局 */justify-content: center; /* 水平居中 */align-items: center; /* 垂直居中 */gap: 10px; /* 按钮之间的间距 */margin-top: 20px; /* 顶部外边距 */
}/* 分页按钮基础样式 */
.pagination-button {padding: 8px 12px; /* 内边距 */border: 1px solid #ddd; /* 边框样式 */background-color: white; /* 背景色 */cursor: pointer; /* 鼠标指针样式 */border-radius: 4px; /* 圆角 */transition: all 0.3s; /* 过渡效果 */
}/* 分页按钮悬停效果 */
.pagination-button:hover:not(:disabled) {background-color: #f0f0f0; /* 悬停背景色 */border-color: #999; /* 悬停边框色 */
}/* 当前页码按钮样式 */
.pagination-button.active {background-color: #1890ff; /* 激活状态背景色 */color: white; /* 激活状态文字颜色 */border-color: #1890ff; /* 激活状态边框色 */
}/* 禁用按钮样式 */
.pagination-button:disabled {background-color: #f5f5f5; /* 禁用状态背景色 */color: #d9d9d9; /* 禁用状态文字颜色 */cursor: not-allowed; /* 禁用状态鼠标样式 */border-color: #d9d9d9; /* 禁用状态边框色 */
}/* 表格操作按钮样式 */
.table-button {padding: 4px 8px; /* 内边距 */border: 1px solid #ddd; /* 边框样式 */background-color: white; /* 背景色 */cursor: pointer; /* 鼠标指针样式 */border-radius: 4px; /* 圆角 */transition: all 0.3s; /* 过渡效果 */
}.table-button:hover {background-color: #f0f0f0; /* 悬停背景色 */border-color: #999; /* 悬停边框色 */
}/* 删除按钮特殊样式 */
.table-button.delete {color: #ff4d4f; /* 文字颜色 */border-color: #ff4d4f; /* 边框颜色 */
}.table-button.delete:hover {background-color: #fff1f0; /* 悬停背景色 */border-color: #ff7875; /* 悬停边框色 */
}/* 分页选择器样式 */
.pagination-select {padding: 8px 12px; /* 内边距 */border: 1px solid #ddd; /* 边框样式 */border-radius: 4px; /* 圆角 */background-color: white; /* 背景色 */cursor: pointer; /* 鼠标指针样式 */margin-left: 10px; /* 左边距 */outline: none; /* 移除默认轮廓 */
}.pagination-select:hover {border-color: #999; /* 悬停边框色 */
}.pagination-select:focus {border-color: #1890ff; /* 聚焦边框色 */
}/* 表格工具栏样式 */
.table-toolbar {margin-bottom: 16px;display: flex;justify-content: flex-end;
}/* 主要按钮样式 */
.table-button.primary {background-color: #1890ff;color: white;border-color: #1890ff;
}.table-button.primary:hover {background-color: #40a9ff;border-color: #40a9ff;
}/* 表单样式 */
.form {width: 100%;max-width: 400px;padding: 20px;background: white;border-radius: 8px;box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}.form-item {margin-bottom: 16px;
}.form-item label {display: block;margin-bottom: 8px;
}/* 表单项输入框和选择框通用样式 */
.form-item input,
.form-item select {width: 100%;padding: 8px 12px;border: 1px solid #d9d9d9;border-radius: 4px;font-size: 14px;line-height: 1.5;transition: all 0.3s;box-sizing: border-box;background-color: #fff;
}/* 输入框和选择框悬停样式 */
.form-item input:hover,
.form-item select:hover {border-color: #40a9ff;
}/* 输入框和选择框聚焦样式 */
.form-item input:focus,
.form-item select:focus {border-color: #40a9ff;outline: none;box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}/* 选择框特殊样式 */
.form-item select {appearance: none;background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23666' d='M6 8.5L1.5 4h9z'/%3E%3C/svg%3E");background-repeat: no-repeat;background-position: right 8px center;padding-right: 30px;
}/* 表单项标签样式 */
.form-item label {display: block;margin-bottom: 8px;color: #000000d9;font-size: 14px;
}.form-buttons {display: flex;gap: 8px;justify-content: flex-end;margin-top: 20px;
}/* 模态框遮罩层 */
.modal-overlay {position: fixed;top: 0;left: 0;right: 0;bottom: 0;background-color: rgba(0, 0, 0, 0.5);display: flex;justify-content: center;align-items: center;z-index: 1000;
}/* 模态框容器 */
.modal {background: white;border-radius: 8px;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);min-width: 400px;
}/* 模态框头部 */
.modal-header {padding: 16px 24px;border-bottom: 1px solid #f0f0f0;
}.modal-header h3 {margin: 0;color: #000000d9;font-weight: 500;font-size: 16px;line-height: 1.4;
}/* 模态框内容区 */
.modal-content {padding: 24px;
}/* 模态框中的表单样式 */
.modal-form {width: auto;max-width: none;padding: 0;background: none;box-shadow: none;
}/* 模态框按钮容器 */
.modal-buttons {display: flex;justify-content: flex-end;gap: 8px;margin-top: 24px;padding-top: 16px;border-top: 1px solid #f0f0f0;
}/* 表格容器样式 */
.table-container {width: 100%;overflow-x: auto;border-radius: 8px;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}/* 空状态样式 */
.table-empty-state,
.table-loading-state {padding: 40px 0;text-align: center;color: #666;
}.empty-icon {font-size: 48px;margin-bottom: 16px;
}.empty-text,
.loading-text {font-size: 14px;margin: 0;color: #666;
}/* 加载状态样式 */
.loading-spinner {display: inline-block;width: 30px;height: 30px;border: 3px solid #f3f3f3;border-top: 3px solid #3498db;border-radius: 50%;animation: spin 1s linear infinite;margin-bottom: 16px;
}@keyframes spin {0% { transform: rotate(0deg); }100% { transform: rotate(360deg); }
}/* 表格悬停效果 */
.table tr:hover {background-color: #f5f5f5;
}/* 确认删除弹窗样式 */
.confirm-modal-overlay {position: fixed;top: 0;left: 0;right: 0;bottom: 0;background-color: rgba(0, 0, 0, 0.65);backdrop-filter: blur(4px);display: flex;justify-content: center;align-items: center;z-index: 1000;animation: confirmModalFadeIn 0.3s ease;
}.confirm-modal {background: white;border-radius: 12px;box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);min-width: 400px;padding: 24px;transform-origin: center;animation: confirmModalScaleIn 0.3s ease;
}.confirm-modal-title {margin: 0 0 16px 0;color: #1f2937;font-size: 20px;font-weight: 600;line-height: 1.4;
}.confirm-modal-content {margin: 0 0 24px 0;color: #4b5563;font-size: 16px;line-height: 1.6;
}.confirm-modal-buttons {display: flex;justify-content: flex-end;gap: 12px;margin-top: 24px;
}.confirm-modal-button {padding: 8px 20px;border: none;border-radius: 6px;font-weight: 500;cursor: pointer;transition: all 0.2s ease;
}.confirm-modal-button-delete {color: white;background-color: #dc2626;
}.confirm-modal-button-delete:hover {background-color: #b91c1c;transform: translateY(-1px);
}.confirm-modal-button-cancel {color: #4b5563;background-color: #f3f4f6;
}.confirm-modal-button-cancel:hover {background-color: #e5e7eb;transform: translateY(-1px);
}@keyframes confirmModalFadeIn {from {opacity: 0;}to {opacity: 1;}
}@keyframes confirmModalScaleIn {from {opacity: 0;transform: scale(0.95);}to {opacity: 1;transform: scale(1);}
}
修改 src/App.jsx
import {Route, BrowserRouter, Routes} from "react-router-dom";
import "./zdpreact.css"
import UserList from "./page/UserList.jsx";function App() {return (<BrowserRouter><Routes><Route path="/" element={<UserList/>}/></Routes></BrowserRouter>)
}export default App;
1.4 引入zdpreq.js
src/api/zdpreq.js
/*** HTTP请求客户端类* 封装了基本的HTTP请求方法,提供统一的接口和错误处理*/
class ZDPReq {/*** 构造函数* @param {Object} config - 配置对象* @param {string} config.baseURL - API基础URL,默认为http://localhost:8080* @param {number} config.timeout - 请求超时时间(毫秒),默认为5000ms*/constructor(config = {}) {this.config = {baseURL: 'http://localhost:8080',timeout: 5000,...config};}/*** 创建超时Promise* 用于实现请求超时控制,当请求超过指定时间后自动reject* @param {number} timeout - 超时时间(毫秒)* @returns {Promise} 超时Promise* @private*/_createTimeoutPromise(timeout) {return new Promise((_, reject) => {setTimeout(() => {reject(new Error('请求超时'));}, timeout);});}/*** 处理HTTP响应* 统一处理响应状态和错误,将响应转换为JSON格式* @param {Response} response - fetch API的响应对象* @returns {Promise<Object>} 解析后的JSON数据* @throws {Error} 当响应状态不为2xx时抛出错误* @private*/async _processResponse(response) {if (!response.ok) {const error = await response.json().catch(() => ({}));throw new Error(error.detail || `请求失败: ${response.status}`);}return response.json();}/*** 构建完整的请求URL* 将baseURL、路径和查询参数组合成完整的URL* @param {string} url - 请求路径* @param {Object} params - URL查询参数对象* @returns {string} 完整的URL字符串* @private*/_createFullURL(url, params) {const fullURL = new URL(this.config.baseURL + url);if (params) {Object.entries(params).forEach(([key, value]) => {fullURL.searchParams.append(key, value);});}return fullURL.toString();}/*** 发送HTTP请求的核心方法* 处理请求配置、超时控制和错误处理* @param {string} method - HTTP方法(GET、POST、PUT、DELETE等)* @param {string} url - 请求路径* @param {Object} options - 请求配置选项* @param {Object} [options.data] - 请求体数据* @param {Object} [options.params] - URL查询参数* @returns {Promise<Object>} 响应数据* @throws {Error} 请求失败时抛出错误* @private*/async _request(method, url, options = {}) {const { data, params } = options;const fullURL = this._createFullURL(url, params);try {const fetchOptions = {method,headers: {'Content-Type': 'application/json'}};if (data) {fetchOptions.body = JSON.stringify(data);}const response = await Promise.race([fetch(fullURL, fetchOptions),this._createTimeoutPromise(this.config.timeout)]);return this._processResponse(response);} catch (error) {throw new Error(error.message || '请求失败');}}/*** 发送GET请求* @param {string} url - 请求路径* @param {Object} [params] - URL查询参数* @returns {Promise<Object>} 响应数据*/async get(url, params) {return this._request('GET', url, { params });}/*** 发送POST请求* @param {string} url - 请求路径* @param {Object} data - 请求体数据* @returns {Promise<Object>} 响应数据*/async post(url, data) {return this._request('POST', url, { data });}/*** 发送PUT请求* @param {string} url - 请求路径* @param {Object} data - 请求体数据* @returns {Promise<Object>} 响应数据*/async put(url, data) {return this._request('PUT', url, { data });}/*** 发送DELETE请求* @param {string} url - 请求路径* @returns {Promise<Object>} 响应数据*/async delete(url) {return this._request('DELETE', url);}
}// 创建并导出HTTP客户端实例
export const zdpreq = new ZDPReq();
1.5 封装用户API接口请求
src/api/userApi.js
import { zdpreq } from './zdpreq';// 用户API对象
export const userApi = {// 获取用户列表getPageUser: async (page = 1, pageSize = 10) => {try {return await zdpreq.get('/users/', {page,page_size: pageSize});} catch (error) {throw new Error(error.message || '获取用户列表失败');}},// 获取单个用户信息getUser: async (userId) => {try {return await zdpreq.get(`/users/${userId}`);} catch (error) {throw new Error(error.message || '获取用户信息失败');}},// 创建新用户addUser: async (userData) => {try {return await zdpreq.post('/users/', userData);} catch (error) {throw new Error(error.message || '创建用户失败');}},// 更新用户信息updateUser: async (userId, userData) => {try {return await zdpreq.put(`/users/${userId}`, userData);} catch (error) {throw new Error(error.message || '更新用户失败');}},// 删除用户deleteUser: async (userId) => {try {return await zdpreq.delete(`/users/${userId}`);} catch (error) {throw new Error(error.message || '删除用户失败');}}
};
1.6 引入表格组件
src/zdpreact/component/Table.jsx
import React from 'react';/*** 通用表格组件* @zdpreact* @description 这是一个可复用的表格组件,支持自定义列配置和数据渲染* * @param {Object[]} columns - 表格列配置数组* @param {string} columns[].key - 列的唯一标识,用于数据映射* @param {string} columns[].title - 列的标题文本* @param {function} [columns[].render] - 可选的自定义渲染函数,用于自定义单元格内容* * @param {Object[]} data - 表格数据数组,每个对象代表一行数据* @param {boolean} loading - 加载状态标志,控制是否显示加载动画*/
function Table({ columns, data, loading }) {/*** 渲染空数据状态的UI组件* @returns {JSX.Element} 返回空状态的JSX元素*/const renderEmpty = () => (<div className="table-empty-state"><div className="empty-icon">📭</div><p className="empty-text">暂无数据</p></div>);/*** 渲染加载状态的UI组件* @returns {JSX.Element} 返回加载状态的JSX元素*/const renderLoading = () => (<div className="table-loading-state"><div className="loading-spinner"></div><p className="loading-text">加载中...</p></div>);return (<div className="table-container"><table className="table">{/* 表头部分 */}<thead><tr>{columns.map(column => (<th key={column.key}>{column.title}</th>))}</tr></thead>{/* 表格主体部分 */}<tbody>{loading ? (// 加载状态显示加载动画<tr><td colSpan={columns.length}>{renderLoading()}</td></tr>) : data.length > 0 ? (// 有数据时渲染数据行data.map((row, rowIndex) => (<tr key={row.id || rowIndex}>{columns.map(column => (<td key={column.key}>{/* 如果列配置中有render函数则使用自定义渲染,否则直接显示数据 */}{column.render ? column.render(row) : row[column.key]}</td>))}</tr>))) : (// 无数据时显示空状态<tr><td colSpan={columns.length}>{renderEmpty()}</td></tr>)}</tbody></table></div>);
}export default Table;
1.7 引入分页组件
src/zdpreact/component/Pagination.jsx
import React from 'react';// 分页组件
function Pagination({ currentPage, totalPages, onPageChange, pageSize, onPageSizeChange }) {// 定义可选的每页显示数量const pageSizeOptions = [2, 5, 10, 20, 50, 100];return (<div className="pagination">{/* 上一页按钮 */}<button className="pagination-button"onClick={() => onPageChange(currentPage - 1)}disabled={currentPage === 1}>上一页</button>{/* 页码按钮 */}{Array.from({ length: totalPages }, (_, index) => (<buttonkey={index + 1}className={`pagination-button ${currentPage === index + 1 ? 'active' : ''}`}onClick={() => onPageChange(index + 1)}>{index + 1}</button>))}{/* 下一页按钮 */}<button className="pagination-button"onClick={() => onPageChange(currentPage + 1)}disabled={currentPage === totalPages}>下一页</button>{/* 每页显示数量选择器 */}<select className="pagination-select"value={pageSize}onChange={(e) => onPageSizeChange(Number(e.target.value))}>{pageSizeOptions.map(size => (<option key={size} value={size}>{size} 条/页</option>))}</select></div>);
}export default Pagination;
1.8 引入表单模态框组件
src/zdpreact/component/FormModal.jsx
import React from 'react';/*** 表单模态框组件* @param {Object} props 组件属性* @param {boolean} props.visible 是否显示模态框* @param {string} props.title 模态框标题* @param {Array} props.fields 表单字段配置* @param {Object} props.initialValues 表单初始值* @param {function} props.onSubmit 表单提交回调* @param {function} props.onCancel 取消按钮回调*/
function FormModal({ visible, title, fields, initialValues = {}, onSubmit, onCancel }) {if (!visible) return null;const handleSubmit = (e) => {e.preventDefault();const formData = new FormData(e.target);const data = {};fields.forEach(field => {const value = formData.get(field.name);data[field.name] = field.type === 'number' ? parseInt(value) : value;});onSubmit(data);};return (<div className="modal-overlay"><div className="modal"><div className="modal-header"><h3>{title}</h3></div><div className="modal-content"><form className="form modal-form" onSubmit={handleSubmit}>{fields.map(field => (<div className="form-item" key={field.name}><label>{field.label}:</label>{field.type === 'select' ? (<select name={field.name} required={field.required}defaultValue={initialValues[field.name] || ''}><option value="">{field.placeholder || '请选择'}</option>{field.options.map(option => (<option key={option.value} value={option.value}>{option.label}</option>))}</select>) : (<inputtype={field.type}name={field.name}required={field.required}placeholder={field.placeholder}defaultValue={initialValues[field.name] || ''}/>)}</div>))}<div className="modal-buttons"><button type="submit" className="table-button primary">确认</button><button type="button" className="table-button"onClick={onCancel}>取消</button></div></form></div></div></div>);
}export default FormModal;
1.9 引入确认模态框组件
src/zdpreact/component/ConfirmModal.jsx
import React from 'react';/*** 确认模态框组件* @param {Object} props 组件属性* @param {boolean} props.visible 是否显示模态框* @param {string} props.title 模态框标题* @param {string} props.content 模态框内容* @param {function} props.onConfirm 确认按钮回调* @param {function} props.onCancel 取消按钮回调*/
function ConfirmModal({ visible, title, content, onConfirm, onCancel }) {if (!visible) return null;return (<div className="confirm-modal-overlay"><div className="confirm-modal"><h3 className="confirm-modal-title">{title}</h3><p className="confirm-modal-content">{content}</p><div className="confirm-modal-buttons"><button className="confirm-modal-button confirm-modal-button-delete"onClick={onConfirm}>确认</button><button className="confirm-modal-button confirm-modal-button-cancel"onClick={onCancel}>取消</button></div></div></div>);
}export default ConfirmModal;
1.10 实现用户增删改查功能
src/page/UserList.jsx
import { useState, useEffect } from 'react';
import Pagination from '../zdpreact/component/Pagination.jsx';
import Table from '../zdpreact/component/Table.jsx';
import ConfirmModal from '../zdpreact/component/ConfirmModal.jsx';
import FormModal from '../zdpreact/component/FormModal.jsx';
import { userApi } from '../api/userApi.js';/*** 用户列表组件* 实现用户的增删改查功能,包含分页、搜索、表单验证等功能*/
function UserList() {// ================ 状态管理 ================// 用户数据相关状态const [users, setUsers] = useState([]); // 用户列表数据const [loading, setLoading] = useState(false); // 加载状态const [error, setError] = useState(null); // 错误信息// 分页相关状态const [currentPage, setCurrentPage] = useState(1); // 当前页码const [itemsPerPage, setItemsPerPage] = useState(10); // 每页条数const [totalItems, setTotalItems] = useState(0); // 总记录数// 模态框状态管理const [deleteConfirm, setDeleteConfirm] = useState({visible: false,record: null});const [addUserModal, setAddUserModal] = useState({visible: false});const [editUserModal, setEditUserModal] = useState({visible: false,record: null});// ================ 数据加载 ================/*** 加载用户列表数据* 包含错误处理和加载状态管理*/const loadUsers = async () => {try {setLoading(true);setError(null);const response = await userApi.getPageUser(currentPage, itemsPerPage);setUsers(response.users);setTotalItems(response.total);} catch (err) {setError('加载用户数据失败:' + (err.message || '未知错误'));console.error('加载用户数据失败:', err);} finally {setLoading(false);}};// 监听分页参数变化,重新加载数据useEffect(() => {loadUsers();}, [currentPage, itemsPerPage]);// ================ 事件处理 ================/*** 处理每页显示数量变化* @param {number} newSize - 新的每页显示数量*/const handlePageSizeChange = (newSize) => {setItemsPerPage(newSize);setCurrentPage(1); // 切换每页数量时重置为第一页};/*** 处理新增用户按钮点击*/const showAddUserModal = () => {setAddUserModal({ visible: true });};/*** 处理编辑按钮点击* @param {Object} record - 当前行用户数据*/const handleEdit = (record) => {setEditUserModal({visible: true,record: { ...record } // 创建数据副本,避免直接修改原数据});};/*** 显示删除确认框* @param {Object} record - 当前行用户数据*/const showDeleteConfirm = (record) => {setDeleteConfirm({visible: true,record});};// ================ 模态框操作处理 ================/*** 处理删除确认*/const handleDeleteConfirm = async () => {try {setLoading(true);await userApi.deleteUser(deleteConfirm.record._id);await loadUsers();setDeleteConfirm({visible: false,record: null});} catch (err) {setError('删除用户失败:' + (err.message || '未知错误'));console.error('删除用户失败:', err);} finally {setLoading(false);}};/*** 处理删除取消*/const handleDeleteCancel = () => {setDeleteConfirm({visible: false,record: null});};/*** 处理新增用户确认* @param {Object} formData - 表单数据*/const handleAddUserConfirm = async (formData) => {try {setLoading(true);await userApi.addUser(formData);await loadUsers();setAddUserModal({ visible: false });} catch (err) {setError('新增用户失败:' + (err.message || '未知错误'));console.error('新增用户失败:', err);} finally {setLoading(false);}};/*** 处理新增用户取消*/const handleAddUserCancel = () => {setAddUserModal({ visible: false });};/*** 处理编辑用户确认* @param {Object} formData - 表单数据*/const handleEditUserConfirm = async (formData) => {try {setLoading(true);await userApi.updateUser(editUserModal.record._id, formData);await loadUsers();setEditUserModal({visible: false,record: null});} catch (err) {setError('更新用户失败:' + (err.message || '未知错误'));console.error('更新用户失败:', err);} finally {setLoading(false);}};/*** 处理编辑用户取消*/const handleEditUserCancel = () => {setEditUserModal({visible: false,record: null});};// ================ 配置项 ================// 表格列定义const columns = [{ key: 'name', title: '姓名' },{ key: 'age', title: '年龄' },{ key: 'gender', title: '性别' },{key: 'action',title: '操作',render: (record) => (<div style={{ display: 'flex', gap: '8px' }}><buttonclassName="table-button"onClick={() => handleEdit(record)}disabled={loading}>编辑</button><buttonclassName="table-button delete"onClick={() => showDeleteConfirm(record)}disabled={loading}>删除</button></div>)}];// 用户表单字段配置const userFields = [{name: 'name',label: '姓名',type: 'text',required: true},{name: 'age',label: '年龄',type: 'number',required: true},{name: 'gender',label: '性别',type: 'select',required: true,options: [{ value: '男', label: '男' },{ value: '女', label: '女' }]}];// ================ 渲染 ================return (<div className="App"><header className="App-header"><h1>用户列表</h1>{/* 错误信息显示 */}{error && (<div className="error-message" style={{ color: 'red', margin: '10px 0' }}>{error}</div>)}{/* 操作栏 */}<div className="table-toolbar"><buttonclassName="table-button primary"onClick={showAddUserModal}disabled={loading}>新增用户</button></div>{/* 用户列表表格 */}<Tablecolumns={columns}data={users}loading={loading}/>{/* 分页组件 */}<PaginationcurrentPage={currentPage}totalPages={Math.ceil(totalItems / itemsPerPage)}onPageChange={setCurrentPage}pageSize={itemsPerPage}onPageSizeChange={handlePageSizeChange}/>{/* 删除确认框 */}<ConfirmModalvisible={deleteConfirm.visible}title="确认删除"content={`确定要删除用户 "${deleteConfirm.record?.name}" 吗?`}onConfirm={handleDeleteConfirm}onCancel={handleDeleteCancel}/>{/* 新增用户表单 */}<FormModalvisible={addUserModal.visible}title="新增用户"fields={userFields}onSubmit={handleAddUserConfirm}onCancel={handleAddUserCancel}/>{/* 编辑用户表单 */}<FormModalvisible={editUserModal.visible}title="编辑用户"fields={userFields}initialValues={editUserModal.record}onSubmit={handleEditUserConfirm}onCancel={handleEditUserCancel}/></header></div>);
}export default UserList;
二、经典三层架构
2.1 MongoDB数据库依赖
app/dependency/database.py
from typing import AsyncGenerator
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase
from fastapi import Depends# MongoDB配置
MONGO_URL = "mongodb://zhangdapeng:zhangdapeng520@localhost:27017/"
DATABASE_NAME = "users_db"# 创建MongoDB客户端
client = AsyncIOMotorClient(MONGO_URL)async def get_database() -> AsyncGenerator[AsyncIOMotorDatabase, None]:"""获取数据库连接的依赖函数"""try:db = client[DATABASE_NAME]yield dbfinally:# 在这里可以添加清理代码如果需要pass# 获取用户集合的依赖函数
async def get_user_collection():"""获取用户集合的依赖函数"""db = client[DATABASE_NAME]return db["users"]
2.2 跨域中间件
app/middleware/cors_middleware.py
from fastapi.middleware.cors import CORSMiddlewaredef setup_cors_middleware(app):"""配置CORS中间件"""app.add_middleware(CORSMiddleware,allow_origins=["*"],allow_credentials=True,allow_methods=["*"],allow_headers=["*"],
2.3 全局异常处理
app/middleware/error_handler.py
from fastapi import Request
from fastapi.responses import JSONResponse
from bson import errors as bson_errorsdef setup_error_handler(app):"""配置全局异常处理器"""@app.exception_handler(Exception)async def global_exception_handler(request: Request, exc: Exception):error_msg = str(exc)if isinstance(exc, bson_errors.InvalidId):return JSONResponse(status_code=400,content={"message": "Invalid ObjectId"},)return JSONResponse(status_code=500,content={"message": f"服务器内部错误: {error_msg}"},)
2.4 初始化所有中间件
app/middleware/__init__.py
from .cors_middleware import setup_cors_middleware
from .error_handler import setup_error_handlerdef setup_middlewares(app):"""初始化所有中间件"""setup_cors_middleware(app)setup_error_handler(app)
2.5 用户请求参数
app/schema/user_model.py
from pydantic import BaseModel
from typing import Listclass User(BaseModel):name: strage: intgender: strclass Config:json_schema_extra = {"example": {"name": "张三","age": 30,"gender": "男"}}class UserResponse(BaseModel):total: intusers: List[dict]
2.6 用户dao层
app/dao/user_dao.py
from bson import ObjectId
from ..dependency.database import get_user_collectionclass UserDAO:def __init__(self):self.collection = Noneasync def initialize(self):"""初始化数据库集合"""self.collection = await get_user_collection()async def create_user(self, user_dict: dict) -> str:"""创建用户"""await self.initialize()result = await self.collection.insert_one(user_dict)return str(result.inserted_id)async def get_users(self, skip: int, limit: int) -> tuple[list, int]:"""获取用户列表"""await self.initialize()users = await self.collection.find().skip(skip).limit(limit).to_list(length=limit)total = await self.collection.count_documents({})for user in users:user["_id"] = str(user["_id"])return users, totalasync def update_user(self, user_id: str, user_dict: dict) -> bool:"""更新用户"""await self.initialize()result = await self.collection.update_one({"_id": ObjectId(user_id)},{"$set": user_dict})return result.modified_count > 0async def delete_user(self, user_id: str) -> bool:"""删除用户"""await self.initialize()result = await self.collection.delete_one({"_id": ObjectId(user_id)})return result.deleted_count > 0async def get_user(self, user_id: str) -> dict:"""获取单个用户"""await self.initialize()user = await self.collection.find_one({"_id": ObjectId(user_id)})if user:user["_id"] = str(user["_id"])return user
2.7 用户service层
app/service/user_service.py
from fastapi import HTTPException
from ..dao.user_dao import UserDAO
from ..schema.user_model import Userclass UserService:def __init__(self):self.user_dao = UserDAO()async def create_user(self, user: User) -> dict:"""创建用户服务"""user_dict = user.dict()user_id = await self.user_dao.create_user(user_dict)return {"message": "用户创建成功", "id": user_id}async def get_users(self, skip: int, page_size: int) -> dict:"""获取用户列表服务"""users, total = await self.user_dao.get_users(skip, page_size)return {"total": total, "users": users}async def update_user(self, user_id: str, user: User) -> dict:"""更新用户服务"""user_dict = user.dict()if not await self.user_dao.update_user(user_id, user_dict):raise HTTPException(status_code=404, detail="用户不存在")return {"message": "用户更新成功"}async def delete_user(self, user_id: str) -> dict:"""删除用户服务"""if not await self.user_dao.delete_user(user_id):raise HTTPException(status_code=404, detail="用户不存在")return {"message": "用户删除成功"}async def get_user(self, user_id: str) -> User:"""获取单个用户服务"""user = await self.user_dao.get_user(user_id)if user is None:raise HTTPException(status_code=404, detail="用户不存在")return User(**user)
2.8 用户controller层
app/controller/user_controller.py
from fastapi import APIRouter, Query
from ..schema.user_model import User, UserResponse
from ..service.user_service import UserServicerouter = APIRouter(tags=["用户管理"])
user_service = UserService()@router.post("/users/", response_model=dict, summary="创建新用户")
async def create_user(user: User):return await user_service.create_user(user)@router.get("/users/", response_model=UserResponse, summary="获取用户列表")
async def get_users(page: int = Query(1, ge=1),page_size: int = Query(10, ge=1)):skip = (page - 1) * page_sizereturn await user_service.get_users(skip, page_size)@router.put("/users/{id}", response_model=dict, summary="更新用户信息")
async def update_user(id: str, user: User):return await user_service.update_user(id, user)@router.delete("/users/{id}", response_model=dict, summary="删除用户")
async def delete_user(id: str):return await user_service.delete_user(id)@router.get("/users/{id}", response_model=User, summary="获取单个用户信息")
async def get_user(id: str):return await user_service.get_user(id)
2.9 入口程序
main.py
from fastapi import FastAPI
from app.controller.user_controller import router as user_router
from app.middleware import setup_middlewares# 创建app
app = FastAPI(title="用户管理API",description="提供用户增删改查和分页查询功能",version="1.0.0",
)# 初始化中间件
setup_middlewares(app)# 注册路由
app.include_router(user_router, tags=["用户管理"])if __name__ == '__main__':import uvicornuvicorn.run(app, host="127.0.0.1", port=8080)
总结
源滚滚编程提供全套的PDF文档,配套源代码,录播课,私教课和直播课,关注并私信我咨询获取。
相关文章:
使用FastAPI和React以及MongoDB构建全栈Web应用07 FastAPI实现经典三层架构
一、单文件简单实现 1.1 开发用户增删改查接口 main.py from fastapi import FastAPI, Request, Query, HTTPException from fastapi.responses import JSONResponse from motor.motor_asyncio import AsyncIOMotorClient from pydantic import BaseModel from bson import …...
《设计模式之禅》笔记
:::info 💡 根据 遗忘曲线:如果没有记录和回顾,6天后便会忘记75%的内容 读书笔记正是帮助你记录和回顾的工具,不必拘泥于形式,其核心是:记录、翻看、思考::: 书名设计模式之禅作者秦小波状态已读完简介深刻…...
JavaScript 循环语句全解析:选择最适合的遍历方式
循环是编程中处理重复任务的核心工具。JavaScript 提供了多种循环语句,每种都有其适用场景和独特优势。本文将深入解析 JavaScript 的 6 种核心循环语句,通过实际示例帮助你精准选择合适的循环方案。 一、基础循环三剑客 1. for 循环 经典索引控制 ja…...
远程服务器pycharm运行tensorboard显示训练轮次图
本文仅针对远程服务器的情况 首先在远程服务器端 首先打开xshell,然后激活自己的虚拟环境 baekee这是我的! conda activate baekee然后cd进去你运行的文件所在的目录 cd /tmp/pycharm_project_732这个项目也是我的! 然后一个很重要的事情…...
Nginx 使用 Keepalived 搭建 nginx 高可用
一、环境准备 两台装有 nginx 的 CentOS 虚拟机。 [rootnginx1 ~]# echo "192.168.40.81 say Hello" > /usr/local/nginx/html/index.html [rootnginx2 ~]# echo "192.168.40.82 say Hello" > /usr/local/nginx/html/index.html 二、原理 Keepaliv…...
A1062 PAT甲级JAVA题解 Talent and Virtue
About 900 years ago, a Chinese philosopher Sima Guang wrote a history book in which he talked about peoples talent and virtue. According to his theory, a man being outstanding in both talent and virtue must be a "sage(圣人)"…...
数据指标和数据标签
数据指标和数据标签是数据管理与分析中的两个重要概念,它们在用途、形式和应用场景上有显著区别。以下是两者的详细对比: 1. 核心定义 维度数据指标(Data Metrics)数据标签(Data Tags/Labels)定义量化衡量…...
常见的排序算法(Java版)简单易懂好上手!!
排序 “排序”顾名思义就是把乱序的变成有序的,就像我们玩斗地主这种牌类游戏时,我们拿到牌都会将牌给排序一下,更好的在对局中方便思考和观察,我们排序算法也亦是如此。 文章目录 排序一、冒泡排序二、选择排序三、插入排序四、…...
用统计零花钱的例子解释:Shuffle 是啥?
举个栗子 🌰:统计全班同学的零花钱总和 假设你是班长,全班有 4个小组,每个小组记录了自己的零花钱情况: 第1组:张三(5元)、李四(3元)、张三(2元) 第2组:王五(4元)、张三(1元)、李四(2元) …...
Kafka topic 中的 partition 数据倾斜问题
在 Kafka 中,如果一个 Topic 有多个 Partition,但这些 Partition 中的消息数量或流量分布不均衡,就会出现 数据倾斜(Data Skew) 的问题。 ✅ 什么是数据倾斜? 数据倾斜指的是: 某些 Partitio…...
Python基础总结(十)之函数
Python函数 函数是Python中也是非常重要的,函数是带名字的代码块,用于完成具体的工作。要执行函数定义的特定任务,可调用该函数。 一、函数的定义 函数的定义要使用def关键字,def后面紧跟函数名,缩进的为函数的代码…...
macOS 15 (Sequoia) 解除Gatekeeper限制
macOS 15 (Sequoia) 解除Gatekeeper限制指南 问题描述 在macOS 15中执行sudo spctl --global-disable命令后,系统提示: Globally disabling the assessment system needs to be confirmed in System Settings 但隐私与安全性界面未显示"任何来源&…...
【Flask开发踩坑实录】pip 安装报错:“No matching distribution found” 的根本原因及解决方案!
关键词:pip 报错、镜像源问题、flask-socketio、Python开发环境、安装失败 作者:未名编程 | 更新时间:2025.05.11 📌 引言:一场莫名其妙的 pip 安装失败 最近在开发一个基于 Flask 的图像检索网站时&#…...
50.辐射抗扰RS和传导抗扰CS测试环境和干扰特征分析
辐射抗扰RS和传到抗扰CS测试环境和干扰特征分析 1. 辐射抗扰RS2. 传导抗扰CS 1. 辐射抗扰RS 辐射抗扰RS考察对外界电磁场干扰得抗扰能力,测试频段为80MHz~2000MHz,用1KHz得正弦波进行调幅,在电波暗室内进行。测试标准:IEC 61000-…...
零基础玩转sqlmap - 从入门到摸清数据库
sqlmap 包下载链接:https://pan.quark.cn/s/a6ab2586f77e 基本操作 最简单的用法:sqlmap -u "网址" - 直接测试这个网址有没有SQL注入漏洞 带参数的测试:如果网址后面有参数,比如 id1,sqlmap会自动检测 指…...
AI面经总结-试读
写在前面 该面经于2022年秋招上岸后耗时一个半月整理完毕,目前涵盖Python、基础理论、分类与聚类、降维、支持向量机SVM、贝叶斯|决策树、KNN、Boosting&Bagging、回归、代价函数与损失函数、激活函数、优化函数、正则优化、初始化与归一化、卷积、池化、传统图…...
python打卡day22@浙大疏锦行
复习日 仔细回顾一下之前21天的内容,没跟上进度的同学补一下进度。 作业: 自行学习参考如何使用kaggle平台,写下使用注意点,并对下述比赛提交代码 一、数据预处理 import pandas as pd import numpy as np import matplo…...
网络安全设备配置与管理-实验5-p150虚拟防火墙配置
实验5-p150虚拟防火墙配置 做不出来可以把项目删掉再新建。 实验六多加几步配置静态路由表就行。 文章目录 实验5-p150虚拟防火墙配置1. 实验目的2. 实验任务3. 实验设备4. 实验拓扑图和设备接口5. 实验命令与步骤1. 连线与配置2. 实验验证 思考题 1. 实验目的 通过该实验掌握…...
数值运算的误差估计
数值运算的误差估计 设两个近似数 x 1 ∗ x_1^* x1∗与 x 2 ∗ x_2^* x2∗的误差限分别为 ε ( x 1 ∗ ) \varepsilon(x_{1}^{*}) ε(x1∗)和 ε ( x 2 ∗ ) \varepsilon(x_{2}^{*}) ε(x2∗) 误差限满足一下运算法则: 和差运算的误差限: 设 y …...
HCIP-BGP实验一
一:拓扑图 二:需求分析: 保证R1-R5的环回地址相互能够通讯。 分析; 1.IP的配置 2.R2-R4完成IGP配置,配置OSPF 3.完成BGP配置。 4.优化配置,包括下一跳的选择,IBGP对等体建邻的IP地址。 三…...
linux内核pinctrl/gpio子系统驱动笔记
目录 一、简单介绍二、主要源码文件和目录gpio子系统pinctrl子系统两个子系统之间的关系设备树例子 三、主要的数据结构gpio子系统pinctrl子系统 四、驱动初始化流程五、难点说明 一、简单介绍 GPIO子系统: Linux GPIO子系统是Linux内核中负责处理GPIO(通用输入输出…...
Qt—多线程基础
一、QThread 1.为什么使用多线程 在默认情况下,Qt使用的是单线程,当你启动一个 Qt 应用程序时,它会运行在一个单一的主线程(也被称为 GUI 线程)中。这个主线程负责处理所有的 GUI 事件和界面渲染。 但在一些其他情况下…...
HTML5表格语法格式详解
HTML5 表格的基本结构 HTML5 表格由 <table> 标签定义,表格中的每一行由 <tr> 标签定义,表头单元格由 <th> 标签定义,数据单元格由 <td> 标签定义。表格的基本结构如下: <table><tr><th…...
《Go小技巧易错点100例》第三十三篇
Validator自定义校验规则 Go语言中广泛使用的validator库支持通过结构体标签定义校验规则。当内置规则无法满足需求时,我们可以轻松扩展自定义校验逻辑。 示例场景:验证用户年龄是否成年(≥18岁) type User struct {Age in…...
Spring AI(3)——Chat Memory
Chat Memory介绍 大型语言模型(LLM)是无状态的,这意味着它们不保留关于以前互动的信息。为了解决这个问题,Spring AI提供了Chat Memory(聊天记忆)功能。通过Chat Memory,用户可以实现在与LLM的…...
双向循环神经网络(Bi-RNN)详解
双向循环神经网络(Bidirectional Recurrent Neural Network, Bi-RNN)是一种能够同时利用序列数据过去和未来信息的循环神经网络架构,在许多序列建模任务中表现出色。 1. Bi-RNN基本概念 1.1 核心思想 Bi-RNN通过组合两个独立的RNN层来工作: 前向RNN&…...
【Bluedroid】HID DEVICE 连接的源码分析
本文分析Android Bluetooth协议栈中HID device设备连接流程的完整实现,从应用层接口到协议栈底层的交互细节。通过关键函数(如connect()、BTA_HdConnect()、HID_DevConnect()等)的代码解析,重点关注btif、bta、HID协议栈三层的协同机制,揭示BTA_HD_CONN_STATE_EVT事件传递…...
第二十一周:项目开发中遇到的相关问题(二)
本周接着介绍本次新闻项目中遇到的一些问题。首先谈谈Controller层中的请求路径问题(RequestMapping),RequestMapping注解是Spring框架中用于处理HTTP请求映射的核心注解,它可以将HTTP请求映射到具体的控制器方法上。 1.关于它的基本作用&…...
深度解析大模型学习率:优化策略与挑战
大模型超参数Learning Rate的深度学习 学习率(Learning Rate)是机器学习和深度学习中最核心的超参数之一,尤其在训练大规模语言模型(LLMs)时,其设置直接影响模型的收敛速度、训练稳定性及最终性能。以下从多维度详细解析学习率的定义、作用、挑战及优化策略。 一、学习率…...
云计算-容器云-KubeVirt 运维
KubeVirt 运维:创建 VM 使用提供的镜像在 kubevirt 命名空间下创建一台 VM,名称为 exam,指定 VM 的内存、CPU、网卡和磁盘等配置。 [rootk8s-master-node1 ~]# kubectl explain kubevirt.spec. --recursive |grep useuseEmulation <boo…...
C++内存管理详解
目录 1.C/C中的内存 2.C内存管理 2.1C语言内存管理 2.2new和delete 2.2.1概念及定义 2.2.2自定义类型内存管理 2.2.3 delete与delete[ ] 1.C/C中的内存 在C/C中编译器会对不同的代码进行内存分配,给代码的内存区主要分为栈区、堆区、数据段(静态区)、代码段(…...
二叉搜索树讲解
1. 二叉搜索树的概念 二叉搜索树又称二叉排序树,它或者是一颗空树,或者是具有以下性质的二叉树: 1. 若它的左子树不为空,则左子树上的所有结点的值都小于等于根节点的值。 2. 若它的右子树不为空,则右子树上的所有结…...
MySQL 索引设计宝典:原理、原则与实战案例深度解析
目录 前言第一章:索引设计的基础原则 (知其然,更要知其所以然)第二章:实战案例:电商订单系统的索引设计第三章:索引设计的实践流程总结结语 🌟我的其他文章也讲解的比较有趣😁,如果喜…...
如何租用服务器并通过ssh连接远程服务器终端
这里我使用的是智算云扉 没有打广告 但确实很便宜 还有二十小时免费额度 链接如下 注册之后 租用新实例 选择操作系统 选择显卡型号 点击租用 选择计费方式 选择镜像 如果跑深度学习的话 就选项目对应的torch版本 没有的话 就创建纯净的cuda 自己安装 点击创建实例 创建之后 …...
TikTok 账号运营干货:AI 驱动优化
TikTok 账号运营是一项需要全方位精心雕琢的工作。首先,账号资料的打造至关重要,务必保证完整且富有吸引力。头像要清晰醒目,能够直观传达账号的核心特色;昵称需简洁易记,方便用户快速识别与记忆;简介则要精…...
Redis 分布式锁
什么是分布式锁 在一个分布式的系统中, 也会涉及到多个节点访问同一个公共资源的情况. 此时就需要通过 锁 来做互斥控制, 避免出现类似于 "线程安全" 的问题 而 java 的 synchronized 或者 C 的 std::mutex, 这样的锁都是只能在当前进程中生效, 在分布式的这种多个进…...
Redis爆肝总结
一、基础 1.介绍 本质上是一个Key-Value类型的内存数据库,数据的加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。 速度快的根本原因 纯内存操作,性能非常出色,每秒可以处理超过10万次读写操作&a…...
Qt模块化架构设计教程 -- 轻松上手插件开发
概述 在软件开发领域,随着项目的增长和需求的变化,保持代码的可维护性和扩展性变得尤为重要。一个有效的解决方案是采用模块化架构,尤其是利用插件系统来增强应用的功能性和灵活性。Qt框架提供了一套强大的插件机制,可以帮助开发者轻松实现这种架构。 模块化与插件系统 模…...
[项目总结] 抽奖系统项目技术应用总结
🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏: 🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 🍕 Collection与…...
【运维】基于Python打造分布式系统日志聚合与分析利器
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在分布式系统中,日志数据分散在多个节点,管理和分析变得复杂。本文详细介绍如何基于Python开发一个日志聚合与分析工具,结合Logstash和F…...
MySql(基础)
表名建议用 反引号 包裹(尤其是表名包含特殊字符或保留字时),但如果表名是普通字符串(如 user),可以省略。 注释(COMMENT 姓名) 数据库 1.查看数据库:show databases…...
怎样选择成长股 读书笔记(一)
文章目录 第一章 成长型投资的困惑一、市场不可预测性的本质困惑二、成长股的筛选悖论三、管理层评估的认知盲区四、长期持有与估值波动的博弈五、实践中的认知升级路径总结:破解困惑的行动框架 第二章 如何阅读应计制利润表一、应计制利润表的本质与核心原则1. 权责…...
系统思考:个人与团队成长
四年前,我交付的系统思考项目,今天学员的反馈依然深深触动了我。 我常常感叹,系统思考不仅仅是一场培训,更像是一场持续的“修炼”。在这条修炼之路上,最珍贵的,便是有志同道合的伙伴们一路同行࿰…...
并行发起http请求
1. 使用 axios Promise.all <template><input type"file" multiple change"handleFileUpload" /> </template><script> import axios from axios;export default {methods: {async handleFileUpload(event) {const files event…...
【数据结构入门训练DAY-31】组合的输出
本文介绍了如何使用深度优先搜索(DFS)算法解决数的组合问题。题目要求从1到n的自然数中选取r个数,输出所有可能的组合,并按字典顺序排列。文章详细描述了解题思路,包括建立数组存储数字、使用DFS递归处理候选数、以及如…...
leetcode0815. 公交路线-hard
1 题目: 公交路线 官方标定难度:难 给你一个数组 routes ,表示一系列公交线路,其中每个 routes[i] 表示一条公交线路,第 i 辆公交车将会在上面循环行驶。 例如,路线 routes[0] [1, 5, 7] 表示第 0 辆公…...
花朵识别系统Python+深度学习+卷积神经网络算法+TensorFlow+人工智能
一、介绍 花朵识别系统。本系统采用Python作为主要编程语言,基于TensorFlow搭建ResNet50卷积神经网络算法模型,并基于前期收集到的5种常见的花朵数据集(向日葵、玫瑰、蒲公英、郁金香、菊花)进行处理后进行模型训练,最…...
LLM 论文精读(四)LLM Post-Training: A Deep Dive into Reasoning Large Language Models
这是一篇2025年发表在arxiv中的LLM领域论文,是一篇非常全面的综述类论文,介绍了当前主流的强化学习方法在LLM上的应用,文章内容比较长,但建议LLM方面的从业人员反复认真阅读。 写在最前面 为了方便你的阅读,以下几点的…...
网址为 http://xxx:xxxx/的网页可能暂时无法连接,或者它已永久性地移动到了新网址
这是由于浏览器默认的非安全端口所导致的,所谓非安全端口,就是浏览器出于安全问题,会禁止一些网络浏览向外的端口。 避免使用6000,6666这样的端口 6000-7000有很多都不行,所以尽量避免使用这个区间 还有在云服务器中,…...
【C++】16.继承
C三大特性:封装,继承,多态 在前面的章节中,我们讲过了封装,也就是通过类和访问修饰符来进行封装。 接下来我们就来认识一下新的特性——继承 1. 继承的概念及定义 1.1 继承的概念 继承(inheritance)机制是面向对…...