多个文件上传

本文涉及的产品
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS AI 助手,专业版
简介: 多个文件上传

 ♥️作者:小宋1021

🤵‍♂️个人主页:小宋1021主页

♥️坚持分析平时学习到的项目以及学习到的软件开发知识,和大家一起努力呀!!!

🎈🎈加油! 加油! 加油! 加油

🎈欢迎评论 💬点赞👍🏻 收藏 📂加关注+


目录

编辑

前端

html

js

useNewUpload.ts

FileApi

数据库

后端

项目构成

主表

实体类

ResearchingManageSaveReqVO

ResearchingManageRespVO

Mapper

service

controller

子表

实体类

Mapper


image.gif 编辑

前端

html

<div style="padding: 8px 0; background: #f8fbff">
        <el-upload
          name="file"
          ref="uploadRef"
          class="upload-file-uploader"
          v-model:file-list="fileList"
          :action="uploadUrl"
          :http-request="httpRequest"
          :auto-upload="autoUpload"
          :before-upload="beforeUpload"
          :drag="drag"
          :limit="props.limit"
          :multiple="props.limit > 1"
          :on-error="excelUploadError"
          :on-exceed="handleExceed"
          :on-remove="handleRemove"
          :on-success="handleFileSuccess"
        >
          <el-button type="primary">
            <Icon icon="ep:upload-filled" />
            选取文件
          </el-button>
          <template v-if="isShowTip" #tip>
            <div style="font-size: 10px">
              1.格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b> 的文件; 2.大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
            </div>
          </template>
        </el-upload>
        
        <el-table ref="multipleTableRef" :data="uploadList" style="width: 100%; margin-top: 10px; height: 200px; width:660px">
          <el-table-column label="序号" type="index"  header-align="center" align="center" width="60px" fixed />
          <el-table-column label="文件名称" align="center" prop="name" :show-overflow-tooltip="true" />
          <el-table-column label="文件路径" align="center" prop="url" :show-overflow-tooltip="true" />
          <el-table-column label="操作" align="center" fixed="right" width="140px">
            <template #default="scope">
              <el-button
                size="small"
                link
                type="primary"
                @click.prevent="deleteFileRow(scope.$index, uploadList)"
                >删除</el-button
              >
            </template>
          </el-table-column>
        </el-table>
      </div>

image.gif

js

