当前位置: 首页 > news >正文

基于 React + Go + PostgreSQL + Redis 的管理系统开发框架

siqian-admin 开发框架

https://github.com/KakaCheng2010/siqian-admin.git

基于 React + Go + PostgreSQL + Redis 的管理系统开发框架

🚀 技术栈

后端

  • Go 1.21+ - 主要后端语言
  • Gin - Web框架
  • GORM - ORM框架
  • PostgreSQL - 主数据库
  • Redis - 缓存和会话存储
  • JWT - 身份认证
  • Viper - 配置管理

前端

  • React 18 - 前端框架
  • TypeScript - 类型安全
  • Ant Design - UI组件库
  • React Router - 路由管理
  • Axios - HTTP客户端
  • Zustand - 状态管理

📋 功能模块

系统管理模块 (sys/)

  • 用户管理 - 用户CRUD、状态管理、角色分配
  • 组织管理 - 树形组织结构、层级管理
  • 角色管理 - 角色定义、权限分配
  • 菜单管理 - 动态菜单、权限控制
  • 字典管理 - 系统字典、数据字典项

认证模块 (auth/)

  • 用户认证 - JWT令牌认证
  • 权限控制 - 基于角色的访问控制
  • 会话管理 - Redis会话存储

🏗️ 项目结构

siqian-admin/
├── backend/                 # Go后端
│   ├── cmd/                # 应用入口
│   │   └── main.go
│   ├── internal/           # 内部包
│   │   ├── api/           # API层
│   │   ├── config/        # 配置管理
│   │   ├── database/      # 数据库连接
│   │   ├── middleware/    # 中间件
│   │   ├── router/        # 路由配置
│   │   ├── service/       # 业务逻辑层
│   │   ├── sys/          # 系统管理模块
│   │   │   ├── api/       # 系统管理API
│   │   │   ├── model/     # 数据模型
│   │   │   └── service/   # 业务逻辑
│   │   └── utils/         # 工具函数
│   ├── config.yaml        # 配置文件
│   ├── Dockerfile
│   ├── go.mod
│   └── go.sum
├── frontend/              # React前端
│   ├── src/
│   │   ├── components/    # 公共组件
│   │   ├── pages/         # 页面组件
│   │   ├── services/      # API服务
│   │   ├── store/         # 状态管理
│   │   ├── router/        # 路由配置
│   │   ├── hooks/         # 自定义Hooks
│   │   └── utils/         # 工具函数
│   ├── package.json
│   └── Dockerfile
└── README.md

🚀 快速开始

环境要求

  • Go 1.21+
  • Node.js 18+
  • Docker & Docker Compose
  • Git

配置文件

数据库文件位于 backend/scripts/siqian-admin.sql
项目配置文件位于 backend/config.yaml

server:port: "8080"        # 服务器端口mode: "debug"       # 运行模式database:host: "localhost"   # 数据库主机port: 5432          # 数据库端口user: "postgres"    # 数据库用户名password: "password" # 数据库密码dbname: "go_admin"  # 数据库名称sslmode: "disable"  # SSL模式redis:host: "localhost"   # Redis主机port: 6379         # Redis端口password: ""       # Redis密码db: 0             # Redis数据库jwt:secret: "your-secret-key" # JWT密钥expire_time: 24           # 过期时间(小时)

启动项目

# 1. 启动数据库和Redis
docker-compose up -d postgres redis# 2. 启动后端
cd backend
go mod tidy
go run cmd/main.go# 3. 启动前端
cd frontend
npm install
npm run dev

访问地址

  • 前端: http://localhost:3000
  • 后端API: http://localhost:8080
  • 默认账户: admin / 123456

🛠️ 新功能开发指南

完整开发流程:从后端到前端

以添加"产品管理"模块为例,展示完整的开发流程:

1. 后端开发

步骤1:创建数据模型

// backend/internal/sys/model/product.go
package modelimport ("time""gorm.io/gorm"
)type Product struct {ID          uint           `json:"id" gorm:"primaryKey"`Name        string         `json:"name" gorm:"not null" binding:"required"`Code        string         `json:"code" gorm:"uniqueIndex;not null" binding:"required"`Price       float64        `json:"price" gorm:"not null"`Category    string         `json:"category"`Status      int            `json:"status" gorm:"default:1"` // 1:正常 0:禁用Description string         `json:"description"`CreatedAt   time.Time      `json:"created_at"`UpdatedAt   time.Time      `json:"updated_at"`DeletedAt   gorm.DeletedAt `json:"-" gorm:"index"`
}func (Product) TableName() string {return "sys_products"
}

