实操学习——个人资料的录入、修改、密码的修改

news2024/11/16 6:44:51

实操学习——个人资料的录入、修改、密码的修改

  • 一、个人资料的录入和修改
  • 知识补充:装饰器
  • 二、密码的修改
  • 知识补充:docker的关闭与启动

在这里插入图片描述

一、个人资料的录入和修改

  1. 在users的app下创建一个用户详情表
from django.contrib.auth.models import User
from django.db import models
from utils.modelsMixin import ModelSetMixin


# Create your models here.
# 创建用户详情表
class UserDetail(ModelSetMixin): #继承自定义的ModelSetMixin
    SEX_CHOICES = (
        (0, '女'),
        (1, '男'),
    )
    # 数据库字段不能存储图片,保存的是图片的路径,所以用TextField
    avatar = models.TextField(null=True, blank=True, verbose_name='头像')
    phone = models.CharField(null=True, blank=True, verbose_name='手机号', max_length=11, unique=True)
    age = models.IntegerField(null=True, blank=True, verbose_name='年龄')
    sex = models.IntegerField(null=True, blank=True, verbose_name='性别', choices=SEX_CHOICES)
    birthday = models.DateField(null=True, blank=True, verbose_name='生日')
    # 一对一关系,关联django自带的user表 ,DO_NOTHING什么都不做
    user = models.OneToOneField(User, on_delete=models.DO_NOTHING)

    class Meta:
        db_table = 'user_detail'
        verbose_name = '用户详情'
        verbose_name_plural = verbose_name

  1. 编写序列化器
from rest_framework.serializers import ModelSerializer
from .models import *


class UserDetailSerializer(ModelSerializer):
    class Meta:
        model = UserDetail
        exclude = ['is_delete']

        extra_kwargs = {
            'avatar': {'read_only': True},
            'user': {'required': False, 'write_only': True}
        }


class UserSerializer(ModelSerializer):
    # 关联系列化
    userdetail = UserDetailSerializer(required=False, read_only=True)

    class Meta:
        model = User
        exclude = ['password', 'last_name', 'user_permissions']

        extra_kwargs = {
            'id': {'read_only': True},
            'last_login': {'read_only': True},
            'is_superuser': {'read_only': True},
            'is_staff': {'read_only': True},
            'is_active': {'read_only': True},
            'date_joined': {'read_only': True},
            'groups': {'read_only': True}
        }


  1. 配置路由
from django.urls import path
from rest_framework.routers import DefaultRouter
from rest_framework_jwt.views import obtain_jwt_token
from .views import *

urlpatterns = [
    path('login/', obtain_jwt_token),
    path('image/verification/<uuid:uuid>/', ImageVerifyView.as_view())
]
router = DefaultRouter()
router.register('users', UserViewSet)
urlpatterns += router.urls
  1. 编写views.py视图函数
import io

from django.contrib.auth.models import User
from django.http import HttpResponse
from django.shortcuts import render

# Create your views here.
from django.views import View
from django_redis import get_redis_connection
from rest_framework.response import Response
from rest_framework.status import HTTP_404_NOT_FOUND
from rest_framework.viewsets import ReadOnlyModelViewSet
from rest_framework.permissions import IsAuthenticated

from config.dbs.redis_dev import LOGIN_KEY_TEMPLATE, EXPIRE_TIME
from utils.permission import ActiveUserPermission, wrap_permisssion, TeacherPermission
from utils.verifyUtil import ImageVerify
from .serializers import *


class ImageVerifyView(View):
    """
    图片验证码视图类

    get
    返回图片验证码
    """

    def get(self, request, uuid):
        """
        返回验证码图片
        :param request: 请求对象
        :param uuid: 客户端的唯一标识
        :return:
        """
        imgVerify = ImageVerify()
        img, code = imgVerify.verify_code()

        # 将图片数据返回给客户端,无需持久化,所以用流,因为是图片,是字节数据,所以用字节流
        img_bytes = io.BytesIO()
        img.save(img_bytes, format='PNG')
        image_bytes = img_bytes.getvalue()  # 图片的字节数据

        # print(code) # 如何保存 uuid:ZFhA key-value 临时数据 缓存在redis中
        cache = get_redis_connection(alias='verify_codes')
        cache.set(LOGIN_KEY_TEMPLATE % uuid, code, EXPIRE_TIME)

        return HttpResponse(image_bytes, content_type='image/png')