import type { UploadInstance, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus'
import { useNewUpload } from '@/components/UploadFile/src/useUpload'
import { CommonStatusEnum } from '@/utils/constants'
import { UploadFile } from 'element-plus/es/components/upload/src/upload'
import { propTypes } from '@/utils/propTypes'
// ========== 上传相关 ==========
const uploadRef = ref<UploadInstance>()
const uploadList = ref<UploadUserFile[]>([])
const fileList = ref<UploadUserFile[]>([])
const uploadNumber = ref<number>(0)
const props = defineProps({
  modelValue: propTypes.oneOfType<any>([]).isRequired,
  title: propTypes.string.def('文件上传'),
  fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf']), // 文件类型, 例如['png', 'jpg', 'jpeg']
  fileSize: propTypes.number.def(5), // 大小限制(MB)
  limit: propTypes.number.def(5), // 数量限制
  autoUpload: propTypes.bool.def(true), // 自动上传
  drag: propTypes.bool.def(false), // 拖拽上传
  isShowTip: propTypes.bool.def(true) // 是否显示提示
})
const fileData = ref<any>([])
const formData = ref({
  id: undefined,
  courseType: undefined,
  subject: undefined,
  fileType: undefined,
  appendixName: undefined,
  creator:  userStore.user.id,
  appendixFile: undefined,
  createTime: undefined,
  title: undefined,
  remark: undefined,
  courseId: undefined,
  fileData: undefined,
  fileExists: undefined,
})
// 文件上传之前判断
const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
  if (fileList.value.length >= props.limit) {
    message.error(`上传文件数量不能超过${props.limit}个!`)
    return false
  }
  let fileExtension = ''
  if (file.name.lastIndexOf('.') > -1) {
    fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1)
  }
  const isImg = props.fileType.some((type: string) => {
    if (file.type.indexOf(type) > -1) return true
    return !!(fileExtension && fileExtension.indexOf(type) > -1)
  })
  const isLimit = file.size < props.fileSize * 1024 * 1024
  if (!isImg) {
    message.error(`文件格式不正确, 请上传${props.fileType.join('/')}格式!`)
    return false
  }
  if (!isLimit) {
    message.error(`上传文件大小不能超过${props.fileSize}MB!`)
    return false
  }
  const existFile = fileList.value.slice(0, fileList.value.length - 1).find(f => f.name === file.name)
  if (existFile) {
    message.error(`当前文件已经存在!`)
    return false
  }
  message.success('正在上传文件,请稍候...')
  uploadNumber.value++
}
const deleteFileRow = (index, rows) => {
  rows.splice(index, 1)
}
// 文件上传成功
const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
  uploadList.value.push({ uid: res.data.id, name: res.data.name, url: res.data.url })
  message.success('上传成功')
}
// 文件数超出提示
const handleExceed: UploadProps['onExceed'] = (): void => {
  message.error(`上传文件数量不能超过${props.limit}个!`)
}
// 上传错误提示
const excelUploadError: UploadProps['onError'] = (): void => {
  message.error('导入数据失败,请您重新上传!')
}
// 删除上传文件
const handleRemove = (file: UploadFile) => {
  const index = fileList.value.map((f) => f.name).indexOf(file.name)
  if (index > -1) {
    fileList.value.splice(index, 1)
  }
}
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
  // 校验表单
  await formRef.value.validate()
  // 提交请求
  formLoading.value = true
  fileData.value = []
  for (let i = 0; i < uploadList.value.length; i++) {
    const UserCourse = {
      fileId: uploadList.value[i].uid
    }
    fileData.value.push(UserCourse)
  }
  try {
    const data = formData.value as unknown as ResearchingManageVO
    data.fileData = fileData.value
    if (uploadList && uploadList.value.length > 0) {
      data.fileExists = 97
    } else {
      data.fileExists = 98
    }
    // if (flag === 1) {
    //   data.status = CommonStatusEnum.DISABLE
    // } else {
    //   data.status = CommonStatusEnum.ENABLE
    // }
    if (formType.value === 'create') {
      await ResearchingManageApi.createResearchingManage(data)
      message.success(t('common.createSuccess'))
    } else {
      await ResearchingManageApi.updateResearchingManage(data)
      message.success(t('common.updateSuccess'))
    }
    dialogVisible.value = false
    // 发送操作成功的事件
    emit('success')
  } finally {
    formLoading.value = false
  }
}
/** 重置表单 */
const resetForm = () => {
  formData.value = {
    id: undefined,
  courseType: undefined,
  subject: undefined,
  fileType: undefined,
  appendixName: undefined,
  creator:  userStore.user.id,
  appendixFile: undefined,
  createTime: undefined,
  title: undefined,
  remark: undefined,
  courseId: undefined,
  fileData: undefined,
  fileExists: undefined,
  }
  formRef.value?.resetFields()
}

image.gif

useNewUpload.ts

import * as FileApi from '@/api/infra/file'
import CryptoJS from 'crypto-js'
import { UploadRawFile, UploadRequestOptions } from 'element-plus/es/components/upload/src/upload'
import axios from 'axios'
export const useUpload = () => {
  // 后端上传地址
  const uploadUrl = import.meta.env.VITE_UPLOAD_URL
  // 是否使用前端直连上传
  const isClientUpload = UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE
  // 重写ElUpload上传方法
  const httpRequest = async (options: UploadRequestOptions) => {
    // 模式一:前端上传
    if (isClientUpload) {
      // 1.1 生成文件名称
      const fileName = await generateFileName(options.file)
      // 1.2 获取文件预签名地址
      const presignedInfo = await FileApi.getFilePresignedUrl(fileName)
      // 1.3 上传文件(不能使用 ElUpload 的 ajaxUpload 方法的原因:其使用的是 FormData 上传,Minio 不支持)
      return axios.put(presignedInfo.uploadUrl, options.file).then(() => {
        // 1.4. 记录文件信息到后端(异步)
        createFile(presignedInfo, fileName, options.file)
        // 通知成功,数据格式保持与后端上传的返回结果一致
        return { data: presignedInfo.url }
      })
    } else {
      // 模式二:后端上传
      // 重写 el-upload httpRequest 文件上传成功会走成功的钩子,失败走失败的钩子
      return new Promise((resolve, reject) => {
        FileApi.updateFile({ file: options.file })
          .then((res) => {
            if (res.code === 0) {
              resolve(res)
            } else {
              reject(res)
            }
          })
          .catch((res) => {
            reject(res)
          })
      })
    }
  }
  return {
    uploadUrl,
    httpRequest
  }
}
/**
 * 创建文件信息
 * @param vo 文件预签名信息
 * @param name 文件名称
 * @param file 文件
 */