步骤2:创建业务逻辑层

// backend/internal/sys/service/product.go
package serviceimport ("siqian-admin/internal/sys/model""gorm.io/gorm"
)type ProductService struct {db *gorm.DB
}func NewProductService(db *gorm.DB) *ProductService {return &ProductService{db: db}
}func (s *ProductService) CreateProduct(product *model.Product) error {return s.db.Create(product).Error
}func (s *ProductService) GetProductByID(id uint) (*model.Product, error) {var product model.Producterr := s.db.First(&product, id).Errorreturn &product, err
}func (s *ProductService) UpdateProduct(product *model.Product) error {return s.db.Save(product).Error
}func (s *ProductService) DeleteProduct(id uint) error {return s.db.Delete(&model.Product{}, id).Error
}func (s *ProductService) ListProducts(page, pageSize int) ([]model.Product, int64, error) {var products []model.Productvar total int64offset := (page - 1) * pageSizeerr := s.db.Model(&model.Product{}).Count(&total).Errorif err != nil {return nil, 0, err}err = s.db.Offset(offset).Limit(pageSize).Find(&products).Errorreturn products, total, err
}

步骤3:创建API层

// backend/internal/sys/api/product.go
package apiimport ("siqian-admin/internal/sys/model""siqian-admin/internal/sys/service""net/http""strconv""github.com/gin-gonic/gin"
)type ProductHandler struct {productService *service.ProductService
}func NewProductHandler(productService *service.ProductService) *ProductHandler {return &ProductHandler{productService: productService}
}type CreateProductRequest struct {Name        string  `json:"name" binding:"required"`Code        string  `json:"code" binding:"required"`Price       float64 `json:"price" binding:"required"`Category    string  `json:"category"`Status      int     `json:"status"`Description string  `json:"description"`
}func (h *ProductHandler) CreateProduct(c *gin.Context) {var req CreateProductRequestif err := c.ShouldBindJSON(&req); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}product := &model.Product{Name:        req.Name,Code:        req.Code,Price:       req.Price,Category:    req.Category,Status:      req.Status,Description: req.Description,}if err := h.productService.CreateProduct(product); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}c.JSON(http.StatusCreated, gin.H{"message": "产品创建成功", "product": product})
}func (h *ProductHandler) GetProduct(c *gin.Context) {id, err := strconv.ParseUint(c.Param("id"), 10, 32)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": "无效的产品ID"})return}product, err := h.productService.GetProductByID(uint(id))if err != nil {c.JSON(http.StatusNotFound, gin.H{"error": "产品不存在"})return}c.JSON(http.StatusOK, product)
}func (h *ProductHandler) ListProducts(c *gin.Context) {page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))products, total, err := h.productService.ListProducts(page, pageSize)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": "获取产品列表失败"})return}c.JSON(http.StatusOK, gin.H{"products":  products,"total":     total,"page":      page,"page_size": pageSize,})
}func (h *ProductHandler) UpdateProduct(c *gin.Context) {id, err := strconv.ParseUint(c.Param("id"), 10, 32)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": "无效的产品ID"})return}var req CreateProductRequestif err := c.ShouldBindJSON(&req); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}product, err := h.productService.GetProductByID(uint(id))if err != nil {c.JSON(http.StatusNotFound, gin.H{"error": "产品不存在"})return}product.Name = req.Nameproduct.Code = req.Codeproduct.Price = req.Priceproduct.Category = req.Categoryproduct.Status = req.Statusproduct.Description = req.Descriptionif err := h.productService.UpdateProduct(product); err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": "更新失败"})return}c.JSON(http.StatusOK, gin.H{"message": "更新成功", "product": product})
}func (h *ProductHandler) DeleteProduct(c *gin.Context) {id, err := strconv.ParseUint(c.Param("id"), 10, 32)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": "无效的产品ID"})return}if err := h.productService.DeleteProduct(uint(id)); err != nil {c.JSON(http.StatusInternalServerError, gin.H{"error": "删除失败"})return}c.JSON(http.StatusOK, gin.H{"message": "删除成功"})
}

步骤4:注册路由