class UserViewSet(ReadOnlyModelViewSet):
    """
    update:
    更新用户个人信息
    前端传入的数据格式:
    {
    'first_name':'张三',
    'email':'zs@maqujiaoyu.com'
    'userdetail':
        {
            'age':18,
            'sex':1
        }
    }
    """
    queryset = User.objects.filter(is_active=1)
    serializer_class = UserSerializer
    # 登录后才可调用该接口
    permission_classes = [IsAuthenticated] 

    @wrap_permisssion(TeacherPermission)
    def list(self, request, *args, **kwargs):
        return ReadOnlyModelViewSet.list(self, request, *args, **kwargs)

    @wrap_permisssion(ActiveUserPermission)
    def retrieve(self, request, *args, **kwargs):
        return ReadOnlyModelViewSet.retrieve(self, request, *args, **kwargs)

    @wrap_permisssion(ActiveUserPermission)
    def update(self, request, pk):
        """
        创建用户详情信息
        """
        # 得到用户 user
        try:
            user = self.get_queryset().get(id=pk)
        except User.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)

        # 得到用户输入的数据进行反序列化
        user_serializer = self.get_serializer(user, data=request.data)
        # 校验
        user_serializer.is_valid(raise_exception=True)
        # 修改user表信息
        user_serializer.save()  # 修改instance,data

        # 判断用户是否传入详情信息
        user_detail = request.data.get('userdetail')  # 获取用户传入的详情数据
        if user_detail:
            # 有,判断用户是否有详情信息,判断数据库里面有没有 hasattr内置函数,判断属性是否存在
            if hasattr(user, 'userdetail'):
                # 如果有,则修改
                user_detail_serializer = UserDetailSerializer(user.userdetail, data=user_detail) #修改传instance
            else:
                # 如果没有,则创建
                user_detail['user'] = pk
                user_detail_serializer = UserDetailSerializer(data=user_detail) #创建不传instance
            user_detail_serializer.is_valid(raise_exception=True)
            user_detail_serializer.save()
            
            user_serializer.data['userdetail'] = user_detail_serializer.data  # 字典的赋值 dict[key] = value
        # 返回修改后的数据
        return Response(user_serializer.data)

  1. 重写permission方法
    设置操作的用户必须是当前登录的用户,确保只能修改自己的个人信息
    1) 在utils文件夹下创建permission.py文件
    在这里插入图片描述
    2)编写类ActiveUserPermission
from functools import update_wrapper

from django.contrib.auth.models import Group
from rest_framework.permissions import BasePermission


class TeacherPermission(BasePermission):
    def has_permission(self, request, view):
        user = request.user  # 当前请求的用户信息
        # 判断身份,查询用户在不在老师这个分组里面
        group = Group.objects.filter(name='老师').first()
        user_groups = user.groups.all()
        return user.is_superuser or group in user_groups


class ActiveUserPermission(BasePermission):
    def has_permission(self, request, view):
        # 操作的用户必须是当前登陆的用户
        user = request.user
        return user.id == int(view.kwargs['pk']) #判断当前用户的id等于传入进来的id


# 更改权限装饰器
def wrap_permisssion(*permissions, validate_permisssion=True):
    def decorator(func):
        def wrapper(self, request, *args, **kwargs):
            self.permission_classes = permissions
            if validate_permisssion:
                self.check_permissions(request)
            return func(self, request, *args, **kwargs)

        return update_wrapper(wrapper, func)

    return decorator

3)在views.py文件中使用

  • 方法一
from utils.permission import ActiveUserPermission, wrap_permisssion, TeacherPermission

class UserViewSet(ReadOnlyModelViewSet):
    """
    update:
    更新用户个人信息
    前端传入的数据格式:
    {
    'first_name':'张三',
    'email':'zs@maqujiaoyu.com'
    'userdetail':
        {
            'age':18,
            'sex':1
        }
    }
    """
    queryset = User.objects.filter(is_active=1)
    serializer_class = UserSerializer
    # 登录后才可调用该接口,并且操作用户必须是当前登录用户
    permission_classes = [IsAuthenticated,ActiveUserPermission]
  • 方法二
    在permission.py中定义权限的装饰器

from functools import update_wrapper
# 更改权限装饰器 *permissions元组不定长参数
# validate_permisssion=True 是否要进行权限的校验
def wrap_permisssion(*permissions, validate_permisssion=True): #定义装饰器,接收装饰器参数
    def decorator(func): #定义函数,接收原本的函数
        def wrapper(self, request, *args, **kwargs): #定义函数,接收原本函数传入的参数
            self.permission_classes = permissions #改变permission_classes为传入进来的permissions参数
            if validate_permisssion: #判断是否需要权限的校验
                self.check_permissions(request) #进行校验
            return func(self, request, *args, **kwargs) # 再调用原本的函数

        return update_wrapper(wrapper, func) #使用update_wrapper将升级后的函数伪装为原本的函数

    return decorator

使用 @wrap_permisssion(ActiveUserPermission)

import io

from django.contrib.auth.models import User
from django.http import HttpResponse
from django.shortcuts import render

# Create your views here.
from django.views import View
from django_redis import get_redis_connection
from rest_framework.response import Response
from rest_framework.status import HTTP_404_NOT_FOUND
from rest_framework.viewsets import ReadOnlyModelViewSet
from rest_framework.permissions import IsAuthenticated

from config.dbs.redis_dev import LOGIN_KEY_TEMPLATE, EXPIRE_TIME
from utils.permission import ActiveUserPermission, wrap_permisssion, TeacherPermission
from utils.verifyUtil import ImageVerify
from .serializers import *
class UserViewSet(ReadOnlyModelViewSet):
    """
    update:
    更新用户个人信息
    前端传入的数据格式:
    {
    'first_name':'张三',
    'email':'zs@maqujiaoyu.com'
    'userdetail':
        {
            'age':18,
            'sex':1
        }
    }
    """
    queryset = User.objects.filter(is_active=1)
    serializer_class = UserSerializer
    # 登录后才可调用该接口
    permission_classes = [IsAuthenticated] 

    @wrap_permisssion(TeacherPermission)
    def list(self, request, *args, **kwargs):
        return ReadOnlyModelViewSet.list(self, request, *args, **kwargs)

    @wrap_permisssion(ActiveUserPermission)
    def retrieve(self, request, *args, **kwargs):
        return ReadOnlyModelViewSet.retrieve(self, request, *args, **kwargs)

    @wrap_permisssion(ActiveUserPermission)
    def update(self, request, pk):
        """
        创建用户详情信息
        """
        # 得到用户 user
        try:
            user = self.get_queryset().get(id=pk)
        except User.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)

        # 得到用户输入的数据进行反序列化
        user_serializer = self.get_serializer(user, data=request.data)
        # 校验
        user_serializer.is_valid(raise_exception=True)
        # 修改user表信息
        user_serializer.save()  # 修改instance,data

        # 判断用户是否传入详情信息
        user_detail = request.data.get('userdetail')  # 获取用户传入的详情数据
        if user_detail:
            # 有,判断用户是否有详情信息,判断数据库里面有没有 hasattr内置函数,判断属性是否存在
            if hasattr(user, 'userdetail'):
                # 如果有,则修改
                user_detail_serializer = UserDetailSerializer(user.userdetail, data=user_detail) #修改传instance
            else:
                # 如果没有,则创建
                user_detail['user'] = pk
                user_detail_serializer = UserDetailSerializer(data=user_detail) #创建不传instance
            user_detail_serializer.is_valid(raise_exception=True)
            user_detail_serializer.save()
            
            user_serializer.data['userdetail'] = user_detail_serializer.data  # 字典的赋值 dict[key] = value
        # 返回修改后的数据
        return Response(user_serializer.data)

知识补充:装饰器

装饰器:在不改变函数本身的情况下进行功能的升级,增加
定义装饰器:装饰器的本质就是闭包,即函数的嵌套

"""
装饰器函数格式
def 装饰器名(接收原本函数的参数):
	def 升级后的函数():
	return 升级后的函数

使用装饰器: @装饰器名

"""
def decorator(func): #定义一个装饰器函数,定义func参数接收原本的函数
	def wrapper(): #定义升级后的函数,可以接收原本函数的参数
		print('权限的校验') #升级后的函数的功能
		func()  #执行原本的函数,已经作为参数传入到func中,直接调用
	return wrapper #返回升级后的函数体

@decorator #使用装饰器 调用装饰器函数,把返回值赋值给原本的函数 等同于test = decorator(test)
def test():
	print(''实现查询功能)

test() # wrapper()

装饰器传参

def wrap_permission(permissions): #再包裹一层函数,用于接收使用装饰器时传入的参数
	def decorator(func): #定义一个装饰器函数,定义func参数接收原本的函数
		def wrapper(): #定义升级后的函数
			print('权限的校验',permissions) #升级后的函数的功能
			func()  #执行原本的函数,已经作为参数传入到func中,直接调用
		return wrapper #返回升级后的函数体
	return decorator

@wrap_permission(ActiveUserPermission)  #使用装饰器 调用装饰器函数,把返回值赋值给原本的函数 等同于test = decorator(test)
def test():
	print(''实现查询功能)

test() # wrapper()

二、密码的修改

在user的views.py中添加如下接口:

    @action(methods=['put'], detail=True)
    def password(self, request, pk):
        # 得到要操作的用户对象
        try:
            user = self.get_queryset().get(id=pk)
        except User.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)

        # 检查原密码是否正确
       	password = request.data['password']  # 得到客户端输入的原密码
        # 确认新密码与新密码在前端进行校验
        new_password = request.data['new_password']  # 得到客户端输入的新密码
        # 不能使用user.password,这个是加密的密码,不能与前端获取的原密码进行比较
        if user.check_password(password):
            # 正确 修改密码
            #set_password保存的是加密后的密码 ,不能用user.password=new_password,保存的是明文
            user.set_password(new_password) 
            user.save()
            return Response({'msg': '修改成功'})
        else:
            # 错误 返回提示,原密码错误
            return Response(status=HTTP_400_BAD_REQUEST)

如果在后端校验新密码与确认新密码是否一致,可以修改如下:
1) 增加如下序列化器

#没有对应的模型,继承serializers.Serializer,有对应的模型,可以直接继承ModelSerializer
class UpdatePasswordSerializer(serializers.Serializer):
    password = serializers.CharField(max_length=60, label='原密码')
    new_password = serializers.CharField(max_length=60, label='新密码')
    re_new_password = serializers.CharField(max_length=60, label='确认新密码')
	#在序列化器中增加密码的校验
    def validate(self, attrs):# 使用对象级别的校验
        if attrs['new_password'] != attrs['re_new_password']:
            raise serializers.ValidationError('两次密码输入不一致')
        return attrs

2) 重写serializer

class UserViewSet(ReadOnlyModelViewSet):
    queryset = User.objects.filter(is_active=1)
    serializer_class = UserSerializer
    permission_classes = [IsAuthenticated]

    # 重写serializer
    def get_serializer(self, *args, **kwargs):
        if self.action == 'password': #如果调用的函数是password,返回UpdatePasswordSerializer
            return UpdatePasswordSerializer(*args, **kwargs)
        return self.serializer_class(*args, **kwargs)

3)修改密码时增加序列化器校验

    @action(methods=['put'], detail=True)
    def password(self, request, pk):
        # 得到要操作的用户对象
        try:
            user = self.get_queryset().get(id=pk)
        except User.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)

        # 检查原密码是否正确
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        password = request.data['password']  # 得到客户端输入的原密码
        # 确认新密码与新密码在前端进行校验
        new_password = request.data['new_password']  # 得到客户端输入的新密码
        # 不能使用user.password,这个是加密的密码,不能与前端获取的原密码进行比较
        if user.check_password(password):
            # 正确 修改密码
            #set_password保存的是加密后的密码 ,不能用user.password=new_password,保存的是明文
            user.set_password(new_password) 
            user.save()
            return Response({'msg': '修改成功'})
        else:
            # 错误 返回提示,原密码错误
            return Response(status=HTTP_400_BAD_REQUEST)

知识补充:docker的关闭与启动

在这里插入图片描述
关闭所有的docker容器(不是删除容器):

sudo docker kill `sudo docker ps -q`

开启所有的docker容器

sudo docker start `sudo docker ps -ap`

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2157410.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C/C++逆向:switch语句逆向分析

在逆向分析中&#xff0c;switch语句会被编译器转化为不同的底层实现方式&#xff0c;这取决于编译器优化和具体的场景。常见的实现方式包括以下几种&#xff1a; ①顺序判断&#xff08;if-else链&#xff09;&#xff1a; 编译器将switch语句转化为一系列的if-else语句。这…

【第十四章:Sentosa_DSML社区版-机器学习时间序列】

目录 【第十四章&#xff1a;Sentosa_DSML社区版-机器学习时间序列】 14.1 ARIMAX 14.2 ARIMA 14.3 HoltWinters 14.4 一次指数平滑预测 14.5 二次指数平滑预测 【第十四章&#xff1a;Sentosa_DSML社区版-机器学习时间序列】 14.1 ARIMAX 1.算子介绍 考虑其他序列对一…

Flutter鸿蒙化(windows)

Flutter鸿蒙化&#xff08;windows&#xff09; 参考资料Window配置Flutter的鸿蒙化环境下载配置环境变量HarmonyOS的环境变量配置配置Flutter的环境变量Flutter doctor -v 检测的问题flutter_flutter仓库地址的警告问题Fliutter doctor –v 报错[!] Android Studio (version 2…

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-18

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-18 1. The Application of Large Language Models in Primary Healthcare Services and the Challenges W YAN, J HU, H ZENG, M LIU, W LIANG - Chinese General Practice, 2024 人工智能大语言模型在基层医疗…

软媒市场新探索:软文媒体自助发布,开启自助发稿新篇章

在繁华喧嚣的软媒市场中,每一个声音都在竭力呼喊,每一个品牌都在奋力展现。而软文,作为一种温柔而坚韧的营销力量,正逐渐崭露头角。特别是软文媒体自助发布平台的出现,更是为企业提供了一个全新的、高效的自助发稿渠道。 软媒市场自助发布平台,正如其名,是一个让企业能够自主发…

离职员工客户如何管理?解锁2024企业微信新功能

公司里员工来来去去很正常&#xff0c;但每次有人走&#xff0c;老板们都会头疼&#xff0c;因为客户信息得有人接着管。客户对公司来说太重要了&#xff0c;不能丢。2024年&#xff0c;企业微信出了个新招&#xff0c;就是员工离职后&#xff0c;客户信息可以轻松转给新来的员…

JVM的基本概念

目录 一、JVM的内存划分 二、JVM的类加载过程 三、JVM的垃圾回收机制&#xff08;GC&#xff09; 四、分代回收 一、JVM的内存划分 一个运行起来的Java进程&#xff0c;就是一个Java虚拟机&#xff0c;就需要从操作系统中申请一大块内存。申请的内存会划分为不同的区域&…

Maven笔记(一):基础使用【记录】

Maven笔记&#xff08;一&#xff09;-基础使用 Maven是专门用于管理和构建Java项目的工具&#xff0c;它的主要功能有&#xff1a; 提供了一套标准化的项目结构 Maven提供了一套标准化的项目结构&#xff0c;所有IDE(eclipse、myeclipse、IntelliJ IDEA 等 项目开发工具) 使…

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-17

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-17 1. Large Language Models in Biomedical and Health Informatics: A Review with Bibliometric Analysis H Yu, L Fan, L Li, J Zhou, Z Ma, L Xian, W Hua, S He… - Journal of Healthcare …, 2024 生物…

HarmonyOS应用开发(组件库)--组件模块化开发、工具包、设计模式(持续更新)

致力于&#xff0c;UI开发拿来即用&#xff0c;提高开发效率 正则表达式...手机号校验...邮箱校验 文件判断文件是否存在 网络下载下载图片从沙箱中图片转为Base64格式从资源文件中读取图片转Base64 组件输入框...矩形输入框...输入框堆叠效果&#xff08;用于登录使用&#xf…

【自动驾驶】决策规划算法(二)参考线模块Ⅰ| 平滑算法与二次规划

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

(学习记录)使用 STM32CubeMX——GPIO引脚输入配置

STM32F103C8T6的GPIO引脚输入配置 时钟配置 &#xff08;学习记录&#xff09;使用 STM32CubeMX——配置时钟&#xff08;入门&#xff09;https://blog.csdn.net/Wang2869902214/article/details/142423522 GPIO 引脚输出配置 &#xff08;学习记录&#xff09;使用 STM32…

Springcloud框架-能源管理系统-能源管理系统源码-能源在线监测平台-双碳平台

一、介绍 基于SpringCloud的能管管理系统-能源管理平台源码-能源在线监测平台-双碳平台源码-SpringCloud全家桶-能管管理系统源码 有需者咨询&#xff0c;非诚勿扰&#xff1b; 二、软件架构 二、功能介绍 三、数字大屏展示 四、数据采集原理 五、软件截图

macos pyenv 安装python tk 、tkinter图形库方法步骤和使用总结

在macos中&#xff0c; pyenv 是一款用来管理多版本python 的工具&#xff0c; 我们常用的tk图形库是一个独立的工具库&#xff0c;我们在python里面使用的tkinter模块仅是调用这个独立的tk图形库&#xff0c; 所以如果我们希望在python里面使用它&#xff0c; 就必须要先安装t…

委托的注册及注销+观察者模式

事件 委托变量如果公开出去&#xff0c;很不安全&#xff0c;外部可以随意调用 所以取消public,封闭它&#xff0c;我们可以自己书写两个方法&#xff0c;供外部注册与注销&#xff0c;委托调用在子方法里调用&#xff0c;这样封装委托变量可以使它更安全&#xff0c;这个就叫…

金融加密机的定义与功能

金融加密机是一种用于保护金融交易数据和信息安全的重要安全设备。它通过硬件和软件的多重保障&#xff0c;确保金融交易中的敏感数据不被泄露或篡改。以下是关于金融加密机的详细介绍&#xff1a; 一、定义与功能 金融加密机是一种硬件安全设备&#xff0c;通过实现各种密码算…

深度deepin初体验(一)系统详细安装过程 | 国产系统

这里写自定义目录标题 深度deepin初体验&#xff08;一&#xff09;系统详细安装过程1.介绍2.安装要求3.环境4.创建虚拟机/系统升级系统选择语言硬盘分区备份文件拷贝系统重启常规设置 深度deepin初体验&#xff08;一&#xff09;系统详细安装过程 1.介绍 深度deepin是在debi…

Python开发深度学习常见安装包 error 解决

Python Python 是一种广泛使用的高级编程语言&#xff0c;它以其清晰的语法和代码可读性而闻名。Python 支持多种编程范式&#xff0c;包括面向对象、命令式、函数式和过程式编程。由于其简洁性和强大的标准库&#xff0c;Python 成为了数据科学、机器学习、网络开发、自动化脚…

气膜馆:新型场馆的盈利之道—轻空间

气膜馆作为一种创新的场馆形式&#xff0c;凭借其先进的技术和灵活的应用&#xff0c;正在快速崛起&#xff0c;展现出广阔的市场前景与丰富的盈利潜力。通过多元化的经营模式&#xff0c;气膜馆为创业者提供了前所未有的商机。本文将深入分析气膜馆的盈利模式及其在市场中的竞…

气膜储煤棚:未来能源管理的新选择—轻空间

在全球对可持续发展与环保的日益重视下&#xff0c;传统的煤炭储存方式面临着诸多挑战。气膜储煤棚应运而生&#xff0c;成为现代煤炭储存的理想解决方案。本文将深入探讨气膜储煤棚的优势与应用&#xff0c;为企业提供新的思路。 先进的技术设计 气膜储煤棚采用创新的气膜技术…