diff --git a/backend/application/settings.py b/backend/application/settings.py index c232e1e..ba01331 100644 --- a/backend/application/settings.py +++ b/backend/application/settings.py @@ -294,7 +294,7 @@ if not os.path.exists(os.path.join(BASE_DIR, 'media')): MEDIA_URL = "/media/" # 项目中存储上传文件的根目录 -MEDIA_ROOT = os.path.join(BASE_DIR, "media") +# MEDIA_ROOT = os.path.join(BASE_DIR, "media") # ================================================= # # ******************** 自定义权限 ******************** # diff --git a/backend/apps/lyusers/views.py b/backend/apps/lyusers/views.py index 53e10bf..52d9aa1 100644 --- a/backend/apps/lyusers/views.py +++ b/backend/apps/lyusers/views.py @@ -12,7 +12,10 @@ from utils.viewset import CustomModelViewSet from rest_framework.permissions import IsAuthenticated from drf_yasg import openapi from drf_yasg.utils import swagger_auto_schema -from utils.imageupload import ImageUpload +# 添加七牛云存储工具类导入 +from utils.qiniu_storage import QiniuStorage +# 移除原有的图片上传工具 +# from utils.imageupload import ImageUpload from mysystem.models import Users from utils.filters import UsersManageTimeFilter from django.contrib.auth.hashers import make_password @@ -136,11 +139,9 @@ class UserManageViewSet(CustomModelViewSet): # ================================================= # #前端获取个人信息 +from utils.qiniu_storage import QiniuStorage + class GetUserinfoView(APIView): - ''' - 前端获取个人信息 - get: - ''' authentication_classes = [JWTAuthentication] permission_classes = [] @@ -148,12 +149,20 @@ class GetUserinfoView(APIView): data = {} user = request.user if request.user.is_authenticated: - data['avatar'] = user.avatar + if user.avatar: + if 'http' not in user.avatar: # 如果不是完整URL,则生成七牛云链接 + qiniu = QiniuStorage() + base_url = f'http://{qiniu.domain}/{user.avatar}' + data['avatar'] = qiniu.q.private_download_url(base_url, expires=3600*24*365) + else: + data['avatar'] = user.avatar + else: + data['avatar'] = "" data['nickname'] = user.nickname data['mobile'] = user.mobile data['id'] = user.id return DetailResponse(data=data) - #匿名用户 + # 匿名用户 data['avatar'] = "" data['nickname'] = "" data['mobile'] = "" @@ -172,11 +181,43 @@ class uploadImagesView(APIView): permission_classes = [IsAuthenticated] def post(self, request, *args, **kwargs): - result = ImageUpload(request,"frontendimages") - if result['code'] == 200 : - return SuccessResponse(data=result['img'],msg=result['msg']) - else: - return ErrorResponse(msg=result['msg']) + file = request.FILES.get('file') + if not file: + return ErrorResponse(msg='请选择文件') + + qiniu = QiniuStorage() + result = qiniu.upload_data(file, 'frontendimages') + + if result['code'] == 200: + return SuccessResponse(data=result['url'], msg=result['msg']) + return ErrorResponse(msg=result['msg']) + +class ChangeAvatarView(APIView): + ''' + 前端app头像修改 + post: + 【功能描述】前端app头像修改
+ 【参数说明】无,需要登录携带token后才能调用
+ ''' + authentication_classes = [JWTAuthentication] + permission_classes = [IsAuthenticated] + + def post(self, request, *args, **kwargs): + file = request.FILES.get('file') + if not file: + return ErrorResponse(msg='请选择文件') + + qiniu = QiniuStorage() + result = qiniu.upload_data(file, 'avatar') + + if result['code'] == 200: + user = request.user + # 只存储文件路径部分,不存储完整URL + key = result['url'] + user.avatar = key + user.save() + return SuccessResponse(data=result['url'], msg=result['msg']) + return ErrorResponse(msg=result['msg']) class SetUserNicknameView(APIView): """ @@ -216,25 +257,25 @@ class SetUserNicknameView(APIView): return SuccessResponse(msg="success") #前端app头像修改 -class ChangeAvatarView(APIView): - ''' - 前端app头像修改 - post: - 【功能描述】前端app头像修改
- 【参数说明】无,需要登录携带token后才能调用
- ''' - authentication_classes = [JWTAuthentication] - permission_classes = [IsAuthenticated] +# class ChangeAvatarView(APIView): +# ''' +# 前端app头像修改 +# post: +# 【功能描述】前端app头像修改
+# 【参数说明】无,需要登录携带token后才能调用
+# ''' +# authentication_classes = [JWTAuthentication] +# permission_classes = [IsAuthenticated] - def post(self, request, *args, **kwargs): - result = ImageUpload(request,"avatar") - if result['code'] == 200 : - user = request.user - user.avatar = result['img'][0] - user.save() - return SuccessResponse(data=result['img'],msg=result['msg']) - else: - return ErrorResponse(msg=result['msg']) +# def post(self, request, *args, **kwargs): +# result = ImageUpload(request,"avatar") +# if result['code'] == 200 : +# user = request.user +# user.avatar = result['img'][0] +# user.save() +# return SuccessResponse(data=result['img'],msg=result['msg']) +# else: +# return ErrorResponse(msg=result['msg']) #注销账号(标记已注销) class DestroyUserView(APIView): diff --git a/backend/apps/platformsettings/views.py b/backend/apps/platformsettings/views.py index 6a11da0..afc75a3 100644 --- a/backend/apps/platformsettings/views.py +++ b/backend/apps/platformsettings/views.py @@ -17,6 +17,7 @@ from utils.common import get_parameter_dic,get_full_image_url,ast_convert from utils.filters import UserLeavingMessageTimeFilter,SystemConfigFilter from utils.models import get_all_models_objects from utils.barcode import barCodeGenerate +from utils.qiniu_storage import QiniuStorage # Create your views here. # ================================================= # @@ -225,13 +226,25 @@ class PlatformImagesUploadView(APIView): authentication_classes = [JWTAuthentication] permission_classes = [IsAuthenticated] + #原上传 + # def post(self, request, *args, **kwargs): + # result = ImageUpload(request, "platform") + # if result['code'] == 200: + # return SuccessResponse(data=result['img'], msg=result['msg']) + # else: + # return ErrorResponse(msg=result['msg']) def post(self, request, *args, **kwargs): - result = ImageUpload(request, "platform") + file = request.FILES.get('file') + if not file: + return ErrorResponse(msg='请选择文件') + + qiniu = QiniuStorage() + result = qiniu.upload_data(file, 'platform') + if result['code'] == 200: - return SuccessResponse(data=result['img'], msg=result['msg']) - else: - return ErrorResponse(msg=result['msg']) - + return SuccessResponse(data=result['url'], msg=result['msg']) + return ErrorResponse(msg=result['msg']) + # ================================================= # # ************** 前端用户获取平台配置信息 view ************** # # ================================================= # diff --git a/backend/config.py b/backend/config.py index 88a6610..d9c075a 100644 --- a/backend/config.py +++ b/backend/config.py @@ -44,7 +44,9 @@ LOGIN_ERROR_RETRY_TIMES = 0 #登录错误次数限制,0表示不限制 LOGIN_ERROR_RETRY_TIMEOUT = 60 #登录错误次数过期时间,单位秒 FRONTEND_API_LIST = ['/api/app/','/api/xcx/','/api/h5/']#微服务前端接口前缀 #DOMAIN_HOST = "http://47.112.174.207:7070"#控制图片上传后保存所使用到的域名 -DOMAIN_HOST = "http://etoai.top" +# DOMAIN_HOST = "http://etoai.top" #阿里云地址 +DOMAIN_HOST = "http://static.etoai.top" #七牛云地址 +MEDIA_ROOT = os.path.join(DOMAIN_HOST, "media") EXEC_LOG_PATH = os.path.join(BASE_DIR, 'logs','lybbnexec.log') TEMP_EXEC_PATH = os.path.join(BASE_DIR, 'logs') LOG_IP_AREA = True #操作日志是否记录用户IP归属地 @@ -223,4 +225,15 @@ ALIPAY_APPID = 'xxxxxxxxxxxxxxxxxx' # 服务器存放证书路径(支付宝支付签发的) ALIPAY_PRIVATE_KEY_PATH = os.path.join(BASE_DIR, 'key', 'app_private_key.pem') -ALIPAY_PUBLIC_KEY_PATH = os.path.join(BASE_DIR, 'key', 'alipay_public_key.pem') \ No newline at end of file +ALIPAY_PUBLIC_KEY_PATH = os.path.join(BASE_DIR, 'key', 'alipay_public_key.pem') + +# 七牛云配置 +QINIU_ACCESS_KEY = "uihXkSxCvmOKywpXmnpqSKeiGmjGAOpn8b9wAB9B" +QINIU_SECRET_KEY = "Xz8VMHep6v0LueUNGJV-mrOrase4rZLmT4qpsYAu" +QINIU_BUCKET_NAME = "etoaistatic" +QINIU_BUCKET_DOMAIN = "static.etoai.top" # 你的七牛云域名 +QINIU_SECURE_URL = False # 是否使用 HTTPS + +# 修改 MEDIA_ROOT 配置 +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # 本地临时存储路径 \ No newline at end of file diff --git a/backend/media/lyzfiles/7/b/7bd78fd81bdb224b1a2831f5085a9112.jpg b/backend/media/lyzfiles/7/b/7bd78fd81bdb224b1a2831f5085a9112.jpg new file mode 100644 index 0000000..cc20ddc Binary files /dev/null and b/backend/media/lyzfiles/7/b/7bd78fd81bdb224b1a2831f5085a9112.jpg differ diff --git a/backend/media/platform/2025-04-13/20250413082524_222.jpg b/backend/media/platform/2025-04-13/20250413082524_222.jpg new file mode 100644 index 0000000..cc20ddc Binary files /dev/null and b/backend/media/platform/2025-04-13/20250413082524_222.jpg differ diff --git a/backend/media/platform/2025-04-13/20250413082617_190.jpg b/backend/media/platform/2025-04-13/20250413082617_190.jpg new file mode 100644 index 0000000..cc20ddc Binary files /dev/null and b/backend/media/platform/2025-04-13/20250413082617_190.jpg differ diff --git a/backend/media/platform/2025-04-13/20250413085721_687.jpg b/backend/media/platform/2025-04-13/20250413085721_687.jpg new file mode 100644 index 0000000..cc20ddc Binary files /dev/null and b/backend/media/platform/2025-04-13/20250413085721_687.jpg differ diff --git a/backend/mysystem/views/user.py b/backend/mysystem/views/user.py index e65c9da..e03571b 100644 --- a/backend/mysystem/views/user.py +++ b/backend/mysystem/views/user.py @@ -147,8 +147,14 @@ class UserViewSet(CustomModelViewSet): """修改当前用户信息""" user = request.user reqData = request.data - Users.objects.filter(id=user.id).update(email=reqData.get('email'),name=reqData.get('name'),gender=reqData.get('gender'),mobile=reqData.get('mobile')) - return SuccessResponse(data=None, msg="修改成功") + # 添加 avatar 字段到更新列表中 + Users.objects.filter(id=user.id).update( + email=reqData.get('email'), + name=reqData.get('name'), + gender=reqData.get('gender'), + mobile=reqData.get('mobile'), + ) + return SuccessResponse(data=None, msg="当前用户信息修改成功") def change_password(self,request,*args, **kwargs): diff --git a/backend/requirements.txt b/backend/requirements.txt index 0bdd63f..eb16dbe 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -71,4 +71,9 @@ python-barcode qqwry-py3 chardet natsort -pywin32 ; sys_platform == 'win32' \ No newline at end of file +pywin32 ; sys_platform == 'win32' +# 七牛云存储 +qiniu>=7.13.0 + +# 图片处理 +Pillow>=10.1.0 \ No newline at end of file diff --git a/backend/requirements_linux.txt b/backend/requirements_linux.txt index c0d66b0..6f79e60 100644 --- a/backend/requirements_linux.txt +++ b/backend/requirements_linux.txt @@ -74,4 +74,9 @@ uwsgi qqwry-py3 chardet natsort -pywin32 ; sys_platform == 'win32' \ No newline at end of file +pywin32 ; sys_platform == 'win32' +# 七牛云存储 +qiniu>=7.13.0 + +# 图片处理 +Pillow>=10.1.0 \ No newline at end of file diff --git a/backend/requirements_now.txt b/backend/requirements_now.txt index 1daf766..d7cd391 100644 --- a/backend/requirements_now.txt +++ b/backend/requirements_now.txt @@ -96,3 +96,8 @@ python-barcode chardet natsort pywin32 ; sys_platform == 'win32' +# 七牛云存储 +qiniu>=7.13.0 + +# 图片处理 +Pillow>=10.1.0 \ No newline at end of file diff --git a/backend/utils/qiniu_storage.py b/backend/utils/qiniu_storage.py new file mode 100644 index 0000000..1254fc6 --- /dev/null +++ b/backend/utils/qiniu_storage.py @@ -0,0 +1,62 @@ +from qiniu import Auth, put_file, put_data +from django.conf import settings +import time +import os +from PIL import Image +from io import BytesIO + +class QiniuStorage: + def __init__(self): + self.q = Auth(settings.QINIU_ACCESS_KEY, settings.QINIU_SECRET_KEY) + self.bucket_name = settings.QINIU_BUCKET_NAME + self.domain = settings.QINIU_BUCKET_DOMAIN + self.max_size = 5 * 1024 * 1024 # 5MB + self.allowed_types = ['.jpg', '.jpeg', '.png', '.gif'] + + def compress_image(self, image_data): + """无损压缩图片""" + img = Image.open(BytesIO(image_data)) + if img.mode in ('RGBA', 'P'): + img = img.convert('RGB') + + output = BytesIO() + # 保持原始尺寸,仅优化质量 + img.save(output, format='JPEG', quality=85, optimize=True) + return output.getvalue() + + def upload_data(self, file_data, file_path): + """上传文件数据""" + # 检查文件大小 + if file_data.size > self.max_size: + return {'code': 400, 'msg': f'文件大小不能超过{self.max_size/1024/1024}MB'} + + # 检查文件类型 + ext = os.path.splitext(file_data.name)[1].lower() + if ext not in self.allowed_types: + return {'code': 400, 'msg': f'只支持{",".join(self.allowed_types)}格式的图片'} + + # 压缩图片 + try: + compressed_data = self.compress_image(file_data.read()) + except Exception as e: + return {'code': 400, 'msg': f'图片压缩失败:{str(e)}'} + + token = self.q.upload_token(self.bucket_name) + timestamp = time.strftime('%Y-%m-%d/%Y%m%d%H%M%S', time.localtime()) + key = f"{file_path}/{timestamp}_{str(int(time.time()*1000))[-3:]}{ext}" + + try: + ret, info = put_data(token, key, compressed_data) + if info.status_code == 200: + base_url = f'http://{self.domain}/{key}' + private_url = self.q.private_download_url(base_url, expires=3600*24*365) + return { + 'code': 200, + 'url': private_url, + 'msg': '上传成功' + } + except Exception as e: + return { + 'code': 400, + 'msg': f'上传失败:{str(e)}' + } \ No newline at end of file diff --git a/frontend/src/components/upload/avatar.vue b/frontend/src/components/upload/avatar.vue index ad4a5e1..8dfc4c2 100644 --- a/frontend/src/components/upload/avatar.vue +++ b/frontend/src/components/upload/avatar.vue @@ -65,12 +65,8 @@ let obj= await platformsettingsUploadPlatformImg(param) if(obj.code == 2000) { let res='' - if (obj.data.data[0].indexOf("://")>=0){ - res = obj.data.data[0] - - }else{ - res = url.split('/api')[0]+obj.data.data[0] - } + res = obj.data.data + // console.log(res) vm.imageurl = res } else { vm.$message.warning(res.msg) diff --git a/frontend/vue.config.js b/frontend/vue.config.js index 7ab1872..68f50ee 100644 --- a/frontend/vue.config.js +++ b/frontend/vue.config.js @@ -169,7 +169,7 @@ module.exports = defineConfig({ }, mangle: true, // 混淆变量名 output: { - comments: false // 移除注释 + comments: true // 移除注释 } } })