// backend/internal/router/router.go
// 在路由配置中添加产品管理路由
func SetupRoutes(r *gin.Engine, db *gorm.DB, rdb *redis.Client) {// ... 其他路由配置// 产品管理productService := sysservice.NewProductService(db)productHandler := sysapi.NewProductHandler(productService)products := authorized.Group("/products"){products.POST("", productHandler.CreateProduct)products.GET("", productHandler.ListProducts)products.GET("/:id", productHandler.GetProduct)products.PUT("/:id", productHandler.UpdateProduct)products.DELETE("/:id", productHandler.DeleteProduct)}
}
2. 前端开发

步骤1:创建API服务

// frontend/src/services/product.ts
import api from './api';export interface Product {id: number;name: string;code: string;price: number;category: string;status: number;description: string;created_at: string;updated_at: string;
}export interface CreateProductRequest {name: string;code: string;price: number;category: string;status: number;description: string;
}export interface ProductListResponse {products: Product[];total: number;page: number;page_size: number;
}export const productService = {getProducts: async (page = 1, pageSize = 10): Promise<ProductListResponse> => {const response = await api.get('/products', {params: { page, page_size: pageSize }});return response.data;},getProduct: async (id: string): Promise<Product> => {const response = await api.get(`/products/${id}`);return response.data;},createProduct: async (data: CreateProductRequest): Promise<Product> => {const response = await api.post('/products', data);return response.data.product;},updateProduct: async (id: string, data: CreateProductRequest): Promise<Product> => {const response = await api.put(`/products/${id}`, data);return response.data.product;},deleteProduct: async (id: string): Promise<void> => {await api.delete(`/products/${id}`);},
};

步骤2:创建页面组件

// frontend/src/pages/product/ProductManagement.tsx
import React, { useState, useEffect } from 'react';
import { Card, Table, Button, Space, Tag, message, Popconfirm, Modal, Form, Input, InputNumber, Select } from 'antd';
import { PlusOutlined, EditOutlined, DeleteOutlined, ReloadOutlined } from '@ant-design/icons';
import { productService, Product, CreateProductRequest } from '../../services/product';const ProductManagement: React.FC = () => {const [products, setProducts] = useState<Product[]>([]);const [loading, setLoading] = useState(false);const [createVisible, setCreateVisible] = useState(false);const [editVisible, setEditVisible] = useState(false);const [editingProduct, setEditingProduct] = useState<Product | null>(null);const [form] = Form.useForm<CreateProductRequest>();const [editForm] = Form.useForm<CreateProductRequest>();// 加载产品列表const loadProducts = async () => {setLoading(true);try {const data = await productService.getProducts();setProducts(data.products);} catch (error) {message.error('加载产品列表失败');} finally {setLoading(false);}};useEffect(() => {loadProducts();}, []);// 打开新增弹窗const openCreateModal = () => {form.resetFields();form.setFieldsValue({ status: 1 });setCreateVisible(true);};// 提交新增const handleCreate = async () => {try {const values = await form.validateFields();await productService.createProduct(values);message.success('创建成功');setCreateVisible(false);loadProducts();} catch (error: any) {if (error?.errorFields) return;message.error(error?.response?.data?.error || '创建失败');}};// 打开编辑弹窗const openEditModal = (product: Product) => {setEditingProduct(product);editForm.setFieldsValue({name: product.name,code: product.code,price: product.price,category: product.category,status: product.status,description: product.description,});setEditVisible(true);};// 提交编辑const handleEdit = async () => {if (!editingProduct) return;try {const values = await editForm.validateFields();await productService.updateProduct(editingProduct.id.toString(), values);message.success('更新成功');setEditVisible(false);loadProducts();} catch (error: any) {if (error?.errorFields) return;message.error(error?.response?.data?.error || '更新失败');}};// 删除产品const handleDelete = async (id: string) => {try {await productService.deleteProduct(id);message.success('删除成功');loadProducts();} catch (error: any) {message.error(error?.response?.data?.error || '删除失败');}};const columns = [{title: '产品名称',dataIndex: 'name',key: 'name',},{title: '产品编码',dataIndex: 'code',key: 'code',},{title: '价格',dataIndex: 'price',key: 'price',render: (price: number) => `¥${price.toFixed(2)}`,},{title: '分类',dataIndex: 'category',key: 'category',},{title: '状态',dataIndex: 'status',key: 'status',render: (status: number) => (<Tag color={status === 1 ? 'green' : 'red'}>{status === 1 ? '正常' : '禁用'}</Tag>),},{title: '操作',key: 'action',render: (_, record: Product) => (<Space><Buttontype="link"icon={<EditOutlined />}onClick={() => openEditModal(record)}>编辑</Button><Popconfirmtitle="确定要删除这个产品吗?"onConfirm={() => handleDelete(record.id.toString())}okText="确定"cancelText="取消"><Button type="link" danger icon={<DeleteOutlined />}>删除</Button></Popconfirm></Space>),},];return (<Card title="产品管理"><div style={{ marginBottom: 16 }}><Space><Button type="primary" icon={<PlusOutlined />} onClick={openCreateModal}>新增产品</Button><Button icon={<ReloadOutlined />} onClick={loadProducts}>刷新</Button></Space></div><Tablecolumns={columns}dataSource={products}rowKey="id"loading={loading}pagination={{showSizeChanger: true,showQuickJumper: true,showTotal: (total) => `${total} 条记录`,}}/>{/* 新增弹窗 */}<Modaltitle="新增产品"open={createVisible}onOk={handleCreate}onCancel={() => setCreateVisible(false)}okText="确定"cancelText="取消"><Form form={form} layout="vertical"><Form.Item name="name" label="产品名称" rules={[{ required: true, message: '请输入产品名称' }]}><Input placeholder="请输入产品名称" /></Form.Item><Form.Item name="code" label="产品编码" rules={[{ required: true, message: '请输入产品编码' }]}><Input placeholder="请输入产品编码" /></Form.Item><Form.Item name="price" label="价格" rules={[{ required: true, message: '请输入价格' }]}><InputNumber placeholder="请输入价格" style={{ width: '100%' }} min={0} /></Form.Item><Form.Item name="category" label="分类"><Input placeholder="请输入分类" /></Form.Item><Form.Item name="status" label="状态"><Select><Select.Option value={1}>正常</Select.Option><Select.Option value={0}>禁用</Select.Option></Select></Form.Item><Form.Item name="description" label="描述"><Input.TextArea placeholder="请输入描述" rows={3} /></Form.Item></Form></Modal>{/* 编辑弹窗 */}<Modaltitle="编辑产品"open={editVisible}onOk={handleEdit}onCancel={() => setEditVisible(false)}okText="确定"cancelText="取消"><Form form={editForm} layout="vertical"><Form.Item name="name" label="产品名称" rules={[{ required: true, message: '请输入产品名称' }]}><Input placeholder="请输入产品名称" /></Form.Item><Form.Item name="code" label="产品编码" rules={[{ required: true, message: '请输入产品编码' }]}><Input placeholder="请输入产品编码" /></Form.Item><Form.Item name="price" label="价格" rules={[{ required: true, message: '请输入价格' }]}><InputNumber placeholder="请输入价格" style={{ width: '100%' }} min={0} /></Form.Item><Form.Item name="category" label="分类"><Input placeholder="请输入分类" /></Form.Item><Form.Item name="status" label="状态"><Select><Select.Option value={1}>正常</Select.Option><Select.Option value={0}>禁用</Select.Option></Select></Form.Item><Form.Item name="description" label="描述"><Input.TextArea placeholder="请输入描述" rows={3} /></Form.Item></Form></Modal></Card>);
};export default ProductManagement;

