使用MinIO存储个人头像
This commit is contained in:
parent
c646c95c7b
commit
478c128ba2
26
flex-ui/src/api/system/oss.js
Normal file
26
flex-ui/src/api/system/oss.js
Normal file
@ -0,0 +1,26 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 查询OSS对象存储列表
|
||||
export function listOss(query) {
|
||||
return request({
|
||||
url: '/resource/oss/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
// 查询OSS对象基于id串
|
||||
export function listByIds(ossId) {
|
||||
return request({
|
||||
url: '/resource/oss/listByIds/' + ossId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 删除OSS对象存储
|
||||
export function delOss(ossId) {
|
||||
return request({
|
||||
url: '/resource/oss/' + ossId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
58
flex-ui/src/api/system/ossConfig.js
Normal file
58
flex-ui/src/api/system/ossConfig.js
Normal file
@ -0,0 +1,58 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 查询对象存储配置列表
|
||||
export function listOssConfig(query) {
|
||||
return request({
|
||||
url: '/resource/oss/config/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
// 查询对象存储配置详细
|
||||
export function getOssConfig(ossConfigId) {
|
||||
return request({
|
||||
url: '/resource/oss/config/' + ossConfigId,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 新增对象存储配置
|
||||
export function addOssConfig(data) {
|
||||
return request({
|
||||
url: '/resource/oss/config',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 修改对象存储配置
|
||||
export function updateOssConfig(data) {
|
||||
return request({
|
||||
url: '/resource/oss/config',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 删除对象存储配置
|
||||
export function delOssConfig(ossConfigId) {
|
||||
return request({
|
||||
url: '/resource/oss/config/' + ossConfigId,
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
// 对象存储状态修改
|
||||
export function changeOssConfigStatus(ossConfigId, status, configKey) {
|
||||
const data = {
|
||||
ossConfigId,
|
||||
status,
|
||||
configKey
|
||||
};
|
||||
return request({
|
||||
url: '/resource/oss/config/changeStatus',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
}
|
@ -104,6 +104,7 @@ export function updateUserPwd(oldPassword, newPassword) {
|
||||
export function uploadAvatar(data) {
|
||||
return request({
|
||||
url: '/system/user/profile/avatar',
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
|
@ -39,7 +39,8 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getToken } from "@/utils/auth";
|
||||
import { listByIds, delOss } from "@/api/system/oss";
|
||||
import { globalHeaders } from "@/utils/request";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: [String, Object, Array],
|
||||
@ -56,7 +57,7 @@ const props = defineProps({
|
||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileType: {
|
||||
type: Array,
|
||||
default: () => ["doc", "xls", "ppt", "txt", "pdf"],
|
||||
default: () => ["doc","docx", "xls", "xlsx", "ppt", "txt", "pdf"],
|
||||
},
|
||||
// 是否显示提示
|
||||
isShowTip: {
|
||||
@ -66,27 +67,34 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const emit = defineEmits();
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const number = ref(0);
|
||||
const uploadList = ref([]);
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
||||
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传文件服务器地址
|
||||
const headers = ref({ Authorization: "Bearer " + getToken() });
|
||||
const uploadFileUrl = ref(baseUrl + "/resource/oss/upload"); // 上传文件服务器地址
|
||||
const headers = ref(globalHeaders());
|
||||
const fileList = ref([]);
|
||||
const showTip = computed(
|
||||
() => props.isShowTip && (props.fileType || props.fileSize)
|
||||
);
|
||||
|
||||
watch(() => props.modelValue, val => {
|
||||
watch(() => props.modelValue, async val => {
|
||||
if (val) {
|
||||
let temp = 1;
|
||||
// 首先将值转为数组
|
||||
const list = Array.isArray(val) ? val : props.modelValue.split(',');
|
||||
let list = [];
|
||||
if (Array.isArray(val)) {
|
||||
list = val;
|
||||
} else {
|
||||
const res = await listByIds(val)
|
||||
list = res.data.map((oss) => {
|
||||
const data = { name: oss.originalName, url: oss.url, ossId: oss.ossId };
|
||||
return data;
|
||||
});
|
||||
}
|
||||
// 然后将数组转为对象数组
|
||||
fileList.value = list.map(item => {
|
||||
if (typeof item === "string") {
|
||||
item = { name: item, url: item };
|
||||
}
|
||||
item = { name: item.name, url: item.url, ossId: item.ossId };
|
||||
item.uid = item.uid || new Date().getTime() + temp++;
|
||||
return item;
|
||||
});
|
||||
@ -134,7 +142,7 @@ function handleUploadError(err) {
|
||||
// 上传成功回调
|
||||
function handleUploadSuccess(res, file) {
|
||||
if (res.code === 200) {
|
||||
uploadList.value.push({ name: res.fileName, url: res.fileName });
|
||||
uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
|
||||
uploadedSuccessfully();
|
||||
} else {
|
||||
number.value--;
|
||||
@ -147,6 +155,8 @@ function handleUploadSuccess(res, file) {
|
||||
|
||||
// 删除文件
|
||||
function handleDelete(index) {
|
||||
let ossId = fileList.value[index].ossId;
|
||||
delOss(ossId);
|
||||
fileList.value.splice(index, 1);
|
||||
emit("update:modelValue", listToString(fileList.value));
|
||||
}
|
||||
@ -167,7 +177,7 @@ function getFileName(name) {
|
||||
if (name.lastIndexOf("/") > -1) {
|
||||
return name.slice(name.lastIndexOf("/") + 1);
|
||||
} else {
|
||||
return "";
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,12 +185,12 @@ function getFileName(name) {
|
||||
function listToString(list, separator) {
|
||||
let strs = "";
|
||||
separator = separator || ",";
|
||||
for (let i in list) {
|
||||
if (list[i].url) {
|
||||
strs += list[i].url + separator;
|
||||
list.forEach(item => {
|
||||
if (item.ossId) {
|
||||
strs += item.ossId + separator;
|
||||
}
|
||||
}
|
||||
return strs != '' ? strs.substr(0, strs.length - 1) : '';
|
||||
})
|
||||
return strs != "" ? strs.substring(0, strs.length - 1) : "";
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -46,7 +46,8 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getToken } from "@/utils/auth";
|
||||
import { listByIds, delOss } from "@/api/system/oss";
|
||||
import {globalHeaders} from "@/utils/request";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: [String, Object, Array],
|
||||
@ -73,33 +74,40 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const emit = defineEmits();
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const number = ref(0);
|
||||
const uploadList = ref([]);
|
||||
const dialogImageUrl = ref("");
|
||||
const dialogVisible = ref(false);
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
||||
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
|
||||
const headers = ref({ Authorization: "Bearer " + getToken() });
|
||||
const uploadImgUrl = ref(baseUrl + "/resource/oss/upload"); // 上传的图片服务器地址
|
||||
const headers = ref(globalHeaders());
|
||||
const fileList = ref([]);
|
||||
const showTip = computed(
|
||||
() => props.isShowTip && (props.fileType || props.fileSize)
|
||||
);
|
||||
|
||||
watch(() => props.modelValue, val => {
|
||||
watch(() => props.modelValue,async val => {
|
||||
if (val) {
|
||||
// 首先将值转为数组
|
||||
const list = Array.isArray(val) ? val : props.modelValue.split(",");
|
||||
let list = [];
|
||||
if (Array.isArray(val)) {
|
||||
list = val;
|
||||
} else {
|
||||
const res = await listByIds(val)
|
||||
list = res.data
|
||||
}
|
||||
// 然后将数组转为对象数组
|
||||
fileList.value = list.map(item => {
|
||||
// 字符串回显处理 如果此处存的是url可直接回显 如果存的是id需要调用接口查出来
|
||||
let itemData;
|
||||
if (typeof item === "string") {
|
||||
if (item.indexOf(baseUrl) === -1) {
|
||||
item = { name: baseUrl + item, url: baseUrl + item };
|
||||
} else {
|
||||
item = { name: item, url: item };
|
||||
}
|
||||
itemData = { name: item, url: item };
|
||||
} else {
|
||||
// 此处name使用ossId 防止删除出现重名
|
||||
itemData = { name: item.ossId, url: item.url, ossId: item.ossId };
|
||||
}
|
||||
return item;
|
||||
return itemData;
|
||||
});
|
||||
} else {
|
||||
fileList.value = [];
|
||||
@ -148,7 +156,7 @@ function handleExceed() {
|
||||
// 上传成功回调
|
||||
function handleUploadSuccess(res, file) {
|
||||
if (res.code === 200) {
|
||||
uploadList.value.push({ name: res.fileName, url: res.fileName });
|
||||
uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
|
||||
uploadedSuccessfully();
|
||||
} else {
|
||||
number.value--;
|
||||
@ -163,6 +171,8 @@ function handleUploadSuccess(res, file) {
|
||||
function handleDelete(file) {
|
||||
const findex = fileList.value.map(f => f.name).indexOf(file.name);
|
||||
if (findex > -1 && uploadList.value.length === number.value) {
|
||||
let ossId = fileList.value[findex].ossId;
|
||||
delOss(ossId);
|
||||
fileList.value.splice(findex, 1);
|
||||
emit("update:modelValue", listToString(fileList.value));
|
||||
return false;
|
||||
@ -197,11 +207,11 @@ function listToString(list, separator) {
|
||||
let strs = "";
|
||||
separator = separator || ",";
|
||||
for (let i in list) {
|
||||
if (undefined !== list[i].url && list[i].url.indexOf("blob:") !== 0) {
|
||||
strs += list[i].url.replace(baseUrl, "") + separator;
|
||||
if (undefined !== list[i].ossId && list[i].url.indexOf("blob:") !== 0) {
|
||||
strs += list[i].ossId + separator;
|
||||
}
|
||||
}
|
||||
return strs != "" ? strs.substr(0, strs.length - 1) : "";
|
||||
return strs != "" ? strs.substring(0, strs.length - 1) : "";
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -210,4 +220,4 @@ function listToString(list, separator) {
|
||||
:deep(.hide .el-upload--picture-card) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@ -36,7 +36,7 @@ const useUserStore = defineStore(
|
||||
return new Promise((resolve, reject) => {
|
||||
getInfo().then(res => {
|
||||
const user = res.user
|
||||
const avatar = (user.avatar == "" || user.avatar == null) ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;
|
||||
const avatar = (user.url == "" || user.url == null) ? defAva : user.url;
|
||||
|
||||
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
|
||||
this.roles = res.roles
|
||||
|
337
flex-ui/src/views/system/oss/config.vue
Normal file
337
flex-ui/src/views/system/oss/config.vue
Normal file
@ -0,0 +1,337 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<div class="mb-[10px]" v-show="showSearch">
|
||||
<el-card shadow="hover">
|
||||
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
|
||||
<el-form-item label="配置key" prop="configKey">
|
||||
<el-input v-model="queryParams.configKey" placeholder="配置key" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="桶名称" prop="bucketName">
|
||||
<el-input v-model="queryParams.bucketName" placeholder="请输入桶名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否默认" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 200px">
|
||||
<el-option key="0" label="是" value="0" />
|
||||
<el-option key="1" label="否" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:oss:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:oss:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:oss:remove']">
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" :data="ossConfigList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="主建" align="center" prop="ossConfigId" v-if="columns[0].visible" />
|
||||
<el-table-column label="配置key" align="center" prop="configKey" v-if="columns[1].visible" />
|
||||
<el-table-column label="访问站点" align="center" prop="endpoint" v-if="columns[2].visible" width="200" />
|
||||
<el-table-column label="自定义域名" align="center" prop="domain" v-if="columns[3].visible" width="200" />
|
||||
<el-table-column label="桶名称" align="center" prop="bucketName" v-if="columns[4].visible" />
|
||||
<el-table-column label="前缀" align="center" prop="prefix" v-if="columns[5].visible" />
|
||||
<el-table-column label="域" align="center" prop="region" v-if="columns[6].visible" />
|
||||
<el-table-column label="桶权限类型" align="center" prop="accessPolicy" v-if="columns[7].visible">
|
||||
<template #default="scope">
|
||||
<el-tag type="warning" v-if="scope.row.accessPolicy === '0'">private</el-tag>
|
||||
<el-tag type="success" v-if="scope.row.accessPolicy === '1'">public</el-tag>
|
||||
<el-tag type="info" v-if="scope.row.accessPolicy === '2'">custom</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否默认" align="center" prop="status" v-if="columns[8].visible">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" fixed="right" align="center" width="150" class-name="small-padding">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:oss:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:oss:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
<!-- 添加或修改对象存储配置对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body>
|
||||
<el-form ref="ossConfigFormRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-form-item label="配置key" prop="configKey">
|
||||
<el-input v-model="form.configKey" placeholder="请输入配置key" />
|
||||
</el-form-item>
|
||||
<el-form-item label="访问站点" prop="endpoint">
|
||||
<el-input v-model="form.endpoint" placeholder="请输入访问站点" />
|
||||
</el-form-item>
|
||||
<el-form-item label="自定义域名" prop="domain">
|
||||
<el-input v-model="form.domain" placeholder="请输入自定义域名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="accessKey" prop="accessKey">
|
||||
<el-input v-model="form.accessKey" placeholder="请输入accessKey" />
|
||||
</el-form-item>
|
||||
<el-form-item label="secretKey" prop="secretKey">
|
||||
<el-input v-model="form.secretKey" placeholder="请输入秘钥" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="桶名称" prop="bucketName">
|
||||
<el-input v-model="form.bucketName" placeholder="请输入桶名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="前缀" prop="prefix">
|
||||
<el-input v-model="form.prefix" placeholder="请输入前缀" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否HTTPS">
|
||||
<el-radio-group v-model="form.isHttps">
|
||||
<el-radio v-for="dict in sys_yes_no" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="桶权限类型">
|
||||
<el-radio-group v-model="form.accessPolicy">
|
||||
<el-radio label="0">private</el-radio>
|
||||
<el-radio label="1">public</el-radio>
|
||||
<el-radio label="2">custom</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="域" prop="region">
|
||||
<el-input v-model="form.region" placeholder="请输入域" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="OssConfig">
|
||||
import {
|
||||
listOssConfig,
|
||||
getOssConfig,
|
||||
delOssConfig,
|
||||
addOssConfig,
|
||||
updateOssConfig,
|
||||
changeOssConfigStatus
|
||||
} from "@/api/system/ossConfig";
|
||||
|
||||
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const { sys_yes_no } = toRefs(proxy?.useDict("sys_yes_no"));
|
||||
|
||||
const ossConfigList = ref([]);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
// 列显隐信息
|
||||
const columns = ref([
|
||||
{ key: 0, label: `主建`, visible: true },
|
||||
{ key: 1, label: `配置key`, visible: false },
|
||||
{ key: 2, label: `访问站点`, visible: true },
|
||||
{ key: 3, label: `自定义域名`, visible: true },
|
||||
{ key: 4, label: `桶名称`, visible: true },
|
||||
{ key: 5, label: `前缀`, visible: true },
|
||||
{ key: 6, label: `域`, visible: true },
|
||||
{ key: 7, label: `桶权限类型`, visible: true },
|
||||
{ key: 8, label: `状态`, visible: true }
|
||||
]);
|
||||
|
||||
|
||||
const initFormData = {
|
||||
ossConfigId: undefined,
|
||||
configKey: '',
|
||||
accessKey: '',
|
||||
secretKey: '',
|
||||
bucketName: '',
|
||||
prefix: '',
|
||||
endpoint: '',
|
||||
domain: '',
|
||||
isHttps: "N",
|
||||
accessPolicy: "1",
|
||||
region: '',
|
||||
status: "1",
|
||||
remark: '',
|
||||
}
|
||||
const data = reactive({
|
||||
form: { ...initFormData },
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
configKey: '',
|
||||
bucketName: '',
|
||||
status: '',
|
||||
},
|
||||
rules: {
|
||||
configKey: [{ required: true, message: "configKey不能为空", trigger: "blur" },],
|
||||
accessKey: [
|
||||
{ required: true, message: "accessKey不能为空", trigger: "blur" },
|
||||
{
|
||||
min: 2,
|
||||
max: 200,
|
||||
message: "accessKey长度必须介于 2 和 100 之间",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
secretKey: [
|
||||
{ required: true, message: "secretKey不能为空", trigger: "blur" },
|
||||
{
|
||||
min: 2,
|
||||
max: 100,
|
||||
message: "secretKey长度必须介于 2 和 100 之间",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
bucketName: [
|
||||
{ required: true, message: "bucketName不能为空", trigger: "blur" },
|
||||
{
|
||||
min: 2,
|
||||
max: 100,
|
||||
message: "bucketName长度必须介于 2 和 100 之间",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
endpoint: [
|
||||
{ required: true, message: "endpoint不能为空", trigger: "blur" },
|
||||
{
|
||||
min: 2,
|
||||
max: 100,
|
||||
message: "endpoint名称长度必须介于 2 和 100 之间",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
accessPolicy: [{ required: true, message: "accessPolicy不能为空", trigger: "blur" }]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询对象存储配置列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listOssConfig(queryParams.value);
|
||||
ossConfigList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
}
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
dialog.visible = false;
|
||||
reset();
|
||||
}
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
proxy.resetForm("ossConfigFormRef");
|
||||
}
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
proxy.resetForm("queryFormRef");
|
||||
handleQuery();
|
||||
}
|
||||
/** 选择条数 */
|
||||
const handleSelectionChange = (selection) => {
|
||||
ids.value = selection.map(item => item.ossConfigId);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = "添加对象存储配置";
|
||||
}
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row) => {
|
||||
reset();
|
||||
const ossConfigId = row?.ossConfigId || ids.value[0];
|
||||
const res = await getOssConfig(ossConfigId);
|
||||
Object.assign(form.value, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = "修改对象存储配置";
|
||||
}
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
proxy.$refs["ossConfigFormRef"].validate(async (valid) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
if (form.value.ossConfigId) {
|
||||
await updateOssConfig(form.value).finally(() => buttonLoading.value = false);
|
||||
} else {
|
||||
await addOssConfig(form.value).finally(() => buttonLoading.value = false);
|
||||
}
|
||||
proxy?.$modal.msgSuccess("新增成功");
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
}
|
||||
/** 状态修改 */
|
||||
const handleStatusChange = async (row) => {
|
||||
let text = row.status === "0" ? "启用" : "停用";
|
||||
try {
|
||||
await proxy?.$modal.confirm('确认要"' + text + '""' + row.configKey + '"配置吗?');
|
||||
await changeOssConfigStatus(row.ossConfigId, row.status, row.configKey);
|
||||
await getList()
|
||||
proxy?.$modal.msgSuccess(text + "成功");
|
||||
} catch { return } finally {
|
||||
row.status = row.status === "0" ? "1" : "0";
|
||||
}
|
||||
|
||||
}
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row) => {
|
||||
const ossConfigIds = row?.ossConfigId || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除OSS配置编号为"' + ossConfigIds + '"的数据项?');
|
||||
loading.value = true;
|
||||
await delOssConfig(ossConfigIds).finally(() => loading.value = false);
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess("删除成功");
|
||||
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
})
|
||||
</script>
|
330
flex-ui/src/views/system/oss/index.vue
Normal file
330
flex-ui/src/views/system/oss/index.vue
Normal file
@ -0,0 +1,330 @@
|
||||
<template>
|
||||
<div class="p-2">
|
||||
<div class="mb-[10px]" v-show="showSearch">
|
||||
<el-card shadow="hover">
|
||||
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
|
||||
<el-form-item label="文件名" prop="fileName">
|
||||
<el-input v-model="queryParams.fileName" placeholder="请输入文件名" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="原名" prop="originalName">
|
||||
<el-input v-model="queryParams.originalName" placeholder="请输入原名" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="文件后缀" prop="fileSuffix">
|
||||
<el-input v-model="queryParams.fileSuffix" placeholder="请输入文件后缀" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间">
|
||||
<el-date-picker
|
||||
v-model="dateRangeCreateTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="服务商" prop="service">
|
||||
<el-input v-model="queryParams.service" placeholder="请输入服务商" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Upload" @click="handleFile" v-hasPermi="['system:oss:upload']">上传文件</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Upload" @click="handleImage" v-hasPermi="['system:oss:upload']">上传图片</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:oss:remove']">
|
||||
删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
:type="previewListResource ? 'danger' : 'warning'"
|
||||
plain
|
||||
@click="handlePreviewListResource(!previewListResource)"
|
||||
v-hasPermi="['system:oss:edit']"
|
||||
>预览开关 :
|
||||
{{
|
||||
previewListResource ? "禁用" : "启用" }}</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="Operation" @click="handleOssConfig" v-hasPermi="['system:oss:list']">配置管理</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="ossList"
|
||||
@selection-change="handleSelectionChange"
|
||||
:header-cell-class-name="handleHeaderClass"
|
||||
@header-click="handleHeaderCLick"
|
||||
v-if="showTable"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="对象存储主键" align="center" prop="ossId" v-if="false" />
|
||||
<el-table-column label="文件名" align="center" prop="fileName" />
|
||||
<el-table-column label="原名" align="center" prop="originalName" />
|
||||
<el-table-column label="文件后缀" align="center" prop="fileSuffix" />
|
||||
<el-table-column label="文件展示" align="center" prop="url">
|
||||
<template #default="scope">
|
||||
<ImagePreview
|
||||
v-if="previewListResource && checkFileSuffix(scope.row.fileSuffix)"
|
||||
:width="100"
|
||||
:height="100"
|
||||
:src="scope.row.url"
|
||||
:preview-src-list="[scope.row.url]"
|
||||
/>
|
||||
<span v-text="scope.row.url" v-if="!checkFileSuffix(scope.row.fileSuffix) || !previewListResource" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上传人" align="center" prop="createByName" />
|
||||
<el-table-column label="服务商" align="center" prop="service" sortable="custom" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="下载" placement="top">
|
||||
<el-button link type="primary" icon="Download" @click="handleDownload(scope.row)" v-hasPermi="['system:oss:download']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:oss:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
<!-- 添加或修改OSS对象存储对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="ossFormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="文件名">
|
||||
<fileUpload v-model="form.file" v-if="type === 0" />
|
||||
<imageUpload v-model="form.file" v-if="type === 1" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Oss">
|
||||
import { listOss, delOss } from "@/api/system/oss";
|
||||
import ImagePreview from "@/components/ImagePreview/index.vue";
|
||||
import { getConfigKey,updateConfigByKey } from "@/api/system/config";
|
||||
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const ossList = ref([]);
|
||||
const showTable = ref(true);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const type = ref(0);
|
||||
const previewListResource = ref(true);
|
||||
const dateRangeCreateTime = ref(['', '']);
|
||||
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
// 默认排序
|
||||
const defaultSort = ref({ prop: 'createTime', order: 'ascending' });
|
||||
|
||||
const initFormData = {
|
||||
file: undefined,
|
||||
}
|
||||
const data = reactive({
|
||||
form: { ...initFormData },
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
fileName: '',
|
||||
originalName: '',
|
||||
fileSuffix: '',
|
||||
createTime: '',
|
||||
service: '',
|
||||
orderByColumn: defaultSort.value.prop,
|
||||
isAsc: defaultSort.value.order
|
||||
},
|
||||
rules: {
|
||||
file: [
|
||||
{ required: true, message: "文件不能为空", trigger: "blur" }
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询OSS对象存储列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await getConfigKey("sys.oss.previewListResource");
|
||||
previewListResource.value = res?.data === undefined ? true : res.data === 'true';
|
||||
const response = await listOss(proxy?.addDateRange(queryParams.value, dateRangeCreateTime.value, "CreateTime"));
|
||||
ossList.value = response.rows;
|
||||
total.value = response.total;
|
||||
loading.value = false;
|
||||
showTable.value = true;
|
||||
}
|
||||
function checkFileSuffix(fileSuffix) {
|
||||
let arr = ["png", "jpg", "jpeg"];
|
||||
return arr.some(type => {
|
||||
return fileSuffix.indexOf(type) > -1;
|
||||
});
|
||||
}
|
||||
/** 取消按钮 */
|
||||
function cancel() {
|
||||
dialog.visible = false;
|
||||
reset();
|
||||
}
|
||||
/** 表单重置 */
|
||||
function reset() {
|
||||
form.value = { ...initFormData };
|
||||
proxy.resetForm("ossFormRef");
|
||||
}
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
showTable.value = false;
|
||||
dateRangeCreateTime.value = ['', ''];
|
||||
proxy.resetForm("queryFormRef");
|
||||
queryParams.value.orderByColumn = defaultSort.value.prop;
|
||||
queryParams.value.isAsc = defaultSort.value.order;
|
||||
handleQuery();
|
||||
}
|
||||
/** 选择条数 */
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map(item => item.ossId);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
/** 设置列的排序为我们自定义的排序 */
|
||||
const handleHeaderClass = ({ column }) => {
|
||||
column.order = column.multiOrder
|
||||
}
|
||||
/** 点击表头进行排序 */
|
||||
const handleHeaderCLick = (column) => {
|
||||
if (column.sortable !== 'custom') {
|
||||
return
|
||||
}
|
||||
switch (column.multiOrder) {
|
||||
case 'descending':
|
||||
column.multiOrder = 'ascending';
|
||||
break;
|
||||
case 'ascending':
|
||||
column.multiOrder = '';
|
||||
break;
|
||||
default:
|
||||
column.multiOrder = 'descending';
|
||||
break;
|
||||
}
|
||||
handleOrderChange(column.property, column.multiOrder)
|
||||
}
|
||||
const handleOrderChange = (prop, order) => {
|
||||
let orderByArr = queryParams.value.orderByColumn ? queryParams.value.orderByColumn.split(",") : [];
|
||||
let isAscArr = queryParams.value.isAsc ? queryParams.value.isAsc.split(",") : [];
|
||||
let propIndex = orderByArr.indexOf(prop)
|
||||
if (propIndex !== -1) {
|
||||
if (order) {
|
||||
//排序里已存在 只修改排序
|
||||
isAscArr[propIndex] = order;
|
||||
} else {
|
||||
//如果order为null 则删除排序字段和属性
|
||||
isAscArr.splice(propIndex, 1);//删除排序
|
||||
orderByArr.splice(propIndex, 1);//删除属性
|
||||
}
|
||||
} else {
|
||||
//排序里不存在则新增排序
|
||||
orderByArr.push(prop);
|
||||
isAscArr.push(order);
|
||||
}
|
||||
//合并排序
|
||||
queryParams.value.orderByColumn = orderByArr.join(",");
|
||||
queryParams.value.isAsc = isAscArr.join(",");
|
||||
getList();
|
||||
}
|
||||
/** 任务日志列表查询 */
|
||||
const handleOssConfig = () => {
|
||||
router.push('/system/oss-config/index')
|
||||
}
|
||||
/** 文件按钮操作 */
|
||||
const handleFile = () => {
|
||||
reset();
|
||||
type.value = 0;
|
||||
dialog.visible = true;
|
||||
dialog.title = "上传文件";
|
||||
}
|
||||
/** 图片按钮操作 */
|
||||
const handleImage = () => {
|
||||
reset();
|
||||
type.value = 1;
|
||||
dialog.visible = true;
|
||||
dialog.title = "上传图片";
|
||||
}
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
dialog.visible = false;
|
||||
getList();
|
||||
}
|
||||
/** 下载按钮操作 */
|
||||
const handleDownload = (row) => {
|
||||
proxy?.$download.oss(row.ossId)
|
||||
}
|
||||
/** 用户状态修改 */
|
||||
const handlePreviewListResource = async (preview) => {
|
||||
let text = preview ? "启用" : "停用";
|
||||
try {
|
||||
await proxy?.$modal.confirm('确认要"' + text + '""预览列表图片"配置吗?');
|
||||
await updateConfigByKey("sys.oss.previewListResource", preview);
|
||||
await getList()
|
||||
proxy?.$modal.msgSuccess(text + "成功");
|
||||
} catch { return }
|
||||
}
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row) => {
|
||||
const ossIds = row?.ossId || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除OSS对象存储编号为"' + ossIds + '"的数据项?');
|
||||
loading.value = true;
|
||||
await delOss(ossIds).finally(() => loading.value = false);
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess("删除成功");
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
})
|
||||
</script>
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="6" :xs="24">
|
||||
<el-card class="box-card">
|
||||
<template v-slot:header>
|
||||
@ -10,7 +10,7 @@
|
||||
</template>
|
||||
<div>
|
||||
<div class="text-center">
|
||||
<userAvatar :user="state.user" />
|
||||
<userAvatar />
|
||||
</div>
|
||||
<ul class="list-group list-group-striped">
|
||||
<li class="list-group-item">
|
||||
|
@ -1,53 +1,62 @@
|
||||
<template>
|
||||
<div class="user-info-head" @click="editCropper()"><img :src="options.img" title="点击上传头像" class="img-circle img-lg" /></div>
|
||||
<el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
|
||||
<el-row>
|
||||
<el-col :xs="24" :md="12" :style="{height: '350px'}">
|
||||
<vue-cropper
|
||||
ref="cropper"
|
||||
:img="options.img"
|
||||
:info="true"
|
||||
:autoCrop="options.autoCrop"
|
||||
:autoCropWidth="options.autoCropWidth"
|
||||
:autoCropHeight="options.autoCropHeight"
|
||||
:fixedBox="options.fixedBox"
|
||||
@realTime="realTime"
|
||||
v-if="visible"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="12" :style="{height: '350px'}">
|
||||
<div class="avatar-upload-preview">
|
||||
<img :src="options.previews.url" :style="options.previews.img"/>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<br/>
|
||||
<el-row>
|
||||
<el-col :lg="2" :md="2">
|
||||
<el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload">
|
||||
<el-button>
|
||||
选择
|
||||
<el-icon class="el-icon--right"><Upload /></el-icon>
|
||||
</el-button>
|
||||
</el-upload>
|
||||
</el-col>
|
||||
<el-col :lg="{span: 1, offset: 2}" :md="2">
|
||||
<el-button icon="Plus" @click="changeScale(1)"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{span: 1, offset: 1}" :md="2">
|
||||
<el-button icon="Minus" @click="changeScale(-1)"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{span: 1, offset: 1}" :md="2">
|
||||
<el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{span: 1, offset: 1}" :md="2">
|
||||
<el-button icon="RefreshRight" @click="rotateRight()"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{span: 2, offset: 6}" :md="2">
|
||||
<el-button type="primary" @click="uploadImg()">提 交</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
<div class="user-info-head" @click="editCropper()">
|
||||
<img :src="options.img" title="点击上传头像" class="img-circle img-lg" />
|
||||
<el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
|
||||
<el-row>
|
||||
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
|
||||
<vue-cropper
|
||||
ref="cropper"
|
||||
:img="options.img"
|
||||
:info="true"
|
||||
:autoCrop="options.autoCrop"
|
||||
:autoCropWidth="options.autoCropWidth"
|
||||
:autoCropHeight="options.autoCropHeight"
|
||||
:fixedBox="options.fixedBox"
|
||||
:outputType="options.outputType"
|
||||
@realTime="realTime"
|
||||
v-if="visible"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="12" :style="{ height: '350px' }">
|
||||
<div class="avatar-upload-preview">
|
||||
<img :src="options.previews.url" :style="options.previews.img" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<br />
|
||||
<el-row>
|
||||
<el-col :lg="2" :md="2">
|
||||
<el-upload
|
||||
action="#"
|
||||
:headers="{'Content-Type':'multipart/form-data'}"
|
||||
:http-request="requestUpload"
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<el-button>
|
||||
选择
|
||||
<el-icon class="el-icon--right"><Upload /></el-icon>
|
||||
</el-button>
|
||||
</el-upload>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
|
||||
<el-button icon="Plus" @click="changeScale(1)"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
||||
<el-button icon="Minus" @click="changeScale(-1)"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
||||
<el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 1, offset: 1 }" :md="2">
|
||||
<el-button icon="RefreshRight" @click="rotateRight()"></el-button>
|
||||
</el-col>
|
||||
<el-col :lg="{ span: 2, offset: 6 }" :md="2">
|
||||
<el-button type="primary" @click="uploadImg()">提 交</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@ -71,33 +80,36 @@ const options = reactive({
|
||||
autoCropHeight: 200, // 默认生成截图框高度
|
||||
fixedBox: true, // 固定截图框大小 不允许改变
|
||||
outputType: "png", // 默认生成截图为PNG格式
|
||||
previews: {} //预览数据
|
||||
fileName: "",
|
||||
previews: {}, //预览数据
|
||||
visible: false
|
||||
});
|
||||
|
||||
|
||||
|
||||
/** 编辑头像 */
|
||||
function editCropper() {
|
||||
open.value = true;
|
||||
};
|
||||
}
|
||||
/** 打开弹出层结束时的回调 */
|
||||
function modalOpened() {
|
||||
visible.value = true;
|
||||
};
|
||||
}
|
||||
/** 覆盖默认上传行为 */
|
||||
function requestUpload() {
|
||||
};
|
||||
function requestUpload() {}
|
||||
/** 向左旋转 */
|
||||
function rotateLeft() {
|
||||
proxy.$refs.cropper.rotateLeft();
|
||||
};
|
||||
}
|
||||
/** 向右旋转 */
|
||||
function rotateRight() {
|
||||
proxy.$refs.cropper.rotateRight();
|
||||
};
|
||||
}
|
||||
/** 图片缩放 */
|
||||
function changeScale(num) {
|
||||
num = num || 1;
|
||||
proxy.$refs.cropper.changeScale(num);
|
||||
};
|
||||
}
|
||||
/** 上传预处理 */
|
||||
function beforeUpload(file) {
|
||||
if (file.type.indexOf("image/") == -1) {
|
||||
@ -107,32 +119,33 @@ function beforeUpload(file) {
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
options.img = reader.result;
|
||||
options.fileName = file.name;
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
/** 上传图片 */
|
||||
function uploadImg() {
|
||||
proxy.$refs.cropper.getCropBlob(data => {
|
||||
let formData = new FormData();
|
||||
formData.append("avatarfile", data);
|
||||
formData.append("avatarfile", data, options.fileName);
|
||||
uploadAvatar(formData).then(response => {
|
||||
open.value = false;
|
||||
options.img = import.meta.env.VITE_APP_BASE_API + response.imgUrl;
|
||||
options.img = response.data.imgUrl;
|
||||
userStore.avatar = options.img;
|
||||
proxy.$modal.msgSuccess("修改成功");
|
||||
visible.value = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
/** 实时预览 */
|
||||
function realTime(data) {
|
||||
options.previews = data;
|
||||
};
|
||||
}
|
||||
/** 关闭窗口 */
|
||||
function closeDialog() {
|
||||
options.img = userStore.avatar;
|
||||
options.visible = false;
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
|
@ -0,0 +1,18 @@
|
||||
package com.ruoyi.common.core.service;
|
||||
|
||||
/**
|
||||
* 通用 OSS服务
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface OssService {
|
||||
|
||||
/**
|
||||
* 通过ossId查询对应的url
|
||||
*
|
||||
* @param ossIds ossId串逗号分隔
|
||||
* @return url串逗号分隔
|
||||
*/
|
||||
String selectUrlByIds(String ossIds);
|
||||
|
||||
}
|
35
ruoyi-common/ruoyi-common-oss/pom.xml
Normal file
35
ruoyi-common/ruoyi-common-oss/pom.xml
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-common-oss</artifactId>
|
||||
|
||||
<description>
|
||||
ruoyi-common-oss oss对象存储服务
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common-json</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-s3</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,38 @@
|
||||
package com.ruoyi.common.oss.constant;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 对象存储常量
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface OssConstant {
|
||||
|
||||
/**
|
||||
* 默认配置KEY
|
||||
*/
|
||||
String DEFAULT_CONFIG_KEY = "sys_oss:default_config";
|
||||
|
||||
/**
|
||||
* 预览列表资源开关Key
|
||||
*/
|
||||
String PEREVIEW_LIST_RESOURCE_KEY = "sys.oss.previewListResource";
|
||||
|
||||
/**
|
||||
* 系统数据ids
|
||||
*/
|
||||
List<Long> SYSTEM_DATA_IDS = Arrays.asList(1L, 2L, 3L, 4L);
|
||||
|
||||
/**
|
||||
* 云服务商
|
||||
*/
|
||||
String[] CLOUD_SERVICE = new String[] {"aliyun", "qcloud", "qiniu", "obs"};
|
||||
|
||||
/**
|
||||
* https 状态
|
||||
*/
|
||||
String IS_HTTPS = "Y";
|
||||
|
||||
}
|
@ -0,0 +1,262 @@
|
||||
package com.ruoyi.common.oss.core;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.amazonaws.ClientConfiguration;
|
||||
import com.amazonaws.HttpMethod;
|
||||
import com.amazonaws.Protocol;
|
||||
import com.amazonaws.auth.AWSCredentials;
|
||||
import com.amazonaws.auth.AWSCredentialsProvider;
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
import com.amazonaws.services.s3.AmazonS3;
|
||||
import com.amazonaws.services.s3.AmazonS3Client;
|
||||
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
|
||||
import com.amazonaws.services.s3.model.*;
|
||||
import com.ruoyi.common.core.utils.DateUtils;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.common.oss.constant.OssConstant;
|
||||
import com.ruoyi.common.oss.entity.UploadResult;
|
||||
import com.ruoyi.common.oss.enumd.AccessPolicyType;
|
||||
import com.ruoyi.common.oss.enumd.PolicyType;
|
||||
import com.ruoyi.common.oss.exception.OssException;
|
||||
import com.ruoyi.common.oss.properties.OssProperties;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* S3 存储协议 所有兼容S3协议的云厂商均支持
|
||||
* 阿里云 腾讯云 七牛云 minio
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public class OssClient {
|
||||
|
||||
private final String configKey;
|
||||
|
||||
private final OssProperties properties;
|
||||
|
||||
private final AmazonS3 client;
|
||||
|
||||
public OssClient(String configKey, OssProperties ossProperties) {
|
||||
this.configKey = configKey;
|
||||
this.properties = ossProperties;
|
||||
try {
|
||||
AwsClientBuilder.EndpointConfiguration endpointConfig =
|
||||
new AwsClientBuilder.EndpointConfiguration(properties.getEndpoint(), properties.getRegion());
|
||||
|
||||
AWSCredentials credentials = new BasicAWSCredentials(properties.getAccessKey(), properties.getSecretKey());
|
||||
AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials);
|
||||
ClientConfiguration clientConfig = new ClientConfiguration();
|
||||
if (OssConstant.IS_HTTPS.equals(properties.getIsHttps())) {
|
||||
clientConfig.setProtocol(Protocol.HTTPS);
|
||||
} else {
|
||||
clientConfig.setProtocol(Protocol.HTTP);
|
||||
}
|
||||
AmazonS3ClientBuilder build = AmazonS3Client.builder()
|
||||
.withEndpointConfiguration(endpointConfig)
|
||||
.withClientConfiguration(clientConfig)
|
||||
.withCredentials(credentialsProvider)
|
||||
.disableChunkedEncoding();
|
||||
if (!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)) {
|
||||
// minio 使用https限制使用域名访问 需要此配置 站点填域名
|
||||
build.enablePathStyleAccess();
|
||||
}
|
||||
this.client = build.build();
|
||||
|
||||
createBucket();
|
||||
} catch (Exception e) {
|
||||
if (e instanceof OssException) {
|
||||
throw e;
|
||||
}
|
||||
throw new OssException("配置错误! 请检查系统配置:[" + e.getMessage() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
public void createBucket() {
|
||||
try {
|
||||
String bucketName = properties.getBucketName();
|
||||
if (client.doesBucketExistV2(bucketName)) {
|
||||
return;
|
||||
}
|
||||
CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
|
||||
AccessPolicyType accessPolicy = getAccessPolicy();
|
||||
createBucketRequest.setCannedAcl(accessPolicy.getAcl());
|
||||
client.createBucket(createBucketRequest);
|
||||
client.setBucketPolicy(bucketName, getPolicy(bucketName, accessPolicy.getPolicyType()));
|
||||
} catch (Exception e) {
|
||||
throw new OssException("创建Bucket失败, 请核对配置信息:[" + e.getMessage() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
public UploadResult upload(byte[] data, String path, String contentType) {
|
||||
return upload(new ByteArrayInputStream(data), path, contentType);
|
||||
}
|
||||
|
||||
public UploadResult upload(InputStream inputStream, String path, String contentType) {
|
||||
if (!(inputStream instanceof ByteArrayInputStream)) {
|
||||
inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream));
|
||||
}
|
||||
try {
|
||||
ObjectMetadata metadata = new ObjectMetadata();
|
||||
metadata.setContentType(contentType);
|
||||
metadata.setContentLength(inputStream.available());
|
||||
PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata);
|
||||
// 设置上传对象的 Acl 为公共读
|
||||
putObjectRequest.setCannedAcl(getAccessPolicy().getAcl());
|
||||
client.putObject(putObjectRequest);
|
||||
} catch (Exception e) {
|
||||
throw new OssException("上传文件失败,请检查配置信息:[" + e.getMessage() + "]");
|
||||
}
|
||||
return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build();
|
||||
}
|
||||
|
||||
public UploadResult upload(File file, String path) {
|
||||
try {
|
||||
PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, file);
|
||||
// 设置上传对象的 Acl 为公共读
|
||||
putObjectRequest.setCannedAcl(getAccessPolicy().getAcl());
|
||||
client.putObject(putObjectRequest);
|
||||
} catch (Exception e) {
|
||||
throw new OssException("上传文件失败,请检查配置信息:[" + e.getMessage() + "]");
|
||||
}
|
||||
return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build();
|
||||
}
|
||||
|
||||
public void delete(String path) {
|
||||
path = path.replace(getUrl() + "/", "");
|
||||
try {
|
||||
client.deleteObject(properties.getBucketName(), path);
|
||||
} catch (Exception e) {
|
||||
throw new OssException("删除文件失败,请检查配置信息:[" + e.getMessage() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
|
||||
return upload(data, getPath(properties.getPrefix(), suffix), contentType);
|
||||
}
|
||||
|
||||
public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
|
||||
return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType);
|
||||
}
|
||||
|
||||
public UploadResult uploadSuffix(File file, String suffix) {
|
||||
return upload(file, getPath(properties.getPrefix(), suffix));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件元数据
|
||||
*
|
||||
* @param path 完整文件路径
|
||||
*/
|
||||
public ObjectMetadata getObjectMetadata(String path) {
|
||||
path = path.replace(getUrl() + "/", "");
|
||||
S3Object object = client.getObject(properties.getBucketName(), path);
|
||||
return object.getObjectMetadata();
|
||||
}
|
||||
|
||||
public InputStream getObjectContent(String path) {
|
||||
path = path.replace(getUrl() + "/", "");
|
||||
S3Object object = client.getObject(properties.getBucketName(), path);
|
||||
return object.getObjectContent();
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
String domain = properties.getDomain();
|
||||
String endpoint = properties.getEndpoint();
|
||||
String header = OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? "https://" : "http://";
|
||||
// 云服务商直接返回
|
||||
if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) {
|
||||
if (StringUtils.isNotBlank(domain)) {
|
||||
return header + domain;
|
||||
}
|
||||
return header + properties.getBucketName() + "." + endpoint;
|
||||
}
|
||||
// minio 单独处理
|
||||
if (StringUtils.isNotBlank(domain)) {
|
||||
return header + domain + "/" + properties.getBucketName();
|
||||
}
|
||||
return header + endpoint + "/" + properties.getBucketName();
|
||||
}
|
||||
|
||||
public String getPath(String prefix, String suffix) {
|
||||
// 生成uuid
|
||||
String uuid = IdUtil.fastSimpleUUID();
|
||||
// 文件路径
|
||||
String path = DateUtils.datePath() + "/" + uuid;
|
||||
if (StringUtils.isNotBlank(prefix)) {
|
||||
path = prefix + "/" + path;
|
||||
}
|
||||
return path + suffix;
|
||||
}
|
||||
|
||||
|
||||
public String getConfigKey() {
|
||||
return configKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取私有URL链接
|
||||
*
|
||||
* @param objectKey 对象KEY
|
||||
* @param second 授权时间
|
||||
*/
|
||||
public String getPrivateUrl(String objectKey, Integer second) {
|
||||
GeneratePresignedUrlRequest generatePresignedUrlRequest =
|
||||
new GeneratePresignedUrlRequest(properties.getBucketName(), objectKey)
|
||||
.withMethod(HttpMethod.GET)
|
||||
.withExpiration(new Date(System.currentTimeMillis() + 1000L * second));
|
||||
URL url = client.generatePresignedUrl(generatePresignedUrlRequest);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查配置是否相同
|
||||
*/
|
||||
public boolean checkPropertiesSame(OssProperties properties) {
|
||||
return this.properties.equals(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前桶权限类型
|
||||
*
|
||||
* @return 当前桶权限类型code
|
||||
*/
|
||||
public AccessPolicyType getAccessPolicy() {
|
||||
return AccessPolicyType.getByType(properties.getAccessPolicy());
|
||||
}
|
||||
|
||||
private static String getPolicy(String bucketName, PolicyType policyType) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("{\n\"Statement\": [\n{\n\"Action\": [\n");
|
||||
builder.append(switch (policyType) {
|
||||
case WRITE -> "\"s3:GetBucketLocation\",\n\"s3:ListBucketMultipartUploads\"\n";
|
||||
case READ_WRITE -> "\"s3:GetBucketLocation\",\n\"s3:ListBucket\",\n\"s3:ListBucketMultipartUploads\"\n";
|
||||
default -> "\"s3:GetBucketLocation\"\n";
|
||||
});
|
||||
builder.append("],\n\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
|
||||
builder.append(bucketName);
|
||||
builder.append("\"\n},\n");
|
||||
if (policyType == PolicyType.READ) {
|
||||
builder.append("{\n\"Action\": [\n\"s3:ListBucket\"\n],\n\"Effect\": \"Deny\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
|
||||
builder.append(bucketName);
|
||||
builder.append("\"\n},\n");
|
||||
}
|
||||
builder.append("{\n\"Action\": ");
|
||||
builder.append(switch (policyType) {
|
||||
case WRITE -> "[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n";
|
||||
case READ_WRITE -> "[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:GetObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n";
|
||||
default -> "\"s3:GetObject\",\n";
|
||||
});
|
||||
builder.append("\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
|
||||
builder.append(bucketName);
|
||||
builder.append("/*\"\n}\n],\n\"Version\": \"2012-10-17\"\n}\n");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.ruoyi.common.oss.entity;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 上传返回体
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class UploadResult {
|
||||
|
||||
/**
|
||||
* 文件路径
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
private String filename;
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package com.ruoyi.common.oss.enumd;
|
||||
|
||||
import com.amazonaws.services.s3.model.CannedAccessControlList;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 桶访问策略配置
|
||||
*
|
||||
* @author 陈賝
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum AccessPolicyType {
|
||||
|
||||
/**
|
||||
* private
|
||||
*/
|
||||
PRIVATE("0", CannedAccessControlList.Private, PolicyType.WRITE),
|
||||
|
||||
/**
|
||||
* public
|
||||
*/
|
||||
PUBLIC("1", CannedAccessControlList.PublicRead, PolicyType.READ),
|
||||
|
||||
/**
|
||||
* custom
|
||||
*/
|
||||
CUSTOM("2",CannedAccessControlList.PublicRead, PolicyType.READ);
|
||||
|
||||
/**
|
||||
* 桶 权限类型
|
||||
*/
|
||||
private final String type;
|
||||
|
||||
/**
|
||||
* 文件对象 权限类型
|
||||
*/
|
||||
private final CannedAccessControlList acl;
|
||||
|
||||
/**
|
||||
* 桶策略类型
|
||||
*/
|
||||
private final PolicyType policyType;
|
||||
|
||||
public static AccessPolicyType getByType(String type) {
|
||||
for (AccessPolicyType value : values()) {
|
||||
if (value.getType().equals(type)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("'type' not found By " + type);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package com.ruoyi.common.oss.enumd;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* minio策略配置
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PolicyType {
|
||||
|
||||
/**
|
||||
* 只读
|
||||
*/
|
||||
READ("read-only"),
|
||||
|
||||
/**
|
||||
* 只写
|
||||
*/
|
||||
WRITE("write-only"),
|
||||
|
||||
/**
|
||||
* 读写
|
||||
*/
|
||||
READ_WRITE("read-write");
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final String type;
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.ruoyi.common.oss.exception;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* OSS异常类
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public class OssException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public OssException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package com.ruoyi.common.oss.factory;
|
||||
|
||||
import com.ruoyi.common.core.constant.CacheNames;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.common.json.utils.JsonUtils;
|
||||
import com.ruoyi.common.oss.constant.OssConstant;
|
||||
import com.ruoyi.common.oss.core.OssClient;
|
||||
import com.ruoyi.common.oss.exception.OssException;
|
||||
import com.ruoyi.common.oss.properties.OssProperties;
|
||||
import com.ruoyi.common.redis.utils.CacheUtils;
|
||||
import com.ruoyi.common.redis.utils.RedisUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 文件上传Factory
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
public class OssFactory {
|
||||
|
||||
private static final Map<String, OssClient> CLIENT_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 获取默认实例
|
||||
*/
|
||||
public static OssClient instance() {
|
||||
// 获取redis 默认类型
|
||||
String configKey = RedisUtils.getCacheObject(OssConstant.DEFAULT_CONFIG_KEY);
|
||||
if (StringUtils.isEmpty(configKey)) {
|
||||
throw new OssException("文件存储服务类型无法找到!");
|
||||
}
|
||||
return instance(configKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据类型获取实例
|
||||
*/
|
||||
public static OssClient instance(String configKey) {
|
||||
String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey);
|
||||
if (json == null) {
|
||||
throw new OssException("系统异常, '" + configKey + "'配置信息不存在!");
|
||||
}
|
||||
OssProperties properties = JsonUtils.parseObject(json, OssProperties.class);
|
||||
// 使用租户标识避免多个租户相同key实例覆盖
|
||||
String key = properties.getTenantId() + ":" + configKey;
|
||||
OssClient client = CLIENT_CACHE.get(key);
|
||||
if (client == null) {
|
||||
CLIENT_CACHE.put(key, new OssClient(configKey, properties));
|
||||
log.info("创建OSS实例 key => {}", configKey);
|
||||
return CLIENT_CACHE.get(key);
|
||||
}
|
||||
// 配置不相同则重新构建
|
||||
if (!client.checkPropertiesSame(properties)) {
|
||||
CLIENT_CACHE.put(key, new OssClient(configKey, properties));
|
||||
log.info("重载OSS实例 key => {}", configKey);
|
||||
return CLIENT_CACHE.get(key);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.ruoyi.common.oss.properties;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* OSS对象存储 配置属性
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
public class OssProperties {
|
||||
|
||||
/**
|
||||
* 租户id
|
||||
*/
|
||||
private String tenantId;
|
||||
|
||||
/**
|
||||
* 访问站点
|
||||
*/
|
||||
private String endpoint;
|
||||
|
||||
/**
|
||||
* 自定义域名
|
||||
*/
|
||||
private String domain;
|
||||
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
private String prefix;
|
||||
|
||||
/**
|
||||
* ACCESS_KEY
|
||||
*/
|
||||
private String accessKey;
|
||||
|
||||
/**
|
||||
* SECRET_KEY
|
||||
*/
|
||||
private String secretKey;
|
||||
|
||||
/**
|
||||
* 存储空间名
|
||||
*/
|
||||
private String bucketName;
|
||||
|
||||
/**
|
||||
* 存储区域
|
||||
*/
|
||||
private String region;
|
||||
|
||||
/**
|
||||
* 是否https(Y=是,N=否)
|
||||
*/
|
||||
private String isHttps;
|
||||
|
||||
/**
|
||||
* 桶权限类型(0private 1public 2custom)
|
||||
*/
|
||||
private String accessPolicy;
|
||||
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
package com.ruoyi.system.controller.system;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.ruoyi.common.core.core.domain.R;
|
||||
import com.ruoyi.common.core.validate.AddGroup;
|
||||
import com.ruoyi.common.core.validate.EditGroup;
|
||||
import com.ruoyi.common.core.validate.QueryGroup;
|
||||
import com.ruoyi.common.web.annotation.RepeatSubmit;
|
||||
import com.ruoyi.common.web.core.BaseController;
|
||||
import com.ruoyi.common.log.annotation.Log;
|
||||
import com.ruoyi.common.log.enums.BusinessType;
|
||||
import com.ruoyi.common.orm.core.page.PageQuery;
|
||||
import com.ruoyi.common.orm.core.page.TableDataInfo;
|
||||
import com.ruoyi.system.domain.bo.SysOssConfigBo;
|
||||
import com.ruoyi.system.domain.vo.SysOssConfigVo;
|
||||
import com.ruoyi.system.service.ISysOssConfigService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 对象存储配置
|
||||
*
|
||||
* @author Lion Li
|
||||
* @author 孤舟烟雨
|
||||
* @author 数据小王子
|
||||
* @date 2023-11-30
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/resource/oss/config")
|
||||
public class SysOssConfigController extends BaseController {
|
||||
|
||||
@Resource
|
||||
private final ISysOssConfigService ossConfigService;
|
||||
|
||||
/**
|
||||
* 查询对象存储配置列表
|
||||
*/
|
||||
@SaCheckPermission("system:oss:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<SysOssConfigVo> list(SysOssConfigBo bo) {
|
||||
return ossConfigService.queryPageList(bo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象存储配置详细信息
|
||||
*
|
||||
* @param ossConfigId OSS配置ID
|
||||
*/
|
||||
@SaCheckPermission("system:oss:query")
|
||||
@GetMapping("/{ossConfigId}")
|
||||
public R<SysOssConfigVo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable Long ossConfigId) {
|
||||
return R.ok(ossConfigService.queryById(ossConfigId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增对象存储配置
|
||||
*/
|
||||
@SaCheckPermission("system:oss:add")
|
||||
@Log(title = "对象存储配置", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated @RequestBody SysOssConfigBo bo) {
|
||||
boolean inserted = ossConfigService.insertByBo(bo);
|
||||
if (!inserted) {
|
||||
return R.fail("新增对象存储配置记录失败!");
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改对象存储配置
|
||||
*/
|
||||
@SaCheckPermission("system:oss:edit")
|
||||
@Log(title = "对象存储配置", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated @RequestBody SysOssConfigBo bo) {
|
||||
Boolean updated = ossConfigService.updateByBo(bo);
|
||||
if (!updated) {
|
||||
R.fail("修改对象存储配置记录失败!");
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除对象存储配置
|
||||
*
|
||||
* @param ossConfigIds OSS配置ID串
|
||||
*/
|
||||
@SaCheckPermission("system:oss:remove")
|
||||
@Log(title = "对象存储配置", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ossConfigIds}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ossConfigIds) {
|
||||
boolean deleted = ossConfigService.deleteWithValidByIds(List.of(ossConfigIds), true);
|
||||
if (!deleted) {
|
||||
R.fail("删除对象存储配置记录失败!");
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 状态修改
|
||||
*/
|
||||
@SaCheckPermission("system:oss:edit")
|
||||
@Log(title = "对象存储状态修改", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/changeStatus")
|
||||
public R<Void> changeStatus(@RequestBody SysOssConfigBo bo) {
|
||||
boolean updated = ossConfigService.updateOssConfigStatus(bo);
|
||||
if (!updated) {
|
||||
R.fail("状态修改失败!");
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
package com.ruoyi.system.controller.system;
|
||||
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.ruoyi.common.core.core.domain.R;
|
||||
import com.ruoyi.common.web.core.BaseController;
|
||||
import com.ruoyi.common.log.annotation.Log;
|
||||
import com.ruoyi.common.log.enums.BusinessType;
|
||||
import com.ruoyi.common.orm.core.page.TableDataInfo;
|
||||
import com.ruoyi.system.domain.bo.SysOssBo;
|
||||
import com.ruoyi.system.domain.vo.SysOssUploadVo;
|
||||
import com.ruoyi.system.domain.vo.SysOssVo;
|
||||
import com.ruoyi.system.service.ISysOssService;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文件上传 控制层
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/resource/oss")
|
||||
public class SysOssController extends BaseController {
|
||||
|
||||
private final ISysOssService ossService;
|
||||
|
||||
/**
|
||||
* 查询OSS对象存储列表
|
||||
*/
|
||||
@SaCheckPermission("system:oss:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<SysOssVo> list(@Validated SysOssBo bo) {
|
||||
return ossService.queryPageList(bo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询OSS对象基于id串
|
||||
*
|
||||
* @param ossIds OSS对象ID串
|
||||
*/
|
||||
@SaCheckPermission("system:oss:list")
|
||||
@GetMapping("/listByIds/{ossIds}")
|
||||
public R<List<SysOssVo>> listByIds(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ossIds) {
|
||||
List<SysOssVo> list = ossService.listSysOssByIds(Arrays.asList(ossIds));
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传OSS对象存储
|
||||
*
|
||||
* @param file 文件
|
||||
*/
|
||||
@SaCheckPermission("system:oss:upload")
|
||||
@Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
|
||||
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public R<SysOssUploadVo> upload(@RequestPart("file") MultipartFile file) {
|
||||
if (ObjectUtil.isNull(file)) {
|
||||
return R.fail("上传文件不能为空");
|
||||
}
|
||||
SysOssVo oss = ossService.upload(file);
|
||||
SysOssUploadVo uploadVo = new SysOssUploadVo();
|
||||
uploadVo.setUrl(oss.getUrl());
|
||||
uploadVo.setFileName(oss.getOriginalName());
|
||||
uploadVo.setOssId(oss.getOssId().toString());
|
||||
return R.ok(uploadVo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载OSS对象
|
||||
*
|
||||
* @param ossId OSS对象ID
|
||||
*/
|
||||
@SaCheckPermission("system:oss:download")
|
||||
@GetMapping("/download/{ossId}")
|
||||
public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
|
||||
ossService.download(ossId, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除OSS对象存储
|
||||
*
|
||||
* @param ossIds OSS对象ID串
|
||||
*/
|
||||
@SaCheckPermission("system:oss:remove")
|
||||
@Log(title = "OSS对象存储", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{ossIds}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ossIds) {
|
||||
Boolean deleted = ossService.deleteWithValidByIds(List.of(ossIds), true);
|
||||
if (!deleted) {
|
||||
R.fail("删除OSS对象存储记录失败!");
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
}
|
@ -11,7 +11,9 @@ import com.ruoyi.system.domain.bo.SysUserBo;
|
||||
import com.ruoyi.system.domain.bo.SysUserProfileBo;
|
||||
import com.ruoyi.system.domain.vo.AvatarVo;
|
||||
import com.ruoyi.system.domain.vo.ProfileVo;
|
||||
import com.ruoyi.system.domain.vo.SysOssVo;
|
||||
import com.ruoyi.system.domain.vo.SysUserVo;
|
||||
import com.ruoyi.system.service.ISysOssService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -44,6 +46,8 @@ public class SysProfileController extends BaseController
|
||||
{
|
||||
@Resource
|
||||
private ISysUserService userService;
|
||||
@Resource
|
||||
private ISysOssService ossService;
|
||||
|
||||
/**
|
||||
* 个人信息
|
||||
@ -126,13 +130,9 @@ public class SysProfileController extends BaseController
|
||||
if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) {
|
||||
return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式");
|
||||
}
|
||||
|
||||
//TODO:需要使用OSS来存储操作用户上传的头像
|
||||
|
||||
SysUserVo sysUser = userService.selectUserById(LoginHelper.getUserId());
|
||||
String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION);
|
||||
if (userService.updateUserAvatar(sysUser.getUserName(), avatar))
|
||||
{
|
||||
SysOssVo oss = ossService.upload(file);
|
||||
String avatar = oss.getUrl();
|
||||
if (userService.updateUserAvatar(LoginHelper.getUserId(), oss.getOssId())) {
|
||||
AvatarVo avatarVo = new AvatarVo();
|
||||
avatarVo.setImgUrl(avatar);
|
||||
return R.ok(avatarVo);
|
||||
|
@ -0,0 +1,50 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import com.ruoyi.common.orm.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* OSS对象存储服务对象
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Table("sys_oss")
|
||||
public class SysOss extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 对象存储主键
|
||||
*/
|
||||
@Id
|
||||
private Long ossId;
|
||||
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 原名
|
||||
*/
|
||||
private String originalName;
|
||||
|
||||
/**
|
||||
* 文件后缀名
|
||||
*/
|
||||
private String fileSuffix;
|
||||
|
||||
/**
|
||||
* URL地址
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 服务商
|
||||
*/
|
||||
private String service;
|
||||
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import com.ruoyi.common.orm.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 对象存储配置对象 sys_oss_config
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Table("sys_oss_config")
|
||||
public class SysOssConfig extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 主建
|
||||
*/
|
||||
@Id
|
||||
private Long ossConfigId;
|
||||
|
||||
/**
|
||||
* 配置key
|
||||
*/
|
||||
private String configKey;
|
||||
|
||||
/**
|
||||
* accessKey
|
||||
*/
|
||||
private String accessKey;
|
||||
|
||||
/**
|
||||
* 秘钥
|
||||
*/
|
||||
private String secretKey;
|
||||
|
||||
/**
|
||||
* 桶名称
|
||||
*/
|
||||
private String bucketName;
|
||||
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
private String prefix;
|
||||
|
||||
/**
|
||||
* 访问站点
|
||||
*/
|
||||
private String endpoint;
|
||||
|
||||
/**
|
||||
* 自定义域名
|
||||
*/
|
||||
private String domain;
|
||||
|
||||
/**
|
||||
* 是否https(0否 1是)
|
||||
*/
|
||||
private String isHttps;
|
||||
|
||||
/**
|
||||
* 域
|
||||
*/
|
||||
private String region;
|
||||
|
||||
/**
|
||||
* 是否默认(0=是,1=否)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 扩展字段
|
||||
*/
|
||||
private String ext1;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 桶权限类型(0private 1public 2custom)
|
||||
*/
|
||||
private String accessPolicy;
|
||||
}
|
@ -1,32 +1,23 @@
|
||||
package com.ruoyi.system.domain;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import com.mybatisflex.annotation.*;
|
||||
import com.ruoyi.common.core.constant.UserConstants;
|
||||
import com.ruoyi.common.orm.core.domain.BaseEntity;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.ruoyi.common.core.annotation.Excel;
|
||||
import com.ruoyi.common.core.annotation.Excel.ColumnType;
|
||||
import com.ruoyi.common.core.annotation.Excel.Type;
|
||||
import com.ruoyi.common.core.annotation.Excels;
|
||||
import com.ruoyi.common.core.xss.Xss;
|
||||
|
||||
/**
|
||||
* 用户对象 sys_user
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Table(value = "sys_user")
|
||||
public class SysUser extends BaseEntity
|
||||
{
|
||||
@ -66,7 +57,7 @@ public class SysUser extends BaseEntity
|
||||
private String gender;
|
||||
|
||||
/** 用户头像 */
|
||||
private String avatar;
|
||||
private Long avatar;
|
||||
|
||||
/** 密码 */
|
||||
private String password;
|
||||
@ -100,26 +91,11 @@ public class SysUser extends BaseEntity
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
public SysUser()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public SysUser(Long userId)
|
||||
{
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public Long getUserId()
|
||||
{
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId)
|
||||
{
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public boolean isAdmin()
|
||||
{
|
||||
return isAdmin(this.userId);
|
||||
@ -130,225 +106,6 @@ public class SysUser extends BaseEntity
|
||||
return userId != null && 1L == userId;
|
||||
}
|
||||
|
||||
public Long getDeptId()
|
||||
{
|
||||
return deptId;
|
||||
}
|
||||
|
||||
public void setDeptId(Long deptId)
|
||||
{
|
||||
this.deptId = deptId;
|
||||
}
|
||||
|
||||
@Xss(message = "用户昵称不能包含脚本字符")
|
||||
@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
|
||||
public String getNickName()
|
||||
{
|
||||
return nickName;
|
||||
}
|
||||
|
||||
public void setNickName(String nickName)
|
||||
{
|
||||
this.nickName = nickName;
|
||||
}
|
||||
|
||||
@Xss(message = "用户账号不能包含脚本字符")
|
||||
@NotBlank(message = "用户账号不能为空")
|
||||
@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
|
||||
public String getUserName()
|
||||
{
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName)
|
||||
{
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public String getUserType() {
|
||||
return userType;
|
||||
}
|
||||
|
||||
public void setUserType(String userType) {
|
||||
this.userType = userType;
|
||||
}
|
||||
|
||||
@Email(message = "邮箱格式不正确")
|
||||
@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
|
||||
public String getEmail()
|
||||
{
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email)
|
||||
{
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
@Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符")
|
||||
public String getPhonenumber()
|
||||
{
|
||||
return phonenumber;
|
||||
}
|
||||
|
||||
public void setPhonenumber(String phonenumber)
|
||||
{
|
||||
this.phonenumber = phonenumber;
|
||||
}
|
||||
|
||||
public String getGender()
|
||||
{
|
||||
return gender;
|
||||
}
|
||||
|
||||
public void setGender(String gender)
|
||||
{
|
||||
this.gender = gender;
|
||||
}
|
||||
|
||||
public String getAvatar()
|
||||
{
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(String avatar)
|
||||
{
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@JsonProperty
|
||||
public String getPassword()
|
||||
{
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password)
|
||||
{
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getStatus()
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status)
|
||||
{
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getDelFlag()
|
||||
{
|
||||
return delFlag;
|
||||
}
|
||||
|
||||
public void setDelFlag(String delFlag)
|
||||
{
|
||||
this.delFlag = delFlag;
|
||||
}
|
||||
|
||||
public String getLoginIp()
|
||||
{
|
||||
return loginIp;
|
||||
}
|
||||
|
||||
public void setLoginIp(String loginIp)
|
||||
{
|
||||
this.loginIp = loginIp;
|
||||
}
|
||||
|
||||
public Date getLoginDate()
|
||||
{
|
||||
return loginDate;
|
||||
}
|
||||
|
||||
public void setLoginDate(Date loginDate)
|
||||
{
|
||||
this.loginDate = loginDate;
|
||||
}
|
||||
|
||||
public SysDept getDept()
|
||||
{
|
||||
return dept;
|
||||
}
|
||||
|
||||
public void setDept(SysDept dept)
|
||||
{
|
||||
this.dept = dept;
|
||||
}
|
||||
|
||||
public List<SysRole> getRoles()
|
||||
{
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(List<SysRole> roles)
|
||||
{
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public Long[] getRoleIds()
|
||||
{
|
||||
return roleIds;
|
||||
}
|
||||
|
||||
public void setRoleIds(Long[] roleIds)
|
||||
{
|
||||
this.roleIds = roleIds;
|
||||
}
|
||||
|
||||
public Long[] getPostIds()
|
||||
{
|
||||
return postIds;
|
||||
}
|
||||
|
||||
public void setPostIds(Long[] postIds)
|
||||
{
|
||||
this.postIds = postIds;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
.append("userId", getUserId())
|
||||
.append("deptId", getDeptId())
|
||||
.append("userName", getUserName())
|
||||
.append("nickName", getNickName())
|
||||
.append("userType", getUserType())
|
||||
.append("email", getEmail())
|
||||
.append("phonenumber", getPhonenumber())
|
||||
.append("sex", getGender())
|
||||
.append("avatar", getAvatar())
|
||||
.append("password", getPassword())
|
||||
.append("status", getStatus())
|
||||
.append("delFlag", getDelFlag())
|
||||
.append("loginIp", getLoginIp())
|
||||
.append("loginDate", getLoginDate())
|
||||
.append("createBy", getCreateBy())
|
||||
.append("createTime", getCreateTime())
|
||||
.append("updateBy", getUpdateBy())
|
||||
.append("updateTime", getUpdateTime())
|
||||
.append("remark", getRemark())
|
||||
.append("dept", getDept())
|
||||
.toString();
|
||||
}
|
||||
|
||||
public boolean isSuperAdmin() {
|
||||
return UserConstants.SUPER_ADMIN_ID.equals(this.userId);
|
||||
|
@ -0,0 +1,49 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import com.ruoyi.common.orm.core.domain.BaseEntity;
|
||||
import com.ruoyi.system.domain.SysOss;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* OSS对象存储分页查询对象 sys_oss
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = SysOss.class, reverseConvertGenerate = false)
|
||||
public class SysOssBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* ossId
|
||||
*/
|
||||
private Long ossId;
|
||||
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 原名
|
||||
*/
|
||||
private String originalName;
|
||||
|
||||
/**
|
||||
* 文件后缀名
|
||||
*/
|
||||
private String fileSuffix;
|
||||
|
||||
/**
|
||||
* URL地址
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 服务商
|
||||
*/
|
||||
private String service;
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package com.ruoyi.system.domain.bo;
|
||||
|
||||
import com.ruoyi.common.core.validate.AddGroup;
|
||||
import com.ruoyi.common.core.validate.EditGroup;
|
||||
import com.ruoyi.common.orm.core.domain.BaseEntity;
|
||||
import com.ruoyi.system.domain.SysOssConfig;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 对象存储配置业务对象 sys_oss_config
|
||||
*
|
||||
* @author Lion Li
|
||||
* @author 孤舟烟雨
|
||||
* @date 2021-08-13
|
||||
*/
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = SysOssConfig.class, reverseConvertGenerate = false)
|
||||
public class SysOssConfigBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 主建
|
||||
*/
|
||||
@NotNull(message = "主建不能为空", groups = {EditGroup.class})
|
||||
private Long ossConfigId;
|
||||
|
||||
/**
|
||||
* 配置key
|
||||
*/
|
||||
@NotBlank(message = "配置key不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
@Size(min = 2, max = 100, message = "configKey长度必须介于{min}和{max} 之间")
|
||||
private String configKey;
|
||||
|
||||
/**
|
||||
* accessKey
|
||||
*/
|
||||
@NotBlank(message = "accessKey不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
@Size(min = 2, max = 100, message = "accessKey长度必须介于{min}和{max} 之间")
|
||||
private String accessKey;
|
||||
|
||||
/**
|
||||
* 秘钥
|
||||
*/
|
||||
@NotBlank(message = "secretKey不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
@Size(min = 2, max = 100, message = "secretKey长度必须介于{min}和{max} 之间")
|
||||
private String secretKey;
|
||||
|
||||
/**
|
||||
* 桶名称
|
||||
*/
|
||||
@NotBlank(message = "桶名称不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
@Size(min = 2, max = 100, message = "bucketName长度必须介于{min}和{max}之间")
|
||||
private String bucketName;
|
||||
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
private String prefix;
|
||||
|
||||
/**
|
||||
* 访问站点
|
||||
*/
|
||||
@NotBlank(message = "访问站点不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
@Size(min = 2, max = 100, message = "endpoint长度必须介于{min}和{max}之间")
|
||||
private String endpoint;
|
||||
|
||||
/**
|
||||
* 自定义域名
|
||||
*/
|
||||
private String domain;
|
||||
|
||||
/**
|
||||
* 是否https(Y=是,N=否)
|
||||
*/
|
||||
private String isHttps;
|
||||
|
||||
/**
|
||||
* 是否默认(0=是,1=否)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 域
|
||||
*/
|
||||
private String region;
|
||||
|
||||
/**
|
||||
* 扩展字段
|
||||
*/
|
||||
private String ext1;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 桶权限类型(0private 1public 2custom)
|
||||
*/
|
||||
@NotBlank(message = "桶权限类型不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private String accessPolicy;
|
||||
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.ruoyi.system.domain.SysOssConfig;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
* 对象存储配置视图对象 sys_oss_config
|
||||
*
|
||||
* @author Lion Li
|
||||
* @author 孤舟烟雨
|
||||
* @date 2021-08-13
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = SysOssConfig.class)
|
||||
public class SysOssConfigVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主建
|
||||
*/
|
||||
private Long ossConfigId;
|
||||
|
||||
/**
|
||||
* 配置key
|
||||
*/
|
||||
private String configKey;
|
||||
|
||||
/**
|
||||
* accessKey
|
||||
*/
|
||||
private String accessKey;
|
||||
|
||||
/**
|
||||
* 秘钥
|
||||
*/
|
||||
private String secretKey;
|
||||
|
||||
/**
|
||||
* 桶名称
|
||||
*/
|
||||
private String bucketName;
|
||||
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
private String prefix;
|
||||
|
||||
/**
|
||||
* 访问站点
|
||||
*/
|
||||
private String endpoint;
|
||||
|
||||
/**
|
||||
* 自定义域名
|
||||
*/
|
||||
private String domain;
|
||||
|
||||
/**
|
||||
* 是否https(Y=是,N=否)
|
||||
*/
|
||||
private String isHttps;
|
||||
|
||||
/**
|
||||
* 域
|
||||
*/
|
||||
private String region;
|
||||
|
||||
/**
|
||||
* 是否默认(0=是,1=否)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 扩展字段
|
||||
*/
|
||||
private String ext1;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 桶权限类型(0private 1public 2custom)
|
||||
*/
|
||||
private String accessPolicy;
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 上传对象信息
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
*/
|
||||
@Data
|
||||
public class SysOssUploadVo {
|
||||
|
||||
/**
|
||||
* URL地址
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 对象存储主键
|
||||
*/
|
||||
private String ossId;
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package com.ruoyi.system.domain.vo;
|
||||
|
||||
import com.ruoyi.system.domain.SysOss;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* OSS对象存储视图对象 sys_oss
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@AutoMapper(target = SysOss.class)
|
||||
public class SysOssVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 对象存储主键
|
||||
*/
|
||||
private Long ossId;
|
||||
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 原名
|
||||
*/
|
||||
private String originalName;
|
||||
|
||||
/**
|
||||
* 文件后缀名
|
||||
*/
|
||||
private String fileSuffix;
|
||||
|
||||
/**
|
||||
* URL地址
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 上传人
|
||||
*/
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
* 上传人名称
|
||||
*/
|
||||
private String createByName;
|
||||
|
||||
/**
|
||||
* 服务商
|
||||
*/
|
||||
private String service;
|
||||
|
||||
|
||||
}
|
@ -3,6 +3,7 @@ package com.ruoyi.system.domain.vo;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.mybatisflex.annotation.ColumnMask;
|
||||
import com.mybatisflex.annotation.RelationOneToOne;
|
||||
import com.mybatisflex.core.mask.Masks;
|
||||
import com.ruoyi.common.translation.annotation.Translation;
|
||||
import com.ruoyi.common.translation.constant.TransConstant;
|
||||
@ -78,9 +79,19 @@ public class SysUserVo implements Serializable {
|
||||
/**
|
||||
* 头像地址
|
||||
*/
|
||||
@Translation(type = TransConstant.OSS_ID_TO_URL)
|
||||
private Long avatar;
|
||||
|
||||
/**
|
||||
* 头像地址URL
|
||||
*/
|
||||
@RelationOneToOne(
|
||||
selfField = "avatar",
|
||||
targetTable = "sys_oss",
|
||||
targetField = "ossId",
|
||||
valueField = "url"
|
||||
)
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
|
@ -0,0 +1,17 @@
|
||||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
import com.ruoyi.system.domain.SysOssConfig;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 对象存储配置Mapper接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @author 孤舟烟雨
|
||||
* @date 2021-08-13
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysOssConfigMapper extends BaseMapper<SysOssConfig> {
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.ruoyi.system.mapper;
|
||||
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
import com.ruoyi.system.domain.SysOss;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 文件上传 数据层
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysOssMapper extends BaseMapper<SysOss> {
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.ruoyi.system.runner;
|
||||
|
||||
import com.ruoyi.system.service.ISysOssConfigService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 初始化 system 模块对应业务数据
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class SystemApplicationRunner implements ApplicationRunner {
|
||||
|
||||
private final ISysOssConfigService ossConfigService;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
ossConfigService.init();
|
||||
log.info("初始化OSS配置成功");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import com.ruoyi.common.orm.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.orm.core.service.IBaseService;
|
||||
import com.ruoyi.system.domain.SysOssConfig;
|
||||
import com.ruoyi.system.domain.bo.SysOssConfigBo;
|
||||
import com.ruoyi.system.domain.vo.SysOssConfigVo;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 对象存储配置Service接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @author 孤舟烟雨
|
||||
* @date 2021-08-13
|
||||
*/
|
||||
public interface ISysOssConfigService extends IBaseService<SysOssConfig> {
|
||||
|
||||
/**
|
||||
* 初始化OSS配置
|
||||
*/
|
||||
void init();
|
||||
|
||||
/**
|
||||
* 查询单个
|
||||
*/
|
||||
SysOssConfigVo queryById(Long ossConfigId);
|
||||
|
||||
/**
|
||||
* 查询列表
|
||||
*/
|
||||
TableDataInfo<SysOssConfigVo> queryPageList(SysOssConfigBo bo);
|
||||
|
||||
|
||||
/**
|
||||
* 根据新增业务对象插入对象存储配置
|
||||
*
|
||||
* @param bo 对象存储配置新增业务对象
|
||||
* @return
|
||||
*/
|
||||
Boolean insertByBo(SysOssConfigBo bo);
|
||||
|
||||
/**
|
||||
* 根据编辑业务对象修改对象存储配置
|
||||
*
|
||||
* @param bo 对象存储配置编辑业务对象
|
||||
* @return
|
||||
*/
|
||||
Boolean updateByBo(SysOssConfigBo bo);
|
||||
|
||||
/**
|
||||
* 校验并删除数据
|
||||
*
|
||||
* @param ids 主键集合
|
||||
* @param isValid 是否校验,true-删除前校验,false-不校验
|
||||
* @return
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 转到非默认状态
|
||||
*/
|
||||
boolean updateOssConfigStatus(SysOssConfigBo bo);
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.ruoyi.system.service;
|
||||
|
||||
import com.ruoyi.common.orm.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.orm.core.service.IBaseService;
|
||||
import com.ruoyi.system.domain.SysOss;
|
||||
import com.ruoyi.system.domain.bo.SysOssBo;
|
||||
import com.ruoyi.system.domain.vo.SysOssVo;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文件上传 服务层
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface ISysOssService extends IBaseService<SysOss> {
|
||||
|
||||
TableDataInfo<SysOssVo> queryPageList(SysOssBo sysOss);
|
||||
|
||||
List<SysOssVo> listSysOssByIds(Collection<Long> ossIds);
|
||||
|
||||
SysOssVo getById(Long ossId);
|
||||
|
||||
SysOssVo upload(MultipartFile file);
|
||||
|
||||
SysOssVo upload(File file);
|
||||
|
||||
void download(Long ossId, HttpServletResponse response) throws IOException;
|
||||
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
}
|
@ -195,11 +195,11 @@ public interface ISysUserService extends IBaseService<SysUser>
|
||||
/**
|
||||
* 修改用户头像
|
||||
*
|
||||
* @param userName 用户名
|
||||
* @param userId 用户ID
|
||||
* @param avatar 头像地址
|
||||
* @return 结果:true 更新成功,false 更新失败
|
||||
* @return 结果
|
||||
*/
|
||||
boolean updateUserAvatar(String userName, String avatar);
|
||||
boolean updateUserAvatar(Long userId, Long avatar);
|
||||
|
||||
/**
|
||||
* 重置用户密码
|
||||
|
@ -0,0 +1,192 @@
|
||||
package com.ruoyi.system.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.ruoyi.common.orm.core.service.impl.BaseServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.ruoyi.common.core.constant.CacheNames;
|
||||
import com.ruoyi.common.core.exception.ServiceException;
|
||||
import com.ruoyi.common.core.utils.MapstructUtils;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.common.json.utils.JsonUtils;
|
||||
import com.ruoyi.common.orm.core.page.PageQuery;
|
||||
import com.ruoyi.common.orm.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.oss.constant.OssConstant;
|
||||
import com.ruoyi.common.redis.utils.CacheUtils;
|
||||
import com.ruoyi.common.redis.utils.RedisUtils;
|
||||
import com.ruoyi.system.domain.SysOssConfig;
|
||||
import com.ruoyi.system.domain.bo.SysOssConfigBo;
|
||||
import com.ruoyi.system.domain.vo.SysOssConfigVo;
|
||||
import com.ruoyi.system.mapper.SysOssConfigMapper;
|
||||
import com.ruoyi.system.service.ISysOssConfigService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static com.ruoyi.system.domain.table.SysOssConfigTableDef.SYS_OSS_CONFIG;
|
||||
|
||||
/**
|
||||
* 对象存储配置Service业务层处理
|
||||
*
|
||||
* @author Lion Li
|
||||
* @author 孤舟烟雨
|
||||
* @author 数据小王子
|
||||
* @date 2023-11-30
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class SysOssConfigServiceImpl extends BaseServiceImpl<SysOssConfigMapper,SysOssConfig> implements ISysOssConfigService {
|
||||
|
||||
private final SysOssConfigMapper baseMapper;
|
||||
|
||||
@Override
|
||||
public QueryWrapper query() {
|
||||
return super.query().from(SYS_OSS_CONFIG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 项目启动时,初始化参数到缓存,加载配置类
|
||||
*/
|
||||
@Override
|
||||
public void init() {
|
||||
List<SysOssConfig> list = this.list();
|
||||
// 加载OSS初始化配置
|
||||
for (SysOssConfig config : list) {
|
||||
String configKey = config.getConfigKey();
|
||||
if ("0".equals(config.getStatus())) {
|
||||
RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, configKey);
|
||||
}
|
||||
CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysOssConfigVo queryById(Long ossConfigId) {
|
||||
return this.getOneAs(query().where(SYS_OSS_CONFIG.OSS_CONFIG_ID.eq(ossConfigId)), SysOssConfigVo.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据bo构建QueryWrapper查询条件
|
||||
*
|
||||
* @param bo
|
||||
* @return 查询条件
|
||||
*/
|
||||
private QueryWrapper buildQueryWrapper(SysOssConfigBo bo) {
|
||||
QueryWrapper queryWrapper = super.buildBaseQueryWrapper();
|
||||
|
||||
if (StringUtils.isNotEmpty(bo.getConfigKey())) {
|
||||
queryWrapper.and(SYS_OSS_CONFIG.CONFIG_KEY.eq(bo.getConfigKey()));
|
||||
}
|
||||
if (StringUtils.isNotEmpty(bo.getBucketName())) {
|
||||
queryWrapper.and(SYS_OSS_CONFIG.BUCKET_NAME.like(bo.getBucketName()));
|
||||
}
|
||||
if (ObjectUtil.isNotNull(bo.getStatus())) {
|
||||
queryWrapper.and(SYS_OSS_CONFIG.STATUS.eq(bo.getStatus()));
|
||||
}
|
||||
queryWrapper.orderBy(SYS_OSS_CONFIG.OSS_CONFIG_ID.asc());
|
||||
|
||||
return queryWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<SysOssConfigVo> queryPageList(SysOssConfigBo bo) {
|
||||
QueryWrapper queryWrapper = buildQueryWrapper(bo);
|
||||
Page<SysOssConfigVo> page = this.pageAs(PageQuery.build(), queryWrapper, SysOssConfigVo.class);
|
||||
return TableDataInfo.build(page);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Boolean insertByBo(SysOssConfigBo bo) {
|
||||
SysOssConfig config = MapstructUtils.convert(bo, SysOssConfig.class);
|
||||
validEntityBeforeSave(config);
|
||||
boolean flag = this.save(config);
|
||||
if (flag) {
|
||||
// 从数据库查询完整的数据做缓存
|
||||
config = this.getById(config.getOssConfigId());
|
||||
CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config));
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean updateByBo(SysOssConfigBo bo) {
|
||||
SysOssConfig config = MapstructUtils.convert(bo, SysOssConfig.class);
|
||||
validEntityBeforeSave(config);
|
||||
boolean flag = this.updateById(config);
|
||||
if (flag) {
|
||||
// 从数据库查询完整的数据做缓存
|
||||
config = this.getById(config.getOssConfigId());
|
||||
CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config));
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(SysOssConfig entity) {
|
||||
if (StringUtils.isNotEmpty(entity.getConfigKey())
|
||||
&& !checkConfigKeyUnique(entity)) {
|
||||
throw new ServiceException("操作配置'" + entity.getConfigKey() + "'失败, 配置key已存在!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if (isValid) {
|
||||
if (CollUtil.containsAny(ids, OssConstant.SYSTEM_DATA_IDS)) {
|
||||
throw new ServiceException("系统内置, 不可删除!");
|
||||
}
|
||||
}
|
||||
List<SysOssConfig> list = CollUtil.newArrayList();
|
||||
for (Long configId : ids) {
|
||||
SysOssConfig config = this.getById(configId);
|
||||
list.add(config);
|
||||
}
|
||||
boolean flag = this.removeByIds(ids);
|
||||
if (flag) {
|
||||
list.forEach(sysOssConfig ->
|
||||
CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, sysOssConfig.getConfigKey()));
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断configKey是否唯一
|
||||
*/
|
||||
private boolean checkConfigKeyUnique(SysOssConfig sysOssConfig) {
|
||||
long ossConfigId = ObjectUtil.isNull(sysOssConfig.getOssConfigId()) ? -1L : sysOssConfig.getOssConfigId();
|
||||
|
||||
QueryWrapper queryWrapper = query();
|
||||
if (StringUtils.isNotEmpty(sysOssConfig.getConfigKey())) {
|
||||
queryWrapper.where(SYS_OSS_CONFIG.CONFIG_KEY.eq(sysOssConfig.getConfigKey()));
|
||||
}
|
||||
SysOssConfig info = this.getOne(queryWrapper);
|
||||
if (ObjectUtil.isNotNull(info) && info.getOssConfigId() != ossConfigId) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转到非默认状态:停用
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean updateOssConfigStatus(SysOssConfigBo bo) {
|
||||
SysOssConfig sysOssConfig = MapstructUtils.convert(bo, SysOssConfig.class);
|
||||
boolean updated = this.updateById(sysOssConfig);
|
||||
if (updated) {
|
||||
RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, sysOssConfig.getConfigKey());
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
package com.ruoyi.system.service.impl;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.ruoyi.common.core.constant.CacheNames;
|
||||
import com.ruoyi.common.core.exception.ServiceException;
|
||||
import com.ruoyi.common.core.service.OssService;
|
||||
import com.ruoyi.common.core.utils.MapstructUtils;
|
||||
import com.ruoyi.common.core.utils.SpringUtils;
|
||||
import com.ruoyi.common.core.utils.StreamUtils;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.common.core.utils.file.FileUtils;
|
||||
import com.ruoyi.common.orm.core.page.PageQuery;
|
||||
import com.ruoyi.common.orm.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.orm.core.service.impl.BaseServiceImpl;
|
||||
import com.ruoyi.common.oss.core.OssClient;
|
||||
import com.ruoyi.common.oss.entity.UploadResult;
|
||||
import com.ruoyi.common.oss.enumd.AccessPolicyType;
|
||||
import com.ruoyi.common.oss.factory.OssFactory;
|
||||
import com.ruoyi.system.domain.SysOss;
|
||||
import com.ruoyi.system.domain.bo.SysOssBo;
|
||||
import com.ruoyi.system.domain.vo.SysOssVo;
|
||||
import com.ruoyi.system.mapper.SysOssMapper;
|
||||
import com.ruoyi.system.service.ISysOssService;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
||||
import static com.ruoyi.system.domain.table.SysOssTableDef.SYS_OSS;
|
||||
|
||||
/**
|
||||
* 文件上传 服务层实现
|
||||
*
|
||||
* @author Lion Li
|
||||
* @author 数据小王子
|
||||
* @date 2023-11-30
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class SysOssServiceImpl extends BaseServiceImpl<SysOssMapper, SysOss> implements ISysOssService, OssService {
|
||||
|
||||
private final SysOssMapper baseMapper;
|
||||
|
||||
@Override
|
||||
public QueryWrapper query() {
|
||||
return super.query().from(SYS_OSS);
|
||||
}
|
||||
|
||||
private QueryWrapper buildQueryWrapper(SysOssBo bo) {
|
||||
QueryWrapper queryWrapper = super.buildBaseQueryWrapper();
|
||||
|
||||
if (StringUtils.isNotEmpty(bo.getFileName())) {
|
||||
queryWrapper.where(SYS_OSS.FILE_NAME.like(bo.getFileName()));
|
||||
}
|
||||
if (StringUtils.isNotEmpty(bo.getOriginalName())) {
|
||||
queryWrapper.and(SYS_OSS.ORIGINAL_NAME.like(bo.getOriginalName()));
|
||||
}
|
||||
if (StringUtils.isNotEmpty(bo.getFileSuffix())) {
|
||||
queryWrapper.and(SYS_OSS.FILE_SUFFIX.eq(bo.getFileSuffix()));
|
||||
}
|
||||
if (StringUtils.isNotEmpty(bo.getUrl())) {
|
||||
queryWrapper.and(SYS_OSS.URL.eq(bo.getUrl()));
|
||||
}
|
||||
Map<String, Object> params = bo.getParams();
|
||||
if (params.get("beginCreateTime") != null && params.get("endCreateTime") != null) {
|
||||
queryWrapper.and(SYS_OSS.CREATE_TIME.between(params.get("beginCreateTime"), params.get("endCreateTime")));
|
||||
}
|
||||
if (StringUtils.isNotEmpty(bo.getService())) {
|
||||
queryWrapper.and(SYS_OSS.SERVICE.eq(bo.getService()));
|
||||
}
|
||||
queryWrapper.orderBy(SYS_OSS.OSS_ID.asc());
|
||||
return queryWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<SysOssVo> queryPageList(SysOssBo bo) {
|
||||
QueryWrapper queryWrapper = buildQueryWrapper(bo);
|
||||
Page<SysOssVo> result = this.pageAs(PageQuery.build(), queryWrapper, SysOssVo.class);
|
||||
List<SysOssVo> filterResult = StreamUtils.toList(result.getRecords(), this::matchingUrl);
|
||||
result.setRecords(filterResult);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SysOssVo> listSysOssByIds(Collection<Long> ossIds) {
|
||||
List<SysOssVo> list = new ArrayList<>();
|
||||
for (Long id : ossIds) {
|
||||
SysOssVo vo = SpringUtils.getAopProxy(this).getById(id);
|
||||
if (ObjectUtil.isNotNull(vo)) {
|
||||
try {
|
||||
list.add(this.matchingUrl(vo));
|
||||
} catch (Exception ignored) {
|
||||
// 如果oss异常无法连接则将数据直接返回
|
||||
list.add(vo);
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String selectUrlByIds(String ossIds) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) {
|
||||
SysOssVo vo = SpringUtils.getAopProxy(this).getById(id);
|
||||
if (ObjectUtil.isNotNull(vo)) {
|
||||
try {
|
||||
list.add(this.matchingUrl(vo).getUrl());
|
||||
} catch (Exception ignored) {
|
||||
// 如果oss异常无法连接则将数据直接返回
|
||||
list.add(vo.getUrl());
|
||||
}
|
||||
}
|
||||
}
|
||||
return String.join(StringUtils.SEPARATOR, list);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId")
|
||||
@Override
|
||||
public SysOssVo getById(Long ossId) {
|
||||
QueryWrapper queryWrapper=query().where(SYS_OSS.OSS_ID.eq(ossId));
|
||||
return this.getOneAs(queryWrapper,SysOssVo.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(Long ossId, HttpServletResponse response) throws IOException {
|
||||
SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId);
|
||||
if (ObjectUtil.isNull(sysOss)) {
|
||||
throw new ServiceException("文件数据不存在!");
|
||||
}
|
||||
FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName());
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
|
||||
OssClient storage = OssFactory.instance(sysOss.getService());
|
||||
try(InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) {
|
||||
int available = inputStream.available();
|
||||
IoUtil.copy(inputStream, response.getOutputStream(), available);
|
||||
response.setContentLength(available);
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysOssVo upload(MultipartFile file) {
|
||||
String originalfileName = file.getOriginalFilename();
|
||||
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
|
||||
OssClient storage = OssFactory.instance();
|
||||
UploadResult uploadResult;
|
||||
try {
|
||||
uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException(e.getMessage());
|
||||
}
|
||||
// 保存文件信息
|
||||
return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysOssVo upload(File file) {
|
||||
String originalfileName = file.getName();
|
||||
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
|
||||
OssClient storage = OssFactory.instance();
|
||||
UploadResult uploadResult = storage.uploadSuffix(file, suffix);
|
||||
// 保存文件信息
|
||||
return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult);
|
||||
}
|
||||
|
||||
private SysOssVo buildResultEntity(String originalfileName, String suffix, String configKey, UploadResult uploadResult) {
|
||||
SysOss oss = new SysOss();
|
||||
oss.setUrl(uploadResult.getUrl());
|
||||
oss.setFileSuffix(suffix);
|
||||
oss.setFileName(uploadResult.getFilename());
|
||||
oss.setOriginalName(originalfileName);
|
||||
oss.setService(configKey);
|
||||
this.save(oss);
|
||||
SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class);
|
||||
return this.matchingUrl(sysOssVo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if (isValid) {
|
||||
// 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
|
||||
List<SysOss> list = this.listByIds(ids);
|
||||
for (SysOss sysOss : list) {
|
||||
OssClient storage = OssFactory.instance(sysOss.getService());
|
||||
storage.delete(sysOss.getUrl());
|
||||
}
|
||||
return this.removeByIds(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 匹配Url
|
||||
*
|
||||
* @param oss OSS对象
|
||||
* @return oss 匹配Url的OSS对象
|
||||
*/
|
||||
private SysOssVo matchingUrl(SysOssVo oss) {
|
||||
OssClient storage = OssFactory.instance(oss.getService());
|
||||
// 仅修改桶类型为 private 的URL,临时URL时长为120s
|
||||
if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {
|
||||
oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120));
|
||||
}
|
||||
return oss;
|
||||
}
|
||||
}
|
@ -301,7 +301,7 @@ public class SysUserServiceImpl extends BaseServiceImpl<SysUserMapper, SysUser>
|
||||
if (ObjectUtil.isNotNull(userId)) {
|
||||
queryWrapper.where(SYS_USER.USER_ID.eq(userId));
|
||||
}
|
||||
return this.getOneAs(queryWrapper,SysUserVo.class);
|
||||
return userMapper.selectOneWithRelationsByQueryAs(queryWrapper,SysUserVo.class);//使用Relation注解从sys_oss中查询头像地址URL
|
||||
}
|
||||
|
||||
/**
|
||||
@ -565,15 +565,16 @@ public class SysUserServiceImpl extends BaseServiceImpl<SysUserMapper, SysUser>
|
||||
|
||||
/**
|
||||
* 修改用户头像
|
||||
* update sys_user set avatar = #{avatar} where user_name = #{userName}
|
||||
* @param userName 用户名
|
||||
* @param avatar 头像地址
|
||||
* update sys_user set avatar = #{avatar} where user_id = #{userId}
|
||||
* @param userId 用户ID
|
||||
* @param avatar 头像地址
|
||||
* @return 结果:true 更新成功,false 更新失败
|
||||
*/
|
||||
@Override
|
||||
public boolean updateUserAvatar(String userName, String avatar) {
|
||||
QueryWrapper queryWrapper = query().where(SYS_USER.USER_NAME.eq(userName));
|
||||
public boolean updateUserAvatar(Long userId, Long avatar) {
|
||||
QueryWrapper queryWrapper = query().where(SYS_USER.USER_ID.eq(userId));
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.setUserId(userId);
|
||||
sysUser.setAvatar(avatar);
|
||||
return this.update(sysUser,queryWrapper);
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.system.mapper.SysOssConfigMapper">
|
||||
|
||||
</mapper>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.system.mapper.SysOssMapper">
|
||||
|
||||
</mapper>
|
Loading…
Reference in New Issue
Block a user