存储配置功能完成

This commit is contained in:
RockYang
2025-08-29 09:08:17 +08:00
parent ebaaefaf7a
commit 696ef20a80
6 changed files with 228 additions and 167 deletions

View File

@@ -8,41 +8,41 @@ package types
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
type OSSConfig struct {
Active string
Local LocalStorageConfig
Minio MiniOssConfig
QiNiu QiNiuOssConfig
AliYun AliYunOssConfig
Active string `json:"active"`
Local LocalStorageConfig `json:"local"`
Minio MiniOssConfig `json:"minio"`
QiNiu QiNiuOssConfig `json:"qiniu"`
AliYun AliYunOssConfig `json:"aliyun"`
}
type MiniOssConfig struct {
Endpoint string
AccessKey string
AccessSecret string
Bucket string
SubDir string
UseSSL bool
Domain string
Endpoint string `json:"endpoint"`
AccessKey string `json:"access_key"`
AccessSecret string `json:"access_secret"`
Bucket string `json:"bucket"`
SubDir string `json:"sub_dir"`
UseSSL bool `json:"use_ssl"`
Domain string `json:"domain"`
}
type QiNiuOssConfig struct {
Zone string
AccessKey string
AccessSecret string
Bucket string
SubDir string
Domain string
Zone string `json:"zone"`
AccessKey string `json:"access_key"`
AccessSecret string `json:"access_secret"`
Bucket string `json:"bucket"`
SubDir string `json:"sub_dir"`
Domain string `json:"domain"`
}
type AliYunOssConfig struct {
Endpoint string
AccessKey string
AccessSecret string
Bucket string
SubDir string
Domain string
Endpoint string `json:"endpoint"`
AccessKey string `json:"access_key"`
AccessSecret string `json:"access_secret"`
Bucket string `json:"bucket"`
Domain string `json:"domain"`
}
type LocalStorageConfig struct {
BasePath string
BaseURL string
BasePath string `json:"base_path"`
BaseURL string `json:"base_url"`
}

View File

@@ -15,16 +15,16 @@ type SMSConfig struct {
// SmsConfigAli 阿里云短信平台配置
type SmsConfigAli struct {
AccessKey string
AccessSecret string
Sign string // 短信签名
CodeTempId string // 验证码短信模板 ID
AccessKey string `json:"access_key"`
AccessSecret string `json:"access_secret"`
Sign string `json:"sign"` // 短信签名
CodeTempId string `json:"code_temp_id"` // 验证码短信模板 ID
}
// SmsConfigBao 短信宝平台配置
type SmsConfigBao struct {
Username string //短信宝平台注册的用户名
Password string //短信宝平台注册的密码
Sign string // 短信签名
CodeTemplate string // 验证码短信模板 匹配
Username string `json:"username"` //短信宝平台注册的用户名
Password string `json:"password"` //短信宝平台注册的密码
Sign string `json:"sign"` // 短信签名
CodeTemplate string `json:"code_template"` // 验证码短信模板 匹配
}

View File

@@ -70,7 +70,7 @@ func (s AliYunOss) PutFile(ctx *gin.Context, name string) (File, error) {
defer src.Close()
fileExt := filepath.Ext(file.Filename)
objectKey := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), fileExt)
objectKey := fmt.Sprintf("%d%s", time.Now().UnixMicro(), fileExt)
// 上传文件
err = s.bucket.PutObject(objectKey, src)
if err != nil {
@@ -104,7 +104,7 @@ func (s AliYunOss) PutUrlFile(fileURL string, ext string, useProxy bool) (string
if ext == "" {
ext = filepath.Ext(parse.Path)
}
objectKey := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), ext)
objectKey := fmt.Sprintf("%d%s", time.Now().UnixMicro(), ext)
// 上传文件字节数据
err = s.bucket.PutObject(objectKey, bytes.NewReader(fileData))
if err != nil {
@@ -118,7 +118,7 @@ func (s AliYunOss) PutBase64(base64Img string) (string, error) {
if err != nil {
return "", fmt.Errorf("error decoding base64:%v", err)
}
objectKey := fmt.Sprintf("%s/%d.png", s.config.SubDir, time.Now().UnixMicro())
objectKey := fmt.Sprintf("%d.png", time.Now().UnixMicro())
// 上传文件字节数据
err = s.bucket.PutObject(objectKey, bytes.NewReader(imageData))
if err != nil {
@@ -130,8 +130,7 @@ func (s AliYunOss) PutBase64(base64Img string) (string, error) {
func (s AliYunOss) Delete(fileURL string) error {
var objectKey string
if strings.HasPrefix(fileURL, "http") {
filename := filepath.Base(fileURL)
objectKey = fmt.Sprintf("%s/%s", s.config.SubDir, filename)
objectKey = filepath.Base(fileURL)
} else {
objectKey = fileURL
}

View File

@@ -323,13 +323,24 @@ const uploadImg = (file) => {
}
</script>
<style lang="scss" scoped>
@use '../../../assets/css/admin/form.scss' as *;
@use '../../../assets/css/main.scss' as *;
<style lang="scss">
@use '@/assets/css/admin/form.scss' as *;
@use '@/assets/css/main.scss' as *;
.basic-config {
display: flex;
justify-content: center;
padding: 20px;
a {
color: #409eff;
&:hover {
text-decoration: underline;
}
}
.el-form-item__label {
font-weight: 700;
}
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div class="container py-3 px-10" v-loading="loading">
<div class="settings container p-5">
<el-tabs v-model="active" type="border-card">
<el-tab-pane label="支付宝" name="alipay">
<template #label>
@@ -17,7 +17,7 @@
>
</div>
<el-form :model="alipay" label-width="140px" label-position="top">
<el-form :model="alipay" class="mt-4" label-position="top">
<el-form-item label="商户ID"><el-input v-model="alipay.app_id" /></el-form-item>
<el-form-item label="商户私钥"
><el-input v-model="alipay.private_key" type="textarea" :rows="5"
@@ -25,14 +25,12 @@
<el-form-item label="支付宝公钥"
><el-input v-model="alipay.alipay_public_key" type="textarea" :rows="3"
/></el-form-item>
<el-form-item
label="回调域名(<span class='text-red-500'>请确保回调域名已备案且在支付宝应用添加了白名单</span>"
>
<el-form-item>
<template #label>
<label class="form-label"
>支付回调域名
<el-tooltip
placement="top"
placement="right"
content="请确保回调域名已备案且在支付宝应用添加了白名单"
>
<i class="iconfont icon-info"></i>
@@ -43,65 +41,75 @@
</el-form-item>
<el-form-item label="启用该支付通道"><el-switch v-model="alipay.enabled" /></el-form-item>
<el-form-item label="启用沙盒模式"><el-switch v-model="alipay.sandbox" /></el-form-item>
<el-form-item>
<el-button type="primary" @click="save('alipay')">保存</el-button>
<el-button @click="test('alipay')">测试</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="微信支付" name="wxpay">
<el-form :model="wxpay" label-width="140px">
<el-form-item label="启用通道"><el-switch v-model="wxpay.enabled" /></el-form-item>
<el-form-item label="AppId"><el-input v-model="wxpay.app_id" /></el-form-item>
<template #label>
<div class="d-flex align-items-center text-green-600">
<i class="iconfont icon-wechat-pay"></i>
<span class="ms-2">微信支付</span>
</div>
</template>
<div class="rounded-md bg-blue-100 p-3 text-gray-500 border-blue-500 border-2 text-base">
如果你不知道怎么获取这些配置信息请参考文档
<a
href="https://docs.geekai.me/plus/config/payment.html#%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98%E9%85%8D%E7%BD%AE"
target="_blank"
>微信支付配置</a
>
</div>
<el-form :model="wxpay" class="mt-4" label-position="top">
<el-form-item label="AppID"><el-input v-model="wxpay.app_id" /></el-form-item>
<el-form-item label="商户号(MchId)"><el-input v-model="wxpay.mch_id" /></el-form-item>
<el-form-item label="证书序列号"><el-input v-model="wxpay.serial_no" /></el-form-item>
<el-form-item label="商户私钥"
><el-input v-model="wxpay.private_key" type="textarea" :rows="3"
/></el-form-item>
<el-form-item label="APIv3 Key"><el-input v-model="wxpay.api_v3_key" /></el-form-item>
<el-form-item label="回调域名"><el-input v-model="wxpay.domain" /></el-form-item>
<el-form-item label="API V3 密钥"><el-input v-model="wxpay.api_v3_key" /></el-form-item>
<el-form-item>
<el-button type="primary" @click="save('wxpay')">保存</el-button>
<el-button @click="test('wxpay')">测试</el-button>
<template #label>
<label class="form-label">回调域名</label>
<el-tooltip placement="right" content="请确保回调域名已备案且在微信应用添加了白名单">
<i class="iconfont icon-info ml-2"></i>
</el-tooltip>
</template>
<el-input v-model="wxpay.domain" />
</el-form-item>
<el-form-item label="启用该支付通道"><el-switch v-model="wxpay.enabled" /></el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="易支付" name="epay">
<el-form :model="epay" label-width="140px">
<el-form-item label="启用通道"><el-switch v-model="epay.enabled" /></el-form-item>
<template #label>
<div class="d-flex align-items-center text-purple-600">
<i class="iconfont icon-reward"></i>
<span class="ms-2">易支付</span>
</div>
</template>
<div class="rounded-md bg-blue-100 p-3 text-gray-500 border-blue-500 border-2 text-base">
如果你不知道怎么获取这些配置信息请参考文档
<a
href="https://docs.geekai.me/plus/config/payment.html#%E6%98%93%E6%94%AF%E4%BB%98%E5%BC%80%E9%80%9A"
target="_blank"
>易支付配置</a
>
</div>
<el-form :model="epay" class="mt-4" label-position="top">
<el-form-item label="商户ID"><el-input v-model="epay.app_id" /></el-form-item>
<el-form-item label="商户私钥"
><el-input v-model="epay.private_key" type="textarea" :rows="3"
/></el-form-item>
<el-form-item label="商户私钥"><el-input v-model="epay.private_key" /></el-form-item>
<el-form-item label="网关地址"><el-input v-model="epay.api_url" /></el-form-item>
<el-form-item label="回调域名"><el-input v-model="epay.domain" /></el-form-item>
<el-form-item>
<el-button type="primary" @click="save('epay')">保存</el-button>
<el-button @click="test('epay')">测试</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="易支付" name="epay">
<el-form :model="epay" label-width="140px">
<el-form-item label="启用通道"><el-switch v-model="epay.enabled" /></el-form-item>
<el-form-item label="商户ID"><el-input v-model="epay.app_id" /></el-form-item>
<el-form-item label="商户私钥"
><el-input v-model="epay.private_key" type="textarea" :rows="3"
/></el-form-item>
<el-form-item label="网关地址"><el-input v-model="epay.api_url" /></el-form-item>
<el-form-item label="回调域名(请确保回调域名已备案且在支付宝应用添加了白名单)"
><el-input v-model="epay.domain"
/></el-form-item>
<el-form-item>
<el-button type="primary" @click="save('epay')">保存</el-button>
<el-button @click="test('epay')">测试</el-button>
</el-form-item>
<el-form-item label="启用该支付通道"><el-switch v-model="epay.enabled" /></el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
<div class="flex justify-center mt-6">
<el-button type="primary" @click="save" :loading="loading">提交保存</el-button>
</div>
</div>
</template>
@@ -159,23 +167,26 @@ onMounted(() => {
})
const save = () => {
loading.value = true
const payload = { alipay: alipay.value, wxpay: wxpay.value, epay: epay.value }
httpPost('/api/admin/config/update/payment', payload)
.then(() => ElMessage.success('保存成功'))
.catch((e) => ElMessage.error(e.message))
.finally(() => (loading.value = false))
}
</script>
<style lang="scss" scoped>
.container {
form {
padding: 20px;
}
<style lang="scss">
.settings {
a {
color: #409eff;
&:hover {
text-decoration: underline;
}
}
.el-form-item__label {
font-weight: 700;
}
}
</style>

View File

@@ -1,65 +1,102 @@
<template>
<div class="form" v-loading="loading">
<el-form label-width="140px">
<el-form-item label="存储引擎">
<el-select v-model="active" style="width: 280px">
<el-option label="本地" value="local" />
<el-option label="MinIO" value="minio" />
<el-option label="七牛云" value="qiniu" />
<el-option label="阿里云OSS" value="aliyun" />
</el-select>
</el-form-item>
<template v-if="active === 'local'">
<el-form :model="local" label-width="140px">
<el-form-item label="BasePath"><el-input v-model="local.BasePath" /></el-form-item>
<el-form-item label="BaseURL"><el-input v-model="local.BaseURL" /></el-form-item>
<div class="settings container p-5">
<el-tabs v-model="activeTab" type="border-card">
<el-tab-pane label="本地" name="local">
<el-form :model="local" label-position="top">
<el-form-item>
<label class="form-label"
>文件存储根目录
<el-tooltip placement="top">
<template #content>
可以是绝对路径/data/static/upload<br />也可以是相对路径./static/upload
</template>
<i class="iconfont icon-info"></i>
</el-tooltip>
</label>
<el-input
v-model="local.base_path"
placeholder="请输入文件存储根目录,如:./static/upload"
/>
</el-form-item>
<el-form-item>
<label class="form-label"
>文件访问根 URL
<el-tooltip placement="top">
<template #content>
可以是绝对路径https://oss.geekai.me/static/upload
<br />也可以是相对路径/static/upload
</template>
<i class="iconfont icon-info"></i>
</el-tooltip>
</label>
<el-input
v-model="local.base_url"
placeholder="请输入文件存储URL/static/upload"
/>
</el-form-item>
</el-form>
</template>
</el-tab-pane>
<template v-else-if="active === 'minio'">
<el-form :model="minio" label-width="140px">
<el-form-item label="Endpoint"><el-input v-model="minio.Endpoint" /></el-form-item>
<el-form-item label="AccessKey"><el-input v-model="minio.AccessKey" /></el-form-item>
<el-tab-pane label="MinIO" name="minio">
<div class="rounded-md bg-blue-100 p-3 text-gray-500 border-blue-500 border-2 text-base">
如果你不知道怎么获取这些配置信息请参考文档
<a
href="https://docs.geekai.me/plus/config/oss.html#%E6%90%AD%E5%BB%BA-minio-%E5%AD%98%E5%82%A8%E6%9C%8D%E5%8A%A1"
target="_blank"
>Minio 配置</a
>
</div>
<el-form :model="minio" class="mt-4" label-position="top">
<el-form-item label="Endpoint"><el-input v-model="minio.endpoint" /></el-form-item>
<el-form-item label="AccessKey"><el-input v-model="minio.access_key" /></el-form-item>
<el-form-item label="AccessSecret"
><el-input v-model="minio.AccessSecret"
><el-input v-model="minio.access_secret"
/></el-form-item>
<el-form-item label="Bucket"><el-input v-model="minio.Bucket" /></el-form-item>
<el-form-item label="UseSSL"><el-switch v-model="minio.UseSSL" /></el-form-item>
<el-form-item label="Domain"><el-input v-model="minio.Domain" /></el-form-item>
<el-form-item label="Bucket"><el-input v-model="minio.bucket" /></el-form-item>
<el-form-item label="UseSSL"><el-switch v-model="minio.use_ssl" /></el-form-item>
<el-form-item label="Domain"><el-input v-model="minio.domain" /></el-form-item>
</el-form>
</template>
</el-tab-pane>
<template v-else-if="active === 'qiniu'">
<el-form :model="qiniu" label-width="140px">
<el-form-item label="Zone"><el-input v-model="qiniu.Zone" /></el-form-item>
<el-form-item label="AccessKey"><el-input v-model="qiniu.AccessKey" /></el-form-item>
<el-tab-pane label="七牛云" name="qiniu">
<el-form :model="qiniu" class="mt-4" label-position="top">
<el-form-item label="Zone"><el-input v-model="qiniu.zone" /></el-form-item>
<el-form-item label="AccessKey"><el-input v-model="qiniu.access_key" /></el-form-item>
<el-form-item label="AccessSecret"
><el-input v-model="qiniu.AccessSecret"
><el-input v-model="qiniu.access_secret"
/></el-form-item>
<el-form-item label="Bucket"><el-input v-model="qiniu.Bucket" /></el-form-item>
<el-form-item label="Domain"><el-input v-model="qiniu.Domain" /></el-form-item>
<el-form-item label="Bucket"><el-input v-model="qiniu.bucket" /></el-form-item>
<el-form-item label="Domain"><el-input v-model="qiniu.domain" /></el-form-item>
</el-form>
</template>
</el-tab-pane>
<template v-else>
<el-form :model="aliyun" label-width="140px">
<el-form-item label="Endpoint"><el-input v-model="aliyun.Endpoint" /></el-form-item>
<el-form-item label="AccessKey"><el-input v-model="aliyun.AccessKey" /></el-form-item>
<el-tab-pane label="阿里云OSS" name="aliyun">
<el-form :model="aliyun" class="mt-4" label-position="top">
<el-form-item label="Endpoint"><el-input v-model="aliyun.endpoint" /></el-form-item>
<el-form-item label="AccessKey"><el-input v-model="aliyun.access_key" /></el-form-item>
<el-form-item label="AccessSecret"
><el-input v-model="aliyun.AccessSecret"
><el-input v-model="aliyun.access_secret"
/></el-form-item>
<el-form-item label="Bucket"><el-input v-model="aliyun.Bucket" /></el-form-item>
<el-form-item label="SubDir"><el-input v-model="aliyun.SubDir" /></el-form-item>
<el-form-item label="Domain"><el-input v-model="aliyun.Domain" /></el-form-item>
<el-form-item label="Bucket"><el-input v-model="aliyun.bucket" /></el-form-item>
<el-form-item label="Domain"><el-input v-model="aliyun.domain" /></el-form-item>
</el-form>
</template>
</el-tab-pane>
</el-tabs>
<el-form-item>
<el-button type="primary" @click="save">保存</el-button>
<el-button @click="test">连接测试</el-button>
</el-form-item>
</el-form>
<div class="mt-3">
<label class="form-label mr-2">存储引擎</label>
<el-radio-group v-model="active" size="large">
<el-radio value="local" border>本地存储</el-radio>
<el-radio value="aliyun" border>阿里云</el-radio>
<el-radio value="qiniu" border>七牛云</el-radio>
<el-radio value="minio" border>Minio</el-radio>
</el-radio-group>
</div>
<div class="flex justify-center mt-6">
<el-button type="primary" @click="save" :loading="loading">提交保存</el-button>
<el-button class="ml-3" @click="test">连接测试</el-button>
</div>
</div>
</template>
@@ -69,44 +106,45 @@ import { ElMessage } from 'element-plus'
import { onMounted, ref } from 'vue'
const loading = ref(true)
const activeTab = ref('local')
const active = ref('local')
const local = ref({ BasePath: '', BaseURL: '' })
const local = ref({ base_path: '', base_url: '' })
const minio = ref({
Endpoint: '',
AccessKey: '',
AccessSecret: '',
Bucket: '',
SubDir: '',
UseSSL: false,
Domain: '',
endpoint: '',
access_key: '',
access_secret: '',
bucket: '',
use_ssl: false,
domain: '',
})
const qiniu = ref({
Zone: 'z2',
AccessKey: '',
AccessSecret: '',
Bucket: '',
SubDir: '',
Domain: '',
zone: 'z2',
access_key: '',
access_secret: '',
bucket: '',
domain: '',
})
const aliyun = ref({
Endpoint: '',
AccessKey: '',
AccessSecret: '',
Bucket: '',
SubDir: '',
Domain: '',
endpoint: '',
access_key: '',
access_secret: '',
bucket: '',
domain: '',
})
onMounted(() => {
httpGet('/api/admin/config/get?key=oss')
.then((res) => {
const data = res.data || {}
const Active = data.Active || data.active || 'local'
active.value = String(Active).toLowerCase()
local.value = data.Local || data.local || local.value
minio.value = data.Minio || data.minio || minio.value
qiniu.value = data.QiNiu || data.qiniu || qiniu.value
aliyun.value = data.AliYun || data.aliyun || aliyun.value
active.value = data.active.toLowerCase() || active.value
local.value = data.local || local.value
minio.value = data.minio || minio.value
qiniu.value = data.qiniu || qiniu.value
aliyun.value = data.aliyun || aliyun.value
minio.value.bucket = minio.value.bucket || 'geekai'
qiniu.value.bucket = qiniu.value.bucket || 'geekai'
aliyun.value.bucket = aliyun.value.bucket || 'geekai'
})
.catch(() => {})
.finally(() => (loading.value = false))
@@ -129,8 +167,10 @@ const test = () => {
}
</script>
<style scoped>
.form {
padding: 10px 20px 40px 20px;
<style lang="scss">
.settings {
.el-form-item__label {
font-weight: 700;
}
}
</style>