统一上传文件不区分图片、视频接口,素材中心私有化

This commit is contained in:
xh
2025-08-27 03:17:40 +08:00
parent a58c1360d7
commit 606bcbef0a
17 changed files with 296 additions and 213 deletions

View File

@@ -8,7 +8,7 @@ import { getToken } from '@/utils/auth'
type album_cate = {
id?: number
pid?: number
type?: number
// type?: number
name?: string
isDelete?: number
createTime?: string
@@ -18,7 +18,7 @@ type album_cate = {
// 查询
type album_cate_query = {
pid?: number
type?: number
// type?: number
name?: string
createTimeStart?: string
createTimeEnd?: string
@@ -29,7 +29,7 @@ type album_cate_query = {
type album_cate_edit = {
id?: number
pid?: number
type?: number
// type?: number
name?: string
}

View File

@@ -28,12 +28,25 @@ export default defineComponent({
default: '100px'
},
// 文件类型
type: {
ext: {
type: String,
default: 'image'
default: ''
}
},
emits: ['close']
emits: ['close'],
computed: {
type() {
const imageExt = ['jpg', 'jpeg', 'png', 'gif', 'bmp']
const videoExt = ['mp4', 'avi', 'mov']
if (imageExt.includes(this.ext)) {
return 'image'
}
if (videoExt.includes(this.ext)) {
return 'video'
}
return 'file'
}
}
})
</script>

View File

@@ -15,7 +15,7 @@ import { shallowRef, ref, reactive } from 'vue'
import type { Ref } from 'vue'
// 左侧分组的钩子函数
export function useCate(type: number) {
export function useCate() {
const treeRef = shallowRef<InstanceType<typeof ElTree>>()
// 分组列表
const cateLists = ref<any[]>([])
@@ -25,18 +25,12 @@ export function useCate(type: number) {
// 获取分组列表
const getCateLists = async () => {
const data = await fileCateLists({
type
})
const data = await fileCateLists({})
const item: any[] = [
{
name: '全部',
id: 0
}
// {
// name: '未分组',
// id: 0
// }
]
cateLists.value = data
cateLists.value.unshift(...item)
@@ -48,7 +42,6 @@ export function useCate(type: number) {
// 添加分组
const handleAddCate = async (value: string) => {
await fileCateAdd({
type,
name: value,
pid: 0
})
@@ -92,7 +85,7 @@ export function useCate(type: number) {
// 处理文件的钩子函数
export function useFile(
cateId: Ref<string | number>,
type: Ref<number>,
ext: string[],
limit: Ref<number>,
size: number
) {
@@ -103,7 +96,7 @@ export function useFile(
const isIndeterminate = ref(false)
const fileParams = reactive({
name: '',
type: type,
ext: ext,
cid: cateId
})
const { pager, getLists, resetPage } = usePaging({

View File

@@ -85,22 +85,10 @@
<div class="operate-btn flex">
<div class="flex-1 flex">
<upload
v-if="type == 'image'"
v-perms="['admin:common:upload:image']"
class="mr-3"
:data="{ cid: cateId }"
:type="type"
:show-progress="true"
@change="refresh"
>
<el-button type="primary">本地上传</el-button>
</upload>
<upload
v-if="type == 'video'"
v-perms="['admin:common:upload:video']"
class="mr-3"
:data="{ cid: cateId }"
:type="type"
:ext="ext"
:show-progress="true"
@change="refresh"
>
@@ -139,7 +127,7 @@
<file-item
:uri="row.uri"
file-size="50px"
:type="type"
:ext="row.ext"
@click.stop="handlePreview(row.uri)"
></file-item>
</template>
@@ -151,6 +139,9 @@
</el-link>
</template>
</el-table-column>
<el-table-column label="大小" prop="size" min-width="100"> </el-table-column>
<el-table-column label="格式" prop="ext" min-width="100"></el-table-column>
<el-table-column prop="createTime" label="上传时间" min-width="100" />
<el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }">
@@ -259,7 +250,7 @@
<file-item
:uri="item.uri"
file-size="100px"
:type="type"
:ext="item.ext"
></file-item>
</del-wrap>
</div>
@@ -268,12 +259,14 @@
</el-scrollbar>
</div>
</div>
<preview v-model="showPreview" :url="previewUrl" :type="type" />
<preview v-model="showPreview" :url="previewUrl" />
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, toRefs, ref, watch } from 'vue'
import { onMounted, toRefs, ref, watch } from 'vue'
import type { PropType } from 'vue'
import { useCate, useFile } from './hook'
import FileItem from './file.vue'
import Preview from './preview.vue'
@@ -286,10 +279,14 @@ const props = defineProps({
type: Number,
default: 1
},
type: {
type: String,
default: 'image'
ext: {
type: Array as PropType<string[]>,
default: () => []
},
// type: {
// type: String,
// default: 'image'
// },
mode: {
type: String,
default: 'picker'
@@ -301,18 +298,18 @@ const props = defineProps({
})
const emit = defineEmits(['change'])
const { limit } = toRefs(props)
const typeValue = computed<number>(() => {
switch (props.type) {
case 'image':
return 10
case 'video':
return 20
case 'file':
return 30
default:
return 0
}
})
// const typeValue = computed<number>(() => {
// switch (props.type) {
// case 'image':
// return 10
// case 'video':
// return 20
// case 'file':
// return 30
// default:
// return 0
// }
// })
// const visible: Ref<boolean> = inject('visible')
const previewUrl = ref('')
const showPreview = ref(false)
@@ -325,7 +322,7 @@ const {
handleDeleteCate,
getCateLists,
handleCatSelect
} = useCate(typeValue.value)
} = useCate()
const {
tableRef,
@@ -345,7 +342,7 @@ const {
selectItems,
handleFileRename
} = useFile(cateId, typeValue, limit, props.pageSize)
} = useFile(cateId, props.ext, limit, props.pageSize)
function handleSelectionChange(val: any[]) {
console.log('handleSelectionChange', val)
selectItems(val)
@@ -356,22 +353,12 @@ const getData = async () => {
treeRef.value?.setCurrentKey(cateId.value)
getFileList()
}
// getData()
const handlePreview = (url: string) => {
previewUrl.value = url
showPreview.value = true
}
// watch(
// () => visible,
// (val) => {
// if (val) {
// getData()
// }
// },
// {
// immediate: true
// }
// )
watch(cateId, () => {
fileParams.name = ''
refresh()

View File

@@ -17,7 +17,7 @@
</template>
<script lang="ts" setup>
import { ref, useTemplateRef, watch, nextTick } from 'vue'
import { ref, useTemplateRef, watch, computed, nextTick } from 'vue'
const props = defineProps({
modelValue: {
type: Boolean,
@@ -26,11 +26,24 @@ const props = defineProps({
url: {
type: String,
default: ''
},
type: {
type: String,
default: 'image'
}
// type: {
// type: String,
// default: 'image'
// }
})
const type = computed(() => {
const imageExt = ['jpg', 'jpeg', 'png', 'gif', 'bmp']
const videoExt = ['mp4', 'avi', 'mov']
const ext = props.url.split('.').pop()
if (imageExt.includes(ext)) {
return 'image'
}
if (videoExt.includes(ext)) {
return 'video'
}
return 'file'
})
const playerRef = useTemplateRef('playerRef')

View File

@@ -14,7 +14,7 @@
:on-error="handleError"
:accept="getAccept"
>
<slot></slot>
<slot></slot>{{ getAccept }}
</el-upload>
<el-dialog
v-if="showProgress && fileList.length"
@@ -41,6 +41,7 @@
<script lang="ts">
import { computed, defineComponent, ref, toRaw, useTemplateRef } from 'vue'
import type { PropType } from 'vue'
import useUserStore from '@/stores/modules/user'
import config from '@/config'
import feedback from '@/utils/feedback'
@@ -54,6 +55,10 @@ export default defineComponent({
type: String,
default: ''
},
ext: {
type: Array as PropType<string[]>,
default: () => []
},
// 上传文件类型
type: {
type: String,
@@ -94,7 +99,7 @@ export default defineComponent({
action = `${config.baseUrl}${config.urlPrefix}${props.url}`
}
} else {
action = `${config.baseUrl}${config.urlPrefix}/common/upload/${props.type}`
action = `${config.baseUrl}${config.urlPrefix}/common/upload/file`
}
const headers = computed(() => ({
token: userStore.token,
@@ -135,14 +140,15 @@ export default defineComponent({
}
const getAccept = computed(() => {
switch (props.type) {
case 'image':
return '.jpg,.png,.gif,.webp,.jpeg,.ico,.bmp'
case 'video':
return '.wmv,.avi,.mov,.mp4,.flv,.rmvb'
default:
return '*'
if (props.ext.length) {
// 补充前缀
return props.ext
.map((item) => {
return `.${item}`
})
.join(',')
}
return '*'
})
return {
uploadRefs,

View File

@@ -5,13 +5,13 @@
<el-tab-pane
v-for="item in tabsMap"
:label="item.name"
:name="item.type"
:index="item.type"
:key="item.type"
:name="item.name"
:index="item.name"
:key="item.name"
lazy
>
<material
:type="item.type"
:ext="item.ext"
mode="page"
file-size="120px"
:limit="-1"
@@ -31,12 +31,24 @@ defineOptions({
})
const tabsMap = [
{
type: 'image',
name: '图片'
// type: '',
name: '全部',
ext: []
},
{
type: 'video',
name: '视频'
// type: 'image',
name: '图片',
ext: ['jpg', 'jpeg', 'png', 'gif', 'bmp']
},
{
// type: 'video',
name: '视频',
ext: ['mp4', 'avi', 'mov']
},
{
// type: 'pdf',
name: 'pdf',
ext: ['pdf']
}
]
const activeTab = ref('image')
@@ -47,11 +59,7 @@ const activeTab = ref('image')
min-width: 700px;
:deep(.el-tabs) {
height: calc(100vh - 180px);
// display: flex;
// flex-direction: column;
// .el-tabs__header {
// margin-bottom: 0 !important;
// }
.el-tabs__content,
.el-tab-pane {
min-height: 0;

View File

@@ -13,3 +13,11 @@ REDIS.URL='redis://localhost:6379'
# 上传文件目录
FILE.UPLOAD_DIRECTORY='/www/wwwroot/x_admin_go/public/uploads/'
GeTui.HOST=''
GeTui.APPID=''
GeTui.APPKEY=''
GeTui.APPSECRET=''
GeTui.MASTERSECRET=''
GeTui.PackName=''

View File

@@ -3,21 +3,30 @@ package config
type fileConfig struct {
UploadDirectory string `mapstructure:"UPLOAD_DIRECTORY"` // 文件目录
PublicPrefix string `mapstructure:"PUBLIC_PREFIX"` // 资源访问前缀
UploadImageSize int64 `mapstructure:"UPLOAD_IMAGE_SIZE"` // 上传图片大小限制
UploadVideoSize int64 `mapstructure:"UPLOAD_VIDEO_SIZE"` // 上传视频大小限制
UploadFileSize int64 `mapstructure:"UPLOAD_FILE_SIZE"` // 上传文件大小限制
UploadImageExt []string `mapstructure:"UPLOAD_IMAGE_EXT"` // 上传图片扩展
UploadVideoExt []string `mapstructure:"UPLOAD_VIDEO_EXT"` // 上传视频扩展
UploadFileExt []string `mapstructure:"UPLOAD_FILE_EXT"` // 上传文件扩展
}
// var uploadImageExtDefault = []string{"png", "jpg", "jpeg", "gif", "ico", "bmp", "webp", "avif"}
var FileConfig = fileConfig{
// 资源访问前缀
PublicPrefix: "/api/uploads",
// 上传文件路径
UploadDirectory: "/tmp/uploads/x_admin_go/",
UploadImageSize: 10 * 1024 * 1024,
UploadVideoSize: 30 * 1024 * 1024,
UploadImageSize: 10 * 1024 * 1024, // 10MB
UploadVideoSize: 500 * 1024 * 1024, // 500MB
UploadFileSize: 1024 * 1024 * 1024, //1GB
// 上传图片扩展
UploadImageExt: []string{"png", "jpg", "jpeg", "gif", "ico", "bmp", "webp", "avif"},
// 上传视频扩展
UploadVideoExt: []string{"mp4", "mp3", "avi", "flv", "rmvb", "mov"},
UploadFileExt: []string{"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "zip", "rar", "7z", "txt"},
}

View File

@@ -1,6 +1,7 @@
package commonController
import (
"x_admin/config"
"x_admin/core/request"
"x_admin/core/response"
"x_admin/middleware"
@@ -39,7 +40,8 @@ func (ah albumHandler) albumList(c *gin.Context) {
if response.IsFailWithResp(c, util.VerifyUtil.VerifyQuery(c, &listReq)) {
return
}
res, err := commonService.AlbumService.AlbumList(page, listReq)
var adminId = config.AdminConfig.GetAdminId(c)
res, err := commonService.AlbumService.AlbumList(adminId, page, listReq)
response.CheckAndRespWithData(c, res, err)
}
@@ -76,7 +78,8 @@ func (ah albumHandler) cateList(c *gin.Context) {
if response.IsFailWithResp(c, util.VerifyUtil.VerifyQuery(c, &listReq)) {
return
}
res, err := commonService.AlbumService.CateList(listReq)
var adminId = config.AdminConfig.GetAdminId(c)
res, err := commonService.AlbumService.CateList(adminId, listReq)
response.CheckAndRespWithData(c, res, err)
}
@@ -86,7 +89,8 @@ func (ah albumHandler) cateAdd(c *gin.Context) {
if response.IsFailWithResp(c, util.VerifyUtil.VerifyJSON(c, &addReq)) {
return
}
response.CheckAndResp(c, commonService.AlbumService.CateAdd(addReq))
var adminId = config.AdminConfig.GetAdminId(c)
response.CheckAndResp(c, commonService.AlbumService.CateAdd(adminId, addReq))
}
// cateRename 类目命名

View File

@@ -15,28 +15,26 @@ func UploadRoute(rg *gin.RouterGroup) {
handle := uploadHandler{}
rg = rg.Group("/common", middleware.TokenAuth())
rg.POST("/upload/image", middleware.RecordLog("上传图片", middleware.RequestFile), handle.uploadImage)
rg.POST("/upload/video", middleware.RecordLog("上传视频", middleware.RequestFile), handle.uploadVideo)
rg.POST("/upload/preUploadFile", middleware.RecordLog("文件预上传", middleware.RequestFile), handle.preUploadFile)
rg.POST("/upload/file", middleware.RecordLog("上传文件", middleware.RequestFile), handle.uploadFile)
}
type uploadHandler struct{}
// uploadImage 上传图片
func (uh uploadHandler) uploadImage(c *gin.Context) {
var uReq commonSchema.CommonUploadImageReq
if response.IsFailWithResp(c, util.VerifyUtil.VerifyBody(c, &uReq)) {
return
}
file, ve := util.VerifyUtil.VerifyFile(c, "file")
if response.IsFailWithResp(c, ve) {
return
}
res, err := commonService.UploadService.UploadImage(file, uReq.Cid, config.AdminConfig.GetAdminId(c))
response.CheckAndRespWithData(c, res, err)
// 文件预上传
func (uh uploadHandler) preUploadFile(c *gin.Context) {
// md5,fileName,fileSize,cid
// 检查MD5是否已存在
// 检查名称是否合规安全
// 检查文件大小是否超过限制
// 检查文件类型是否符合要求
// 如果文件存在复制到cid对应目录
}
// uploadVideo 上传视频
func (uh uploadHandler) uploadVideo(c *gin.Context) {
// uploadFile 上传文件
func (uh uploadHandler) uploadFile(c *gin.Context) {
var uReq commonSchema.CommonUploadImageReq
if response.IsFailWithResp(c, util.VerifyUtil.VerifyBody(c, &uReq)) {
return
@@ -45,6 +43,6 @@ func (uh uploadHandler) uploadVideo(c *gin.Context) {
if response.IsFailWithResp(c, ve) {
return
}
res, err := commonService.UploadService.UploadVideo(file, uReq.Cid, config.AdminConfig.GetAdminId(c))
res, err := commonService.UploadService.UploadFile(file, uReq.Cid, config.AdminConfig.GetAdminId(c))
response.CheckAndRespWithData(c, res, err)
}

View File

@@ -10,12 +10,13 @@ import (
type Album struct {
ID uint `gorm:"primarykey;comment:'主键ID'"`
Cid uint `gorm:"not null;default:0;comment:'类目ID'"`
Aid uint `gorm:"not null;default:0;comment:'管理ID'"`
AdminId uint `gorm:"not null;default:0;comment:'管理ID'"`
Uid uint `gorm:"not null;default:0;comment:'用户ID'"`
Type int `gorm:"not null;default:10;comment:'文件类型: [10=图片, 20=视频]''"`
// Type int `gorm:"not null;default:10;comment:'文件类型: [10=图片, 20=视频]''"`
Name string `gorm:"not null;default:'';comment:'文件名称''"`
Uri string `gorm:"not null;comment:'文件路径'"`
Ext string `gorm:"not null;default:'';comment:'文件扩展'"`
Hash string `gorm:"not null;default:'';comment:'文件hash'"`
Size int64 `gorm:"not null;default:0;comment:文件大小"`
IsDelete soft_delete.DeletedAt `gorm:"not null;default:0;softDelete:flag,DeletedAtField:DeleteTime;comment:'是否删除: 0=否, 1=是'"`
CreateTime core.NullTime `gorm:"autoCreateTime;not null;comment:'创建时间'"`
@@ -26,8 +27,10 @@ type Album struct {
// AlbumCate 相册分类实体
type AlbumCate struct {
ID uint `gorm:"primarykey;comment:'主键ID'"`
AdminId uint `gorm:"not null;default:0;comment:'管理员ID'"`
Pid uint `gorm:"not null;default:0;comment:'父级ID'"`
Type int `gorm:"not null;default:10;comment:'文件类型: [10=图片, 20=视频]''"`
// Type int `gorm:"not null;default:10;comment:'文件类型: [10=图片, 20=视频]''"`
Name string `gorm:"not null;default:'';comment:'分类名称''"`
IsDelete soft_delete.DeletedAt `gorm:"not null;default:0;softDelete:flag,DeletedAtField:DeleteTime;comment:'是否删除: 0=否, 1=是'"`
CreateTime core.NullTime `gorm:"autoCreateTime;not null;comment:'创建时间'"`

View File

@@ -20,7 +20,7 @@ var StorageDriver = storageDriver{}
// UploadFile 文件对象
type UploadFile struct {
Name string // 文件名称
Type int // 文件类型
// Type int // 文件类型
Size int64 // 文件大小
Ext string // 文件扩展
Uri string // 文件路径
@@ -31,25 +31,34 @@ type UploadFile struct {
type storageDriver struct{}
// Upload 根据引擎类型上传文件
func (sd storageDriver) Upload(file *multipart.FileHeader, folder string, fileType int) (uf *UploadFile, e error) {
func (sd storageDriver) Upload(file *multipart.FileHeader) (uf *UploadFile, e error) {
// TODO: engine默认local
if e = sd.checkFile(file, fileType); e != nil {
fileExt := sd.getFileExt(file.Filename)
if fileExt == "" {
return nil, response.AssertArgumentError.SetMessage("文件类型错误!")
}
if e = sd.checkFile(file.Filename, file.Size); e != nil {
return
}
key := sd.buildSaveName(file)
var folder string = fileExt
saveName := sd.buildSaveName(file)
engine := "local"
if engine == "local" {
if e = sd.localUpload(file, key, folder); e != nil {
if e = sd.localUpload(file, folder, saveName); e != nil {
return
}
} else {
core.Logger.Errorf("storageDriver.Upload engine err: err=[unsupported engine]")
return nil, response.Failed.SetMessage(fmt.Sprintf("engine:%s 暂时不支持", engine))
}
fileRelPath := path.Join(folder, key)
fileRelPath := path.Join(folder, saveName)
return &UploadFile{
Name: file.Filename,
Type: fileType,
// Type: int(fileType),
Size: file.Size,
Ext: strings.ToLower(strings.Replace(path.Ext(file.Filename), ".", "", 1)),
Uri: fileRelPath,
@@ -58,7 +67,7 @@ func (sd storageDriver) Upload(file *multipart.FileHeader, folder string, fileTy
}
// localUpload 本地上传 (临时方法)
func (sd storageDriver) localUpload(file *multipart.FileHeader, key string, folder string) (e error) {
func (sd storageDriver) localUpload(file *multipart.FileHeader, folder string, saveName string) (e error) {
// TODO: 临时方法,后续调整
// 映射目录
directory := config.FileConfig.UploadDirectory
@@ -70,8 +79,8 @@ func (sd storageDriver) localUpload(file *multipart.FileHeader, key string, fold
}
defer src.Close()
// 文件信息
savePath := path.Join(directory, folder, path.Dir(key))
saveFilePath := path.Join(directory, folder, key)
savePath := path.Join(directory, folder, path.Dir(saveName))
saveFilePath := path.Join(directory, folder, saveName)
// 创建目录
err = os.MkdirAll(savePath, 0755)
if err != nil && !os.IsExist(err) {
@@ -94,6 +103,7 @@ func (sd storageDriver) localUpload(file *multipart.FileHeader, key string, fold
"storageDriver.localUpload Copy err: file=[%s], err=[%+v]", saveFilePath, err)
return response.Failed.SetMessage("上传文件失败: " + err.Error())
}
return nil
}
@@ -101,35 +111,40 @@ func (sd storageDriver) localUpload(file *multipart.FileHeader, key string, fold
func (sd storageDriver) buildSaveName(file *multipart.FileHeader) string {
name := file.Filename
ext := strings.ToLower(path.Ext(name))
date := time.Now().Format("20060201")
date := time.Now().Format("20060102")
return path.Join(date, util.ToolsUtil.MakeUuid()+ext)
}
// checkFile 文件验证
func (sd storageDriver) checkFile(file *multipart.FileHeader, fileType int) (e error) {
fileName := file.Filename
// getFileExt 获取文件扩展名
func (sd storageDriver) getFileExt(fileName string) string {
fileExt := strings.ToLower(strings.Replace(path.Ext(fileName), ".", "", 1))
fileSize := file.Size
switch fileType {
case 10:
// 图片文件
if !util.ToolsUtil.Contains(config.FileConfig.UploadImageExt, fileExt) {
return response.Failed.SetMessage("不被支持的图片扩展: " + fileExt)
return fileExt
}
// checkFile 文件验证
func (sd storageDriver) checkFile(fileName string, fileSize int64) (e error) {
fileExt := sd.getFileExt(fileName)
if util.ToolsUtil.Contains(config.FileConfig.UploadImageExt, fileExt) {
// 图片文件
if fileSize > config.FileConfig.UploadImageSize {
return response.Failed.SetMessage("上传图片不能超出限制: " + strconv.FormatInt(config.FileConfig.UploadImageSize/1024/1024, 10) + "M")
}
case 20:
} else if util.ToolsUtil.Contains(config.FileConfig.UploadVideoExt, fileExt) {
// 视频文件
if !util.ToolsUtil.Contains(config.FileConfig.UploadVideoExt, fileExt) {
return response.Failed.SetMessage("不被支持的视频扩展: " + fileExt)
}
if fileSize > config.FileConfig.UploadVideoSize {
return response.Failed.SetMessage("上传视频不能超出限制: " + strconv.FormatInt(config.FileConfig.UploadVideoSize/1024/1024, 10) + "M")
}
default:
} else if util.ToolsUtil.Contains(config.FileConfig.UploadFileExt, fileExt) {
// 文件
if fileSize > config.FileConfig.UploadFileSize {
return response.Failed.SetMessage("上传文件不能超出限制: " + strconv.FormatInt(config.FileConfig.UploadFileSize/1024/1024, 10) + "M")
}
} else {
core.Logger.Errorf("storageDriver.checkFile fileType err: err=[unsupported fileType]")
return response.Failed.SetMessage("上传文件类型错误")
}
return nil
}

View File

@@ -9,8 +9,10 @@ type CommonUploadImageReq struct {
//CommonAlbumListReq 相册文件列表参数
type CommonAlbumListReq struct {
Cid int `form:"cid,default=-1"` // 类目ID
Type int `form:"type" binding:"omitempty,oneof=10 20 30"` // 文件类型: [10=图片, 20=视频]
// Type int `form:"type" binding:"omitempty,oneof=10 20 30"` // 文件类型: [10=图片, 20=视频]
Name string `form:"name"` // 文件名称
Ext []string `form:"ext[]"` // 文件扩展
}
//CommonAlbumRenameReq 相册文件重命名参数
@@ -28,13 +30,14 @@ type CommonAlbumMoveReq struct {
//CommonAlbumAddReq 相册文件新增参数
type CommonAlbumAddReq struct {
Cid uint `form:"cid" binding:"gte=0"` // 类目ID
Aid uint `form:"aid" binding:"gte=0"` // 管理ID
Uid uint `form:"uid" binding:"gte=0"` // 用户ID
Type int `form:"type" binding:"oneof=10 20 30"` // 文件类型: [10=图片, 20=视频,30文件]
AdminId uint `form:"admin_id" binding:"gte=0"` // 管理ID
// Uid uint `form:"uid" binding:"gte=0"` // 用户ID
// Type int `form:"type" binding:"oneof=10 20 30"` // 文件类型: [10=图片, 20=视频,30文件]
Name string `form:"name"` // 文件名称
Uri string `form:"uri"` // 文件路径
Ext string `form:"ext"` // 文件扩展
Size int64 `form:"size"` // 文件大小
Hash string `form:"hash"`
}
//CommonAlbumDelReq 相册文件删除参数
@@ -44,14 +47,14 @@ type CommonAlbumDelReq struct {
//CommonCateListReq 相册分类列表参数
type CommonCateListReq struct {
Type int `form:"type" binding:"omitempty,oneof=10 20 30"` // 分类类型: [10=图片,20=视频,30文件]
// Type int `form:"type" binding:"omitempty,oneof=10 20 30"` // 分类类型: [10=图片,20=视频,30文件]
Name string `form:"name"` // 分类名称
}
//CommonCateAddReq 相册分类新增参数
type CommonCateAddReq struct {
Pid uint `form:"pid" binding:"gte=0"` // 父级ID
Type int `form:"type" binding:"required,oneof=10 20 30"` // 分类类型: [10=图片,20=视频,30文件]
// Type int `form:"type" binding:"required,oneof=10 20 30"` // 分类类型: [10=图片,20=视频,30文件]
Name string `form:"name" binding:"required,min=1,max=30"` // 分类名称
}
@@ -70,9 +73,9 @@ type CommonCateDelReq struct {
type CommonUploadFileResp struct {
ID uint `json:"id" structs:"id"` // 主键
Cid uint `json:"cid" structs:"cid"` // 类目ID
Aid uint `json:"aid" structs:"aid"` // 管理ID
AdminId uint `json:"admin_id" structs:"admin_id"` // 管理ID
Uid uint `json:"uid" structs:"uid"` // 用户ID
Type int `json:"type" structs:"type"` // 文件类型: [10=图片, 20=视频]
// Type int `json:"type" structs:"type"` // 文件类型: [10=图片, 20=视频]
Name string `json:"name" structs:"name"` // 文件名称
Uri string `json:"url" structs:"url"` // 文件路径
Path string `json:"path" structs:"path"` // 访问地址

View File

@@ -14,22 +14,10 @@ import (
"gorm.io/gorm"
)
type IAlbumService interface {
AlbumList(page request.PageReq, listReq commonSchema.CommonAlbumListReq) (res response.PageResp, e error)
AlbumRename(id uint, name string) (e error)
AlbumMove(ids []uint, cid int) (e error)
AlbumAdd(addReq commonSchema.CommonAlbumAddReq) (res uint, e error)
AlbumDel(ids []uint) (e error)
CateList(listReq commonSchema.CommonCateListReq) (mapList []commonSchema.CommonCateListResp, e error)
CateAdd(addReq commonSchema.CommonCateAddReq) (e error)
CateRename(id uint, name string) (e error)
CateDel(id uint) (e error)
}
var AlbumService = NewAlbumService()
// NewAlbumService 初始化
func NewAlbumService() IAlbumService {
func NewAlbumService() *albumService {
db := core.GetDB()
return &albumService{db: db}
}
@@ -40,21 +28,29 @@ type albumService struct {
}
// AlbumList 相册文件列表
func (albSrv albumService) AlbumList(page request.PageReq, listReq commonSchema.CommonAlbumListReq) (res response.PageResp, e error) {
func (albSrv albumService) AlbumList(adminId uint, page request.PageReq, listReq commonSchema.CommonAlbumListReq) (res response.PageResp, e error) {
// 分页信息
limit := page.PageSize
offset := page.PageSize * (page.PageNo - 1)
// 查询
albumModel := albSrv.db.Model(&common_model.Album{}).Where("is_delete = ?", 0)
albumModel = albumModel.Where("admin_id = ?", adminId)
if listReq.Cid > 0 {
albumModel = albumModel.Where("cid = ?", listReq.Cid)
}
if listReq.Name != "" {
albumModel = albumModel.Where("name like ?", "%"+listReq.Name+"%")
}
if listReq.Type > 0 {
albumModel = albumModel.Where("type = ?", listReq.Type)
if len(listReq.Ext) > 0 {
albumModel = albumModel.Where("ext in ?", listReq.Ext)
}
// if listReq.Type > 0 {
// albumModel = albumModel.Where("type = ?", listReq.Type)
// }
// 总数
var count int64
err := albumModel.Count(&count).Error
@@ -161,12 +157,14 @@ func (albSrv albumService) AlbumDel(ids []uint) (e error) {
}
// CateList 相册分类列表
func (albSrv albumService) CateList(listReq commonSchema.CommonCateListReq) (mapList []commonSchema.CommonCateListResp, e error) {
func (albSrv albumService) CateList(adminId uint, listReq commonSchema.CommonCateListReq) (mapList []commonSchema.CommonCateListResp, e error) {
var cates []common_model.AlbumCate
cateModel := albSrv.db.Where("is_delete = ?", 0).Order("id desc")
if listReq.Type > 0 {
cateModel = cateModel.Where("type = ?", listReq.Type)
}
// if listReq.Type > 0 {
// cateModel = cateModel.Where("type = ?", listReq.Type)
// }
cateModel = cateModel.Where("admin_id = ?", adminId)
if listReq.Name != "" {
cateModel = cateModel.Where("name like ?", "%"+listReq.Name+"%")
}
@@ -180,9 +178,12 @@ func (albSrv albumService) CateList(listReq commonSchema.CommonCateListReq) (map
}
// CateAdd 分类新增
func (albSrv albumService) CateAdd(addReq commonSchema.CommonCateAddReq) (e error) {
func (albSrv albumService) CateAdd(adminId uint, addReq commonSchema.CommonCateAddReq) (e error) {
var cate common_model.AlbumCate
convert_util.Copy(&cate, addReq)
cate.AdminId = adminId
err := albSrv.db.Create(&cate).Error
e = response.CheckErr(err, "Cate添加失败")
return

View File

@@ -1,18 +1,16 @@
package commonService
import (
"fmt"
"mime/multipart"
"time"
"x_admin/plugin"
"x_admin/schema/commonSchema"
"x_admin/util"
"x_admin/util/convert_util"
)
type IUploadService interface {
UploadImage(file *multipart.FileHeader, cid uint, aid uint) (res commonSchema.CommonUploadFileResp, e error)
UploadVideo(file *multipart.FileHeader, cid uint, aid uint) (res commonSchema.CommonUploadFileResp, e error)
}
var UploadService = NewUploadService()
// NewUploadService 初始化
@@ -23,26 +21,34 @@ func NewUploadService() *uploadService {
// uploadService 上传服务实现类
type uploadService struct{}
// UploadImage 上传图片
func (upSrv uploadService) UploadImage(file *multipart.FileHeader, cid uint, aid uint) (res commonSchema.CommonUploadFileResp, e error) {
return upSrv.uploadFile(file, "image", 10, cid, aid)
}
// UploadVideo 上传视频
func (upSrv uploadService) UploadVideo(file *multipart.FileHeader, cid uint, aid uint) (res commonSchema.CommonUploadFileResp, e error) {
return upSrv.uploadFile(file, "video", 20, cid, aid)
}
// uploadFile 上传文件
func (upSrv uploadService) uploadFile(file *multipart.FileHeader, folder string, fileType int, cid uint, aid uint) (res commonSchema.CommonUploadFileResp, e error) {
// UploadFile 上传
// cid 分类id
// AdminId 用户id
func (upSrv uploadService) UploadFile(file *multipart.FileHeader, cid uint, AdminId uint) (res commonSchema.CommonUploadFileResp, e error) {
var upRes *plugin.UploadFile
if upRes, e = plugin.StorageDriver.Upload(file, folder, fileType); e != nil {
if upRes, e = plugin.StorageDriver.Upload(file); e != nil {
return
}
var startTime = time.Now()
// 计算文件MD5
md5, e := util.ToolsUtil.GetFileMD5(file)
if e != nil {
return
}
var endTime = time.Now()
var costTime = endTime.UnixNano() - startTime.UnixNano()
// 毫秒
costTime = costTime / 1000000
fmt.Printf("\n文件大小%d , md5时间 %d毫秒\n", file.Size, costTime)
var addReq commonSchema.CommonAlbumAddReq
convert_util.Copy(&addReq, upRes)
addReq.Aid = aid
addReq.Cid = cid
addReq.AdminId = AdminId //管理员
addReq.Cid = cid // 分类id
addReq.Hash = md5
var albumId uint
if albumId, e = AlbumService.AlbumAdd(addReq); e != nil {
return

View File

@@ -4,8 +4,10 @@ import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"io"
"math"
"math/rand"
"mime/multipart"
"os"
"reflect"
"strconv"
@@ -45,6 +47,20 @@ func (tu toolsUtil) MakeMd5(data string) string {
return hex.EncodeToString(sum[:])
}
// GetFileMD5 获取文件MD5
func (tu toolsUtil) GetFileMD5(file *multipart.FileHeader) (string, error) {
f, err := file.Open()
if err != nil {
return "", err
}
defer f.Close()
hash := md5.New()
if _, err := io.Copy(hash, f); err != nil {
return "", err
}
return hex.EncodeToString(hash.Sum(nil)), nil
}
// MakeToken 生成唯一Token
func (tu toolsUtil) MakeToken() string {
ms := time.Now().UnixMilli()