步骤3:添加路由

在菜单管理配置菜单并赋给角色,重新登录后即可访问

🔐 权限控制使用指南

后端权限控制

1. 中间件权限控制

// 在路由中使用权限中间件
users.GET("", middleware.AuthMiddleware(rdb, []string{"user:list"}), userHandler.ListUsers)
users.POST("", middleware.AuthMiddleware(rdb, []string{"user:create"}), userHandler.CreateUser)
users.PUT("/:id", middleware.AuthMiddleware(rdb, []string{"user:update"}), userHandler.UpdateUser)
users.DELETE("/:id", middleware.AuthMiddleware(rdb, []string{"user:delete"}), userHandler.DeleteUser)

2. 权限标识规范

  • 格式:模块:操作
  • 示例:user:listuser:createuser:updateuser:delete
  • 菜单权限:user:menu

前端权限控制

// 在组件中使用权限控制
import { usePermission } from '../utils/permission';const UserManagement: React.FC = () => {const canCreate = usePermission('user:create');const canUpdate = usePermission('user:update');const canDelete = usePermission('user:delete');return (<div>{canCreate && (<Button type="primary" onClick={handleCreate}>新增用户</Button>)}<Tablecolumns={[// ... 其他列{title: '操作',render: (_, record) => (<Space>{canUpdate && (<Button onClick={() => handleEdit(record)}>编辑</Button>)}{canDelete && (<Button danger onClick={() => handleDelete(record)}>删除</Button>)}</Space>),},]}/></div>);
};

3. 路由权限控制

// 在路由配置中使用权限控制
import { usePermission } from '../utils/permission';const ProtectedRoute: React.FC<{ permission: string; children: React.ReactNode }> = ({permission,children,
}) => {const hasPermission = usePermission(permission);if (!hasPermission) {return <div>无权限访问</div>;}return <>{children}</>;
};// 使用示例
<Route path="/users" element={<ProtectedRoute permission="user:menu"><UserManagement /></ProtectedRoute>} 
/>

前端字典使用

1. 在组件中使用字典

// 使用字典数据
import { useDict } from '../hooks/useDict';
import { useDictStore } from '../store/dictStore';const UserForm: React.FC = () => {// 方式1:使用Hookconst { dictOptions: statusOptions } = useDict('user_status');// 方式2:直接从Store获取const { getDictOptions } = useDictStore();const genderOptions = getDictOptions('user_gender');return (<Form><Form.Item name="status" label="状态"><Select options={statusOptions} placeholder="请选择状态" /></Form.Item><Form.Item name="gender" label="性别"><Select options={genderOptions} placeholder="请选择性别" /></Form.Item></Form>);
};

🧩 组件使用指南

1. OrganizationTree组件

// 使用组织树组件
import OrganizationTree from '../components/OrganizationTree';const MyComponent: React.FC = () => {const [selectedOrgId, setSelectedOrgId] = useState<string | null>(null);return (<OrganizationTreeselectedOrgId={selectedOrgId}onSelect={setSelectedOrgId}defaultExpandAll={true}title="选择组织"size="default"/>);
};

2. UserSelector组件

// 使用用户选择器组件
import UserSelector from '../components/UserSelector';const MyComponent: React.FC = () => {const [userSelectorVisible, setUserSelectorVisible] = useState(false);const [selectedUserKeys, setSelectedUserKeys] = useState<React.Key[]>([]);const handleUserSelect = (userIds: string[]) => {console.log('选中的用户ID:', userIds);setUserSelectorVisible(false);};return (<div><Button onClick={() => setUserSelectorVisible(true)}>选择用户</Button><UserSelectorvisible={userSelectorVisible}title="选择用户"selectedUserKeys={selectedUserKeys}onCancel={() => setUserSelectorVisible(false)}onOk={handleUserSelect}/></div>);
};

🖼️ 效果展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

http://www.dtcms.com/a/481753.html

相关文章:

  • Flink Checkpoint 设计理念深度解析(附源码)
  • 从 TF-IDF 到 Word2Vec:让推荐系统更懂语义
  • 01-ELK安装ES,ES-head
  • OpenCV4-直方图与傅里叶变换-项目实战-信用卡数字识别
  • 医院排班挂号系统小程序
  • 河北建设厅网站打不开是什么原因国际新闻直播
  • C++设计模式_行为型模式_命令模式Command
  • Blender自动化展UV插件 UV Factory 4.3 v1 – Powerful Modular Uv Tools
  • 网络与通信安全课程复习汇总2——信息保密
  • 密码学安全:CIA三元组与三大核心技术
  • 建网站怎么做本地的营销网站建设
  • 短剧分销系统技术拆解:渠道推广码生成、订单归因与实时分账系统实现
  • ​RocketMQ 与 RabbitMQ 全面对比:架构、性能与适用场景解析
  • RabbitMQ 消息可靠投递
  • RabbitMQ全面详解:从核心概念到企业级应用
  • 北京市建设工程第四检测所网站小程序定制开发团队
  • 安徽网站优化flash如何做网页
  • AI文档处理:AI在处理扫描版PDF时准确率低,如何提升?
  • TDengine 数学函数 EXP 用户手册
  • C语言自定义变量类型结构体理论:从初见到精通​​​​​​​(下)
  • 医疗网络功能虚拟化与深度强化学习的动态流量调度优化研究(下)
  • SpringMVC练习:加法计算器与登录
  • 小模型的应用
  • 深度学习进阶(一)——从 LeNet 到 Transformer:卷积的荣光与注意力的崛起
  • QPSK信号载波同步技术---极性Costas 法载波同步
  • 盘多多网盘搜索苏州seo排名公司
  • 国外有趣的网站wordpress小视频主题
  • RTC、UDP、TCP和HTTP以及直播等区别
  • Java面试场景:从Spring Web到Kafka的音视频应用挑战
  • 基于EDBO-ELM(改进蜣螂算法优化极限学习机)数据回归预测