function createFile(vo: FileApi.FilePresignedUrlRespVO, name: string, file: UploadRawFile) {
  const fileVo = {
    configId: vo.configId,
    url: vo.url,
    path: name,
    name: file.name,
    type: file.type,
    size: file.size
  }
  FileApi.createFile(fileVo)
  return fileVo
}
/**
 * 生成文件名称(使用算法SHA256)
 * @param file 要上传的文件
 */
async function generateFileName(file: UploadRawFile) {
  // 读取文件内容
  const data = await file.arrayBuffer()
  const wordArray = CryptoJS.lib.WordArray.create(data)
  // 计算SHA256
  const sha256 = CryptoJS.SHA256(wordArray).toString()
  // 拼接后缀
  const ext = file.name.substring(file.name.lastIndexOf('.'))
  return `${sha256}${ext}`
}
/**
 * 上传类型
 */
enum UPLOAD_TYPE {
  // 客户端直接上传(只支持S3服务)
  CLIENT = 'client',
  // 客户端发送到后端上传
  SERVER = 'server'
}
export const useNewUpload = () => {
  // 后端上传地址
  const uploadUrl = import.meta.env.VITE_UPLOAD_URL
  // 是否使用前端直连上传
  const isClientUpload = UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE
  // 重写ElUpload上传方法
  const httpRequest = async (options: UploadRequestOptions) => {
    // 模式一:前端上传
    if (isClientUpload) {
      // 1.1 生成文件名称
      const fileName = await generateFileName(options.file)
      // 1.2 获取文件预签名地址
      const presignedInfo = await FileApi.getFilePresignedUrl(fileName)
      // 1.3 上传文件(不能使用 ElUpload 的 ajaxUpload 方法的原因:其使用的是 FormData 上传,Minio 不支持)
      return axios.put(presignedInfo.uploadUrl, options.file).then(() => {
        // 1.4. 记录文件信息到后端(异步)
        createFile(presignedInfo, fileName, options.file)
        // 通知成功,数据格式保持与后端上传的返回结果一致
        return { data: presignedInfo.url }
      })
    } else {
      // 模式二:后端上传
      // 重写 el-upload httpRequest 文件上传成功会走成功的钩子,失败走失败的钩子
      return new Promise((resolve, reject) => {
        FileApi.uploadFile({ file: options.file })
          .then((res) => {
            if (res.code === 0) {
              resolve(res)
            } else {
              reject(res)
            }
          })
          .catch((res) => {
            reject(res)
          })
      })
    }
  }
  return {
    uploadUrl,
    httpRequest
  }
}

image.gif

FileApi

import request from '@/config/axios'
export interface FilePageReqVO extends PageParam {
  path?: string
  type?: string
  createTime?: Date[]
}
// 文件预签名地址 Response VO
export interface FilePresignedUrlRespVO {
  // 文件配置编号
  configId: number
  // 文件上传 URL
  uploadUrl: string
  // 文件 URL
  url: string
}
// 查询文件列表
export const getFilePage = (params: FilePageReqVO) => {
  return request.get({ url: '/infra/file/page', params })
}
// 删除文件
export const deleteFile = (id: number) => {
  return request.delete({ url: '/infra/file/delete?id=' + id })
}
// 获取文件预签名地址
export const getFilePresignedUrl = (path: string) => {
  return request.get<FilePresignedUrlRespVO>({
    url: '/infra/file/presigned-url',
    params: { path }
  })
}
// 创建文件
export const createFile = (data: any) => {
  return request.post({ url: '/infra/file/create', data })
}
// 上传文件
export const updateFile = (data: any) => {
  return request.upload({ url: '/infra/file/upload', data })
}
// 上传文件
export const uploadFile = (data: any) => {
  return request.upload({ url: '/infra/file/uploadFile', data })
}

image.gif

数据库

创建一个上传文件的子表,字段如下:

image.gif 编辑

image.gif 编辑

主表字段如下:

image.gif 编辑

后端

项目构成

image.gif 编辑

image.gif 编辑

主表

实体类

package com.todod.education.module.teach.dal.dataobject.researchingmanage;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import com.todod.education.framework.mybatis.core.dataobject.BaseDO;
/**
 * 教研管理 DO
 *
 * @author 平台管理员
 */
@TableName("teach_researching_manage")
@KeySequence("teach_researching_manage_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ResearchingManageDO extends BaseDO {
    /**
     * 主键id
     */
    @TableId
    private Long id;
    /**
     * 课程产品 课程id
     */
    private Long courseId;
    /**
     * 标题
     */
    private String title;
    /**
     * 课程类型
     */
    private String courseType;
    /**
     * 课程名称
     */
    @TableField(exist = false)
    private String courseName;
    /**
     * 科目
     *
     */
    private String subject;
    /**
     * 文件类型
     */
    private String fileType;
    /**
     * 附件名称
     */
    private String appendixName;
    /**
     * 附件
     */
    private String appendixFile;
    /**
     * 附件(97:有/98:无)
     */
    private Short fileExists;
    /**
     * 备注
     */
    private String remark;
    /**
     * 教案管理附件id
     */
    @TableField(exist = false)
    private Long teachResearchingManageId;
}

image.gif

ResearchingManageSaveReqVO

package com.todod.education.module.teach.controller.admin.researchingmanage.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import com.todod.education.module.teach.dal.dataobject.researchingmanage.ResearchingManageFileDO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import jakarta.validation.constraints.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 教研管理新增/修改 Request VO")
@Data
public class ResearchingManageSaveReqVO {
    @Schema(description = "主键id", requiredMode = Schema.RequiredMode.REQUIRED, example = "13167")
    private Long id;
    @Schema(description = "课程产品 课程id", requiredMode = Schema.RequiredMode.REQUIRED, example = "13167")
    private Long courseId;
    @Schema(description = "标题", example = "2")
    private String title;
    @Schema(description = "课程类型", example = "2")
    private String courseType;
    @Schema(description = "科目")
    private String subject;
    @Schema(description = "文件类型", example = "2")
    private String fileType;
    @Schema(description = "附件名称", example = "芋艿")
    private String appendixName;
    @Schema(description = "创建人")
    private String creator;
    @Schema(description = "附件(97:有/98:无)", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("附件(97:有/98:无)")
    private Short fileExists;
    @Schema(description = "创建时间")
    private LocalDateTime createTime;
//
//    @Schema(description = "附件")
//    private String appendixFile;
    @Schema(description = "备注")
    private String remark;
    @Schema(description = "备注")
    private String courseName;
//    @Schema(description = "附件子表数据", example = "测试")
//    private List<ResearchingManageFileDO> appendixFile;
    @Schema(description = "附件子表数据", example = "测试")
    private List<ResearchingManageFileDO> fileData;
}

image.gif

ResearchingManageRespVO

package com.todod.education.module.teach.controller.admin.researchingmanage.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
import com.todod.education.framework.excel.core.annotations.DictFormat;
import com.todod.education.framework.excel.core.convert.DictConvert;
@Schema(description = "管理后台 - 教研管理 Response VO")
@Data
@ExcelIgnoreUnannotated
public class ResearchingManageRespVO {
    @Schema(description = "主键id", requiredMode = Schema.RequiredMode.REQUIRED, example = "20302")
    private Long id;
    @Schema(description = "课程产品 课程id", requiredMode = Schema.RequiredMode.REQUIRED, example = "20302")
    private Long courseId;
    @Schema(description = "标题", example = "2")
    @ExcelProperty("标题")
    private String title;
    @Schema(description = "课程名称", example = "2")
    @ExcelProperty("课程名称")
    private String courseName;
    @Schema(description = "课程类型", example = "2")
    @ExcelProperty("课程类型")
    private String courseType;
    @Schema(description = "科目")
    @ExcelProperty(value = "科目", converter = DictConvert.class)
    @DictFormat("subject") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
    private String subject;
    @Schema(description = "文件类型", example = "2")
    @ExcelProperty("文件类型")
    private String fileType;
    @Schema(description = "附件名称", example = "芋艿")
    @ExcelProperty("附件名称")
    private String appendixName;
    @Schema(description = "创建人")
    private Long creator;
    @Schema(description = "创建时间")
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
    @Schema(description = "最后编辑人")
    private Long updater;
    @Schema(description = "最后编辑时间")
    @ExcelProperty("最后编辑时间")
    private LocalDateTime updateTime;
    @Schema(description = "附件")
    private String appendixFile;
    @Schema(description = "备注")
    private String remark;
    @Schema(description = "教研管理附件id", requiredMode = Schema.RequiredMode.REQUIRED, example = "20302")
    private Long teachResearchingManageId;
    @Schema(description = "附件(97:有/98:无)", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("附件(97:有/98:无)")
    private Short fileExists;
}

image.gif

Mapper

package com.todod.education.module.teach.dal.mysql.researchingmanage;
import com.todod.education.framework.mybatis.core.mapper.BaseMapperX;
import com.todod.education.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.todod.education.module.teach.dal.dataobject.researchingmanage.ResearchingManageFileDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
 * 教研管理文件 Mapper
 *
 * @author 平台管理员
 */
@Mapper
public interface ResearchingManageFileMapper extends BaseMapperX<ResearchingManageFileDO> {
    /**
     * 获得教研管理附件列表
     *
     * @param teacherResearchingManageId 公函明细
     * @return 教研管理文件明细列表
     */
    default List<ResearchingManageFileDO> getSimpleFileList(Long teacherResearchingManageId) {
        return selectList(new LambdaQueryWrapperX<ResearchingManageFileDO>()
                .eqIfPresent(ResearchingManageFileDO::getTeachResearchingManageId, teacherResearchingManageId)
        );
    }
}

image.gif

service

package com.todod.education.module.teach.service.researchingmanage;
import java.util.*;
import jakarta.validation.*;
import com.todod.education.module.teach.controller.admin.researchingmanage.vo.*;
import com.todod.education.module.teach.dal.dataobject.researchingmanage.ResearchingManageDO;
import com.todod.education.framework.common.pojo.PageResult;
import com.todod.education.framework.common.pojo.PageParam;
/**
 * 教研管理 Service 接口
 *
 * @author 平台管理员
 */
public interface ResearchingManageService {
    /**
     * 创建教研管理
     *
     * @param createReqVO 创建信息
     * @return 编号
     */
    Long createResearchingManage(@Valid ResearchingManageSaveReqVO createReqVO);
    /**
     * 更新教研管理
     *
     * @param updateReqVO 更新信息
     */
    void updateResearchingManage(@Valid ResearchingManageSaveReqVO updateReqVO);
}

image.gif

package com.todod.education.module.teach.service.researchingmanage;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.todod.education.module.teach.dal.dataobject.researchingmanage.ResearchingManageFileDO;
import com.todod.education.module.teach.dal.mysql.researchingmanage.ResearchingManageFileMapper;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import com.todod.education.module.teach.controller.admin.researchingmanage.vo.*;
import com.todod.education.module.teach.dal.dataobject.researchingmanage.ResearchingManageDO;
import com.todod.education.framework.common.pojo.PageResult;
import com.todod.education.framework.common.pojo.PageParam;
import com.todod.education.framework.common.util.object.BeanUtils;
import com.todod.education.module.teach.dal.mysql.researchingmanage.ResearchingManageMapper;
import static com.todod.education.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.todod.education.module.teach.enums.ErrorCodeConstants.*;
/**
 * 教研管理 Service 实现类
 *
 * @author 平台管理员
 */
@Service
@Validated
public class ResearchingManageServiceImpl implements ResearchingManageService {
    @Resource
    private ResearchingManageMapper researchingManageMapper;
    @Resource
    private ResearchingManageFileMapper researchingManageFileMapper;
    @Override
    public Long createResearchingManage(ResearchingManageSaveReqVO createReqVO) {
        // 插入
        ResearchingManageDO researchingManage = BeanUtils.toBean(createReqVO, ResearchingManageDO.class);
        researchingManageMapper.insert(researchingManage);
        //往文件子表中插入数据
        for (ResearchingManageFileDO fileDO : createReqVO.getFileData()) {
            ResearchingManageFileDO researchingManageFileDO = new ResearchingManageFileDO();
            researchingManageFileDO.setTeachResearchingManageId(researchingManage.getId());
            researchingManageFileDO.setFileId(fileDO.getId());
            researchingManageFileMapper.insert(researchingManageFileDO);
        }
        // 返回
        return researchingManage.getId();
    }
    @Override
    public void updateResearchingManage(ResearchingManageSaveReqVO updateReqVO) {
        // 校验存在
        validateResearchingManageExists(updateReqVO.getId());
        // 更新
        ResearchingManageDO updateObj = BeanUtils.toBean(updateReqVO, ResearchingManageDO.class);
        researchingManageMapper.updateById(updateObj);
        //往文件子表中插入数据
        for (ResearchingManageFileDO fileDO : updateReqVO.getFileData()) {
            ResearchingManageFileDO researchingManageFileDO = new ResearchingManageFileDO();
            researchingManageFileDO.setTeachResearchingManageId(updateObj.getId());
            researchingManageFileDO.setFileId(fileDO.getId());
            researchingManageFileMapper.insert(researchingManageFileDO);
        }
    }
}

image.gif

controller

package com.todod.education.module.teach.controller.admin.researchingmanage;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.constraints.*;
import jakarta.validation.*;
import jakarta.servlet.http.*;
import java.util.*;
import java.io.IOException;
import com.todod.education.framework.common.pojo.PageParam;
import com.todod.education.framework.common.pojo.PageResult;
import com.todod.education.framework.common.pojo.CommonResult;
import com.todod.education.framework.common.util.object.BeanUtils;
import static com.todod.education.framework.common.pojo.CommonResult.success;
import com.todod.education.framework.excel.core.util.ExcelUtils;
import com.todod.education.framework.apilog.core.annotation.ApiAccessLog;
import static com.todod.education.framework.apilog.core.enums.OperateTypeEnum.*;
import com.todod.education.module.teach.controller.admin.researchingmanage.vo.*;
import com.todod.education.module.teach.dal.dataobject.researchingmanage.ResearchingManageDO;
import com.todod.education.module.teach.service.researchingmanage.ResearchingManageService;
@Tag(name = "管理后台 - 教研管理")
@RestController
@RequestMapping("/teach/researching-manage")
@Validated
public class ResearchingManageController {
    @Resource
    private ResearchingManageService researchingManageService;
    @PostMapping("/create")
    @Operation(summary = "创建教研管理")
    @PreAuthorize("@ss.hasPermission('teach:researching-manage:create')")
    public CommonResult<Long> createResearchingManage(@Valid @RequestBody ResearchingManageSaveReqVO createReqVO) {
        return success(researchingManageService.createResearchingManage(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新教研管理")
    @PreAuthorize("@ss.hasPermission('teach:researching-manage:update')")
    public CommonResult<Boolean> updateResearchingManage(@Valid @RequestBody ResearchingManageSaveReqVO updateReqVO) {
        researchingManageService.updateResearchingManage(updateReqVO);
        return success(true);
    }
}

image.gif

子表

实体类

package com.todod.education.module.teach.dal.dataobject.researchingmanage;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.todod.education.framework.mybatis.core.dataobject.BaseDO;
import lombok.*;
/**
 * 教研管理文件 DO
 *
 * @author 平台管理员
 */
@TableName("teach_researching_manage_file")
@KeySequence("teach_researching_manage_file_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ResearchingManageFileDO extends BaseDO {
    /**
     * 主键id
     */
    @TableId
    private Long id;
    /**
     * 教研管理id
     */
    private Long teachResearchingManageId;
    /**
     * 附件id
     */
    private Long fileId;
}

image.gif

Mapper

package com.todod.education.module.teach.dal.mysql.researchingmanage;
import com.todod.education.framework.mybatis.core.mapper.BaseMapperX;
import com.todod.education.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.todod.education.module.teach.dal.dataobject.researchingmanage.ResearchingManageFileDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
 * 教研管理文件 Mapper
 *
 * @author 平台管理员
 */
@Mapper
public interface ResearchingManageFileMapper extends BaseMapperX<ResearchingManageFileDO> {
    /**
     * 获得教研管理附件列表
     *
     * @param teacherResearchingManageId 公函明细
     * @return 教研管理文件明细列表
     */
    default List<ResearchingManageFileDO> getSimpleFileList(Long teacherResearchingManageId) {
        return selectList(new LambdaQueryWrapperX<ResearchingManageFileDO>()
                .eqIfPresent(ResearchingManageFileDO::getTeachResearchingManageId, teacherResearchingManageId)
        );
    }
}

image.gif


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
前端开发 JavaScript 安全
【前端相关】elementui使用el-upload组件实现自定义上传
【前端相关】elementui使用el-upload组件实现自定义上传
3511 0
|
移动开发 小程序
知识付费小程序注册时类目该如何选择?
知识付费小程序注册时类目该如何选择?
1098 0
|
JavaScript
【ElementUI】Vue+ElementUI多文件上传,一次请求上传多个文件!
教大家一次请求,上传多个文件。 ElementUI如果是默认方案,上传多张图片并不是真正的一次上传多张,而是发送多次请求,一次请求携带一张图片。
1532 0
element-el-time-picker 开始时间-结束时间-回显(整理)
element-el-time-picker 开始时间-结束时间-回显(整理)
|
网络协议 前端开发 Unix
QEMU&KVM-2 Live Migration
虚拟机的迁移是指把一台VM上的OS迁移到另外一台VM,两个VM可以run在不同的物理机上。 包括:Offline Migration和Live Migration。这里讲讲比较常用的Live Migration(热迁移)。 在热迁移过程中,Guest OS完全无感,其运行的任务,在快速迁移过后能继续运行。 首先,对于Guest OS从一个VM迁移到其他VM,涉及到对register配置,di
8347 0
QEMU&KVM-2 Live Migration
|
JavaScript 前端开发
Element_文件上传&&多个文件上传
Element_文件上传&&多个文件上传
1198 0
|
11月前
|
人工智能 自然语言处理 机器人
今日AI论文推荐:ReCamMaster、PLADIS、SmolDocling、FlowTok
由浙江大学、快手科技等机构提出的ReCamMaster是一个相机控制的生成式视频重渲染框架,可以使用新的相机轨迹重现输入视频的动态场景。该工作的核心创新在于利用预训练的文本到视频模型的生成能力,通过一种简单但强大的视频条件机制。为克服高质量训练数据的稀缺问题,研究者使用虚幻引擎5构建了一个全面的多相机同步视频数据集,涵盖多样化的场景和相机运动。
577 2
今日AI论文推荐:ReCamMaster、PLADIS、SmolDocling、FlowTok
|
人工智能 监控 安全
云端问道18期实践教学-AI 浪潮下的数据安全管理实践
本文主要介绍AI浪潮下的数据安全管理实践,主要分为背景介绍、Access Point、Bucket三个部分
461 54
|
8月前
|
存储 API 内存技术
GD32通过SPI和QSPI模式读取GD的NOR Flash
GD32通过SPI和QSPI模式读取GD的NOR Flash
1252 2
|
SQL 监控 关系型数据库
MySQL如何查看每个分区的数据量
通过本文的介绍,您可以使用MySQL的 `INFORMATION_SCHEMA`查询每个分区的数据量。了解分区数据量对数据库优化和管理具有重要意义,可以帮助您优化查询性能、平衡数据负载和监控数据库健康状况。希望本文对您在MySQL分区管理和性能优化方面有所帮助。
1148 1