This commit is contained in:
wangchen147 2023-12-28 17:55:37 +08:00
parent 83169f4f56
commit 772b461466
27610 changed files with 8045 additions and 2929516 deletions

View File

@ -67,6 +67,8 @@ declare module '@vue/runtime-core' {
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElTimeline: typeof import('element-plus/es')['ElTimeline']
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']

8313
admin/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,7 @@
"vue": "3.2.45",
"vue-i18n": "9.2.2",
"vue-router": "4.1.6",
"vue-web-terminal": "3.1.7",
"vue-web-terminal": "3.2.2",
"vue3-video-play": "1.3.1-beta.6",
"vue-jsonp": "2.0.0",
"lodash-es": "4.17.21",
@ -55,4 +55,4 @@
"vite": "4.1.0",
"vue-tsc": "1.0.24"
}
}
}

View File

@ -0,0 +1,38 @@
import request from '@/utils/request'
/**
*
* @param addon
*/
export function cloudBuild() {
return request.post('niucloud/build')
}
/**
*
* @param addon
*/
export function getCloudBuildTask() {
return request.get('niucloud/build')
}
/**
*
*/
export function getCloudBuildLog() {
return request.get('niucloud/build/log')
}
/**
*
*/
export function clearCloudBuildTask() {
return request.post('niucloud/build/clear')
}
/**
*
*/
export function preBuildCheck() {
return request.get('niucloud/build/check')
}

View File

@ -57,3 +57,11 @@ export function downloadVersion(params: Record<string, any>) {
export function getFrameworkNewVersion() {
return request.get(`niucloud/framework/newversion`)
}
/**
*
* @returns
*/
export function getFrameworkVersionList() {
return request.get(`niucloud/framework/version/list`)
}

View File

@ -0,0 +1,47 @@
import request from '@/utils/request'
/**
*
* @param addon
*/
export function getUpgradeContent(addon: string = '') {
return request.get(addon ? `upgrade/${addon}` : 'upgrade')
}
/**
*
* @param addon
*/
export function getUpgradeTask() {
return request.get('upgrade/task')
}
/**
*
* @param addon
*/
export function upgradeAddon(addon: string = '') {
return request.post(addon ? `upgrade/${addon}` : 'upgrade')
}
/**
*
*/
export function executeUpgrade() {
return request.post('upgrade/execute')
}
/**
*
*/
export function preUpgradeCheck(addon: string = '') {
return request.get(addon ? `upgrade/check/${addon}` : 'upgrade/check')
}
/**
*
*/
export function clearUpgradeTask() {
return request.post('upgrade/clear')
}

View File

@ -0,0 +1,268 @@
<template>
<el-dialog v-model="showDialog" :title="t('cloudbuild.title')" width="850px" :close-on-click-modal="false"
:close-on-press-escape="false" :before-close="dialogClose">
<div v-show="active == 'build'" class="h-[60vh]" v-loading="loading">
<div class="h-[60vh] flex flex-col" v-if="cloudBuildCheck && !cloudBuildTask">
<el-scrollbar>
<div class="bg-[#fff] my-3" v-if="cloudBuildCheck.dir">
<p class="pt-[20px] pl-[20px] ">{{ t('cloudbuild.dirPermission') }}</p>
<div class="px-[20px] pt-[10px] text-[14px] el-table">
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
<el-col :span="12">
<span>{{ t('cloudbuild.path') }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('cloudbuild.demand') }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('status') }}</span>
</el-col>
</el-row>
<el-row class="pb-[10px] items pl-[15px]"
v-for="item in cloudBuildCheck.dir.is_readable">
<el-col :span="12">
<span>{{ item.dir }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('cloudbuild.readable') }}</span>
</el-col>
<el-col :span="6">
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
<span v-else>
<el-icon color="red">
<CloseBold />
</el-icon>
</span>
</el-col>
</el-row>
<el-row class="pb-[10px] items pl-[15px]"
v-for="item in cloudBuildCheck.dir.is_write">
<el-col :span="12">
<span>{{ item.dir }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('cloudbuild.write') }}</span>
</el-col>
<el-col :span="6">
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
<span v-else>
<el-icon color="red">
<CloseBold />
</el-icon>
</span>
</el-col>
</el-row>
</div>
</div>
</el-scrollbar>
</div>
<div class="h-[60vh]" v-show="cloudBuildTask">
<terminal ref="terminalRef" context="" :init-log="null" :show-header="false"
:show-log-time="true" @exec-cmd="onExecCmd"/>
</div>
</div>
<div v-show="active == 'complete'">
<div class="h-[60vh] flex flex-col">
<div class="flex-1 h-0">
<el-result icon="success" :title="t('cloudbuild.cloudbuildSuccess')"></el-result>
</div>
</div>
</div>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, h, watch } from 'vue'
import { t } from '@/lang'
import { getCloudBuildLog, getCloudBuildTask, cloudBuild, clearCloudBuildTask, preBuildCheck } from '@/app/api/cloud'
import { Terminal, TerminalFlash } from 'vue-web-terminal'
import 'vue-web-terminal/lib/theme/dark.css'
import { AnyObject } from "@/types/global"
import { ElNotification, ElMessageBox } from "element-plus"
import {preUpgradeCheck} from "@/app/api/upgrade";
const showDialog = ref<boolean>(false)
const cloudBuildTask = ref<null | AnyObject>(null)
const active = ref('build')
const cloudBuildCheck = ref<null | AnyObject>(null)
const loading = ref(false)
const terminalRef = ref(null)
const emits = defineEmits(['complete'])
let cloudBuildLog = []
/**
* 查询升级任务
*/
const getCloudBuildTaskFn = () => {
getCloudBuildTask().then(({ data }) => {
if (!data) return
cloudBuildTask.value = data
if (!showDialog.value) {
showElNotification()
}
}).catch()
}
getCloudBuildTaskFn()
const getCloudBuildLogFn = () => {
getCloudBuildLog().then(res => {
if (!res.data) {
if (showDialog.value && cloudBuildLog.length) {
active.value = 'complete'
terminalRef.value.execute('clear')
}
notificationEl && notificationEl.close()
cloudBuildTask.value = null
return
}
const data = res.data.data ?? []
let error = ''
if (data[0] && data[0].length && showDialog.value) {
if (cloudBuildLog.length == 0) {
terminalRef.value.execute('clear')
terminalRef.value.execute('开始编译')
}
data[0].forEach(item => {
if (!cloudBuildLog.includes(item.action)) {
terminalRef.value.pushMessage({ content: `正在执行:${item.action}` })
cloudBuildLog.push(item.action)
if (item.code == 0) {
error = item.msg
terminalRef.value.pushMessage({ content: item.msg, class: 'error' })
}
}
})
}
if (error) return
setTimeout(() => {
getCloudBuildLogFn()
}, 2000)
}).catch()
}
let notificationEl : any = null
/**
* 升级中任务提示
*/
const showElNotification = () => {
notificationEl = ElNotification.success({
title: t('warning'),
dangerouslyUseHTMLString: true,
message: h('div', {}, [
t('cloudbuild.executingTips'),
h('span', { class: 'text-primary cursor-pointer', onClick: elNotificationClick }, [t('cloudbuild.clickView')])
]),
duration: 0,
showClose: false
})
}
const elNotificationClick = () => {
showDialog.value = true
active.value = 'build'
getCloudBuildLogFn()
}
const open = async () => {
showDialog.value = true
loading.value = true
active.value = 'build'
if (cloudBuildTask.value) {
loading.value = false
getCloudBuildLogFn()
return
}
preBuildCheck().then(async ({ data }) => {
if (data.is_pass) {
cloudBuild().then(({ data }) => {
loading.value = false
cloudBuildTask.value = data
getCloudBuildLogFn()
}).catch(() => {
showDialog.value = false
})
} else {
loading.value = false
cloudBuildCheck.value = data
}
}).catch(() => {
showDialog.value = false
})
}
/**
* 升级进度动画
*/
let flashInterval = null
const terminalFlash = new TerminalFlash()
const onExecCmd = (key, command, success, failed, name) => {
if (command == '开始编译') {
success(terminalFlash)
const frames = makeIterator(['/', '——', '\\', '|'])
flashInterval = setInterval(() => {
terminalFlash.flush('> ' + frames.next().value)
}, 150)
}
}
const makeIterator = (array: string[]) => {
var nextIndex = 0
return {
next() {
if ((nextIndex + 1) == array.length) {
nextIndex = 0
}
return { value: array[nextIndex++] }
}
}
}
const dialogClose = (done: () => {}) => {
if (active.value == 'cloudbuild' && cloudBuildTask.value) {
ElMessageBox.confirm(
t('cloudbuild.showDialogCloseTips'),
t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
done()
}).catch(() => { })
} else {
done()
}
}
watch(() => showDialog.value, () => {
if (!showDialog.value) {
cloudBuildLog = []
flashInterval && clearInterval(flashInterval)
clearCloudBuildTask()
}
})
defineExpose({
open,
cloudBuildTask
})
</script>
<style lang="scss" scoped>
.table-head-bg {
background-color: var(--el-table-header-bg-color);
}
</style>

View File

@ -0,0 +1,319 @@
<template>
<el-dialog v-model="showDialog" :title="t('upgrade.title')" width="850px" :close-on-click-modal="false"
:close-on-press-escape="false" :before-close="dialogClose">
<div v-show="active == 'content'">
<div class="h-[60vh] flex flex-col" v-if="upgradeContent">
<div class="text-lg">
本次升级将从<span class="font-bold">{{ upgradeContent.version }}</span>升级到<span class="font-bold">{{ upgradeContent.upgrade_version }}</span>版本
</div>
<div class="mt-[10px]" v-if="upgradeContent.upgrade_version != upgradeContent.last_version">
<el-alert type="info" show-icon >
<template #title>
当前最新版本为{{ upgradeContent.last_version }}您的服务已于{{ upgradeContent.expire_time }}到期如需升级到最新版可在<a class="text-primary" href="https://www.niucloud.com" target="_blank">niucloud-admin官网</a>购买相关服务后再进行升级
</template>
</el-alert>
</div>
<el-scrollbar class="flex-1 h-0 mt-[20px]">
<div class="mt-[20px]" v-for="(item, index) in upgradeContent.version_list" :key="index">
<div class="font-bold text-lg">{{ item.version_no }}</div>
<div class="mt-[5px]">{{ item.release_time }}</div>
<div class="mt-[10px] p-[10px] rounded bg-[#f4f4f5]" v-if="item.upgrade_log" v-html="item.upgrade_log"></div>
</div>
</el-scrollbar>
</div>
<div class="flex justify-end">
<el-button type="primary" @click="handleUpgrade" :loading="uploading">{{ t('upgrade.upgradeButton') }}</el-button>
</div>
</div>
<div v-show="active == 'upgrade'">
<div class="h-[60vh] flex flex-col" v-if="upgradeCheck && !upgradeTask">
<el-scrollbar>
<div class="bg-[#fff] my-3" v-if="upgradeCheck.dir">
<p class="pt-[20px] pl-[20px] ">{{ t('upgrade.dirPermission') }}</p>
<div class="px-[20px] pt-[10px] text-[14px] el-table">
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
<el-col :span="12">
<span>{{ t('upgrade.path') }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('upgrade.demand') }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('status') }}</span>
</el-col>
</el-row>
<el-row class="pb-[10px] items pl-[15px]"
v-for="item in upgradeCheck.dir.is_readable">
<el-col :span="12">
<span>{{ item.dir }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('upgrade.readable') }}</span>
</el-col>
<el-col :span="6">
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
<span v-else>
<el-icon color="red">
<CloseBold />
</el-icon>
</span>
</el-col>
</el-row>
<el-row class="pb-[10px] items pl-[15px]"
v-for="item in upgradeCheck.dir.is_write">
<el-col :span="12">
<span>{{ item.dir }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('upgrade.write') }}</span>
</el-col>
<el-col :span="6">
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
<span v-else>
<el-icon color="red">
<CloseBold />
</el-icon>
</span>
</el-col>
</el-row>
</div>
</div>
</el-scrollbar>
</div>
<div class="h-[60vh]" v-show="upgradeTask">
<terminal ref="terminalRef" :context="upgradeTask ? upgradeTask.upgrade.app_key : ''" :init-log="null" :show-header="false"
:show-log-time="true" @exec-cmd="onExecCmd"/>
</div>
</div>
<div v-show="active == 'complete'">
<div class="h-[60vh] flex flex-col">
<div class="flex-1 h-0">
<el-result icon="success" :title="t('upgrade.upgradeSuccess')"></el-result>
<el-alert :title="t('upgrade.upgradeCompleteTips')" type="error" :closable="false" />
</div>
<div class="flex justify-end">
<el-button type="default" @click="showDialog = false">{{ t('upgrade.localBuild') }}</el-button>
<el-button type="primary" @click="handleCloudBuild">{{ t('upgrade.cloudBuild') }}</el-button>
</div>
</div>
</div>
</el-dialog>
<cloud-build ref="cloudBuildRef" />
</template>
<script lang="ts" setup>
import {ref, h, watch} from 'vue'
import { t } from '@/lang'
import { getUpgradeContent, getUpgradeTask, upgradeAddon, executeUpgrade, preUpgradeCheck, clearUpgradeTask } from '@/app/api/upgrade'
import { Terminal, TerminalFlash } from 'vue-web-terminal'
import 'vue-web-terminal/lib/theme/dark.css'
import { AnyObject } from "@/types/global"
import CloudBuild from '@/app/components/cloud-build/index.vue'
import { ElNotification, ElMessage, ElMessageBox } from "element-plus"
const showDialog = ref<boolean>(false)
const upgradeContent = ref<null | AnyObject>(null)
const upgradeTask = ref<null | AnyObject>(null)
const active = ref('content')
const upgradeCheck = ref<null | AnyObject>(null)
const uploading = ref(false)
const terminalRef = ref(null)
const emits = defineEmits(['complete'])
const cloudBuildRef = ref(null)
let upgradeLog = []
/**
* 查询升级任务
*/
const getUpgradeTaskFn = () => {
getUpgradeTask().then(({ data }) => {
if (!data) return
//
if (!showDialog.value) {
showElNotification()
return
}
if (!upgradeTask.value) {
terminalRef.value.execute('clear')
terminalRef.value.execute('开始升级')
}
data.log.forEach(item => {
if (!upgradeLog.includes(item)) {
terminalRef.value.pushMessage({content: `正在执行:${item}`})
upgradeLog.push(item)
}
})
//
if (data.error) {
upgradeTask.value = data
ElMessage({ message: '升级失败', type: 'error' })
terminalRef.value.pushMessage({ content: data.error, class: 'error' })
return
}
//
if (data.step == 'upgradeComplete') {
active.value = 'complete'
notificationEl && notificationEl.close()
emits('complete')
return
}
upgradeTask.value = data
executeUpgradeFn()
}).catch()
}
getUpgradeTaskFn()
const executeUpgradeFn = () => {
executeUpgrade().then(() => {
getUpgradeTaskFn()
}).catch()
}
let notificationEl : any = null
/**
* 升级中任务提示
*/
const showElNotification = () => {
notificationEl = ElNotification.success({
title: t('warning'),
dangerouslyUseHTMLString: true,
message: h('div', {}, [
t('upgrade.upgradingTips'),
h('span', { class: 'text-primary cursor-pointer', onClick: elNotificationClick }, [t('upgrade.clickView')])
]),
duration: 0,
showClose: false
})
}
const elNotificationClick = () => {
showDialog.value = true
active.value = 'upgrade'
getUpgradeTaskFn()
notificationEl && notificationEl.close()
}
/**
* 执行升级
*/
const handleUpgrade = async () => {
if (uploading.value) return
uploading.value = true
const appKey = upgradeContent.value?.app.app_key != 'niucloud-admin' ? upgradeContent.value?.app.app_key : ''
await preUpgradeCheck(appKey)
.then(async ({ data }) => {
if (data.is_pass) {
await upgradeAddon(appKey).then(() => {
getUpgradeTaskFn()
}).catch(() => {
uploading.value = false
})
} else {
upgradeCheck.value = data
}
})
.catch()
if (uploading.value) active.value = 'upgrade'
}
const open = (addonKey: string = '') => {
if (upgradeTask.value) {
ElMessage({ message: '已有正在执行中的升级任务', type: 'error' })
showDialog.value = true
return
}
getUpgradeContent(addonKey).then(({ data }) => {
upgradeContent.value = data
if (data.version == data.last_version) {
ElMessage({ message: '已经是最新版本了', type: 'error' })
return
}
showDialog.value = true
}).catch()
}
/**
* 升级进度动画
*/
let flashInterval = null
const terminalFlash = new TerminalFlash()
const onExecCmd = (key, command, success, failed, name) => {
if (command == '开始升级') {
success(terminalFlash)
const frames = makeIterator(['/', '——', '\\', '|'])
flashInterval = setInterval(() => {
terminalFlash.flush('> ' + frames.next().value)
}, 150)
}
}
const makeIterator = (array: string[]) => {
var nextIndex = 0
return {
next() {
if ((nextIndex + 1) == array.length) {
nextIndex = 0
}
return { value: array[nextIndex++] }
}
}
}
const dialogClose = (done: () => {}) => {
if (active.value == 'upgrade' && upgradeTask.value && !upgradeTask.value.error) {
ElMessageBox.confirm(
t('upgrade.showDialogCloseTips'),
t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
done()
}).catch(() => { })
} else {
done()
}
}
watch(() => showDialog.value, () => {
if (!showDialog.value) {
clearUpgradeTaskFn()
}
})
const clearUpgradeTaskFn = () => {
upgradeTask.value = null
upgradeLog = []
flashInterval && clearInterval(flashInterval)
clearUpgradeTask().then(() => {}).catch()
}
/**
* 云编译
*/
const handleCloudBuild = () => {
showDialog.value = false
cloudBuildRef.value?.open()
}
defineExpose({
open
})
</script>
<style lang="scss" scoped>
.table-head-bg {
background-color: var(--el-table-header-bg-color);
}
</style>

View File

@ -13,5 +13,6 @@
"authSecretPlaceholder": "请输入授权秘钥",
"updateCode": "重新绑定",
"notHaveAuth": "还没有授权?去购买",
"authInfoTips": "授权码和授权秘钥可在Niucloud官网我的授权 授权详情中查看"
}
"authInfoTips": "授权码和授权秘钥可在Niucloud官网我的授权 授权详情中查看",
"cloudBuildTips": "是否要进行云编译该操作可能会影响到正在访问的客户是否要继续操作?"
}

View File

@ -46,6 +46,6 @@
"officialAccountDesc": "微信扫码关注",
"WeCom": "客服二维码",
"WeComDesc": "扫码联系客服",
"tel": "服务热线:"
}
"tel": "服务热线:",
"newVersion": "最新版本"
}

View File

@ -69,5 +69,7 @@
"tipText":"标识指开发应用或插件的文件夹名称",
"uninstallTips": "卸载插件将会移除admin web uni-app目录下该插件的内容是否要继续进行卸载",
"upgrade": "升级",
"newVersion": "最新版本"
"newVersion": "最新版本",
"cloudBuild": "云编译",
"cloudBuildTips": "是否要进行云编译该操作可能会影响到正在访问的客户是否要继续操作?"
}

View File

@ -0,0 +1,4 @@
{
"clickTutorial": "查看教程",
"clickSecretKey": "获取密钥"
}

View File

@ -2,30 +2,35 @@
<div class="main main-container min-w-[1000px] min-h-[650px]" v-loading="loading">
<el-card class="box-card !border-none" shadow="never" v-if="!loading">
<div class="flex">
<div class="bg-[#F3F6FF] mr-[14px] w-[402px] pt-[30px] pl-[32px] pr-[46px] pb-[60px]">
<div class="bg-[#F3F6FF] mr-[14px] w-[402px] pt-[30px] pl-[32px] pr-[20px] pb-[60px] timeline-log-wrap">
<div class="flex items-center justify-between">
<span class="text-[20px] text-[#333333]">版本信息</span>
<el-button class="text-[#4C4C4C] w-[78px] h-[32px] !bg-transparent">检测更新</el-button>
<span class="text-[20px]">版本信息</span>
<div class="flex-1 w-0 flex justify-end">
<el-button class="text-[#4C4C4C] w-[78px] h-[32px] !bg-transparent" @click="getFrameworkVersionListFn" v-if="!newVersion || (newVersion && newVersion.version_no == versions)">检测更新</el-button>
<el-button class="text-[#4C4C4C] w-[78px] h-[32px]" type="primary" @click="handleUpgrade" v-else>一键升级</el-button>
<el-button class="text-[#4C4C4C] w-[78px] h-[32px]" type="primary" @click="handleCloudBuild">云编译</el-button>
</div>
</div>
<div class="mt-[30px] flex items-center text-[14px] text-[#797979]">
<span>当前版本</span>
<span class="text-[26px] ml-[15px] mr-[10px] text-[#656668]">{{versions}}</span>
<em class="text-[12px]">(当前已是最新版本)</em>
<em class="text-[12px]" v-if="!newVersion || (newVersion && newVersion.version_no == versions)">(当前已是最新版本)</em>
<em class="text-[12px] text-[red]" v-else>(最新版本{{ newVersion.version_no }})</em>
</div>
</div>
<div class="flex-1 flex justify-between items-center bg-[#F3F6FF] pt-[34px] pl-[30px] pr-[60px] pb-[62px]">
<div class="flex-1 flex justify-between items-center bg-[#F3F6FF] pt-[34px] pl-[30px] pr-[60px] pb-[62px] timeline-log-wrap">
<div class="flex flex-col">
<div class="flex flex-wrap items-center">
<p class="text-[20px] text-[#333] mr-[20px]">授权信息</p>
<p class="text-[20px] mr-[20px]">授权信息</p>
<span class="text-[14px] text-[#666]">{{ authinfo.company_name || '--' }}</span>
</div>
<div class="mt-[46px] ml-[40px] flex flex-wrap">
<span class="text-[14px] text-[#797979] mr-[84px]">授权域名<em
class="ml-[12px] text-[12px] text-[#222222]">{{ authinfo.site_address || '--'
<span class="text-[14px] mr-[84px]">授权域名<em
class="ml-[12px] text-[12px]">{{ authinfo.site_address || '--'
}}</em></span>
<span class="text-[14px] flex items-center text-[#797979]">
<span class="text-[14px] flex items-center">
<span>授权码</span>
<em class="ml-[12px] mr-[10px] text-[12px] text-[#222222]">{{ authinfo.auth_code ? (isCheck
<em class="ml-[12px] mr-[10px] text-[12px]">{{ authinfo.auth_code ? (isCheck
?
authinfo.auth_code : hideAuthCode(authinfo.auth_code)) : '--' }}</em>
<el-icon v-if="!isCheck" @click="isCheck = !isCheck" class="text-[12px] cursor-pointer">
@ -35,7 +40,7 @@
<Hide />
</el-icon>
</span>
</div>
</div>
</div>
<div class="flex flex-1 flex-wrap justify-end relative">
<el-button class="w-[154px] !h-[48px] mt-[8px]" type="primary"
@ -89,23 +94,53 @@
</div>
</div>
</el-card>
<el-card class="box-card !border-none " shadow="never" v-if="!loading">
<div class="text-[20px] mb-[20px]">历史版本</div>
<el-timeline>
<el-timeline-item :timestamp="item.release_time + ' 版本:' + item.version_no" v-for="item in frameworkVersionList" type="primary" :hollow="true" placement="top">
<div class="mt-[10px] p-[20px] bg-overlay rounded-md timeline-log-wrap" v-if="item.upgrade_log">
<div v-html="item.upgrade_log"></div>
</div>
</el-timeline-item>
</el-timeline>
</el-card>
<upgrade ref="upgradeRef" />
<cloud-build ref="cloudBuildRef" />
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { reactive, ref, computed } from 'vue'
import { t } from '@/lang'
import { getVersions } from '@/app/api/auth'
import { getAuthinfo, setAuthinfo, getAdminAuthinfo } from '@/app/api/module'
import { FormInstance, FormRules } from 'element-plus'
import { getAuthinfo, setAuthinfo, getFrameworkVersionList } from '@/app/api/module'
import { ElMessageBox, FormInstance, FormRules } from 'element-plus'
import { useRoute } from 'vue-router'
import Upgrade from '@/app/components/upgrade/index.vue'
import CloudBuild from '@/app/components/cloud-build/index.vue'
const route = useRoute()
const pageName = route.meta.title
const upgradeRef = ref(null)
const cloudBuildRef = ref(null)
const getAuthCodeDialog = ref(null)
const authCodeApproveDialog = ref(false)
const isCheck = ref(false)
const frameworkVersionList = ref([])
const getFrameworkVersionListFn = () => {
getFrameworkVersionList().then(({ data }) => {
frameworkVersionList.value = data
})
}
getFrameworkVersionListFn()
const newVersion = computed(() => {
return frameworkVersionList.value.length ? frameworkVersionList.value[0] : null
})
const hideAuthCode = (res) => {
const authCode = JSON.parse(JSON.stringify(res))
@ -177,12 +212,43 @@ const market = () => {
}
const versions = ref('')
const getVersionsInfo = () =>{
getVersions().then(res =>{
versions.value = res.data.version.version
})
const getVersionsInfo = () => {
getVersions().then(res => {
versions.value = res.data.version.version
})
}
getVersionsInfo()
/**
* 升级
*/
const handleUpgrade = () => {
if (!authinfo.value) {
authCodeApproveFn()
return
}
getVersionsInfo()
upgradeRef.value?.open()
}
const handleCloudBuild = () => {
if (!authinfo.value) {
authCodeApproveFn()
return
}
if (cloudBuildRef.value.cloudBuildTask) {
cloudBuildRef.value?.open()
return
}
ElMessageBox.confirm(t('cloudBuildTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
cloudBuildRef.value?.open()
})
}
</script>
<style lang="scss" scoped>
@ -200,6 +266,15 @@ const versions = ref('')
em {
font-style: normal
}
.timeline-log-wrap {
background: #F5F7F9;
}
html.dark {
.timeline-log-wrap {
background: var(--el-bg-color);
color: var(--el-text-color-regular);
}
}
</style>
<style>
.auth-code-dialog .el-overlay {

View File

@ -12,6 +12,9 @@
<div class="flex items-center mt-[12px]">
<img class="w-[12px] h-[12px]" src="@/app/assets/images/versions.png" />
<span class="ml-[7px] text-[16px] font-600 text-[#424242] leading-[16px] font-[600]">{{ statInfo.version.version }}</span>
<div class="ml-[10px] cursor-pointer" v-if="newVersion" @click="toUpgrade">
<el-tag type="danger" size="small">{{ t('newVersion') }}{{ newVersion.last_version }}</el-tag>
</div>
<el-link class="text-color ml-[30px] text-[14px] leading-[20px]" href="https://www.niucloud.com/"
target="_blank" :underline="false">{{ t("officialWbsite") }}</el-link>
<el-link class="ml-[12px] text-color text-[14px] leading-[20px]"
@ -170,6 +173,8 @@ import { img } from "@/utils/common";
import { useRouter } from "vue-router";
import { useRoute } from "vue-router";
import useStyleStore from '@/stores/modules/style'
import { getFrameworkNewVersion } from '@/app/api/module'
const loading = ref(true);
const visitStat = ref<any>(null);
const memberStat = ref<any>(null);
@ -179,6 +184,11 @@ const siteStat = ref<any>(null);
const systemStore = useSystemStore();
const styleStore = useStyleStore()
const newVersion = ref(null)
getFrameworkNewVersion().then(({ data }) => {
newVersion.value = data
}).catch()
let statInfo = ref({
today_data: {},
@ -300,6 +310,10 @@ const nowTime = () => {
time.value = year + '-' + month + '-' + day + ' ' + hh + ':' + mm + ':' + ss
}
nowTime()
const toUpgrade = () => {
router.push({ path: '/tools/authorize' })
}
</script>
<style lang="scss" scoped>

View File

@ -1,6 +1,6 @@
<template>
<div class="pt-[59px] px-[20px] app-store" v-loading="authLoading">
<div v-if="info[activeName] && !loading && !authLoading">
<div>
<div class="flex justify-between items-center h-[32px] mb-4">
<span class="text-[20px] text-[#222]">{{ t('localAppText') }}</span>
<el-input class="!w-[250px]" :placeholder="t('search')" v-model="search_name" @keyup.enter="query">
@ -40,9 +40,13 @@
</template>
</el-image>
<div
class="flex flex-col justify-center h-[54px] pl-[20px] text-[#222] font-500 text-[13px]">
class="flex flex-col justify-center pl-[20px] text-[#222] font-500 text-[13px]">
<div class="w-[236px] truncate leading-[18px]">{{ row.title }}</div>
<div class="w-[236px] truncate leading-[18px] mt-[6px]">{{ row.version }}</div>
<div class="w-[236px] truncate leading-[18px] mt-[6px]" v-if="row.install_info && Object.keys(row.install_info)?.length">{{ row.install_info.version }}</div>
<div class="w-[236px] truncate leading-[18px] mt-[6px]" v-else>{{ row.version }}</div>
<div class="mt-[3px]" v-if="row.install_info && Object.keys(row.install_info)?.length && row.install_info.version != row.version">
<el-tag type="danger" size="small">{{ t('newVersion') }}{{ row.version }}</el-tag>
</div>
</div>
</div>
</template>
@ -82,6 +86,8 @@
<template #default="{ row }">
<el-button class="!text-[13px]" type="primary" link @click="getAddonDetialFn(row)">{{
t('detail') }}</el-button>
<el-button class="!text-[13px]" v-if="row.install_info && Object.keys(row.install_info)?.length && row.install_info.version != row.version"
type="primary" link @click="upgradeAddonFn(row.key)">{{ t('upgrade') }}</el-button>
<el-button class="!text-[13px]"
v-if="row.install_info && Object.keys(row.install_info)?.length && activeName != 'all'"
type="primary" link @click="uninstallAddonFn(row.key)">{{ t('unload') }}</el-button>
@ -95,6 +101,8 @@
<span v-else-if="row.is_download && row.install_info <= 0">{{ t('installDown') }}</span>
<span v-else>{{ t('down') }}</span>
</el-button>
<el-button class="!text-[13px]" type="primary" v-if="row.install_info && Object.keys(row.install_info)?.length" link @click="handleCloudBuild">{{
t('cloudBuild') }}</el-button>
</template>
</el-table-column>
</el-table>
@ -295,7 +303,7 @@
</div>
<div v-show="installStep == 2" class="h-[50vh] mt-[20px]">
<terminal ref="terminalRef" :context="currAddon" :init-log="null" :show-header="false"
:show-log-time="true" />
:show-log-time="true" @exec-cmd="onExecCmd"/>
</div>
<div v-show="installStep == 3" class="h-[50vh] mt-[20px] flex flex-col">
<el-result icon="success" :title="t('addonInstallSuccess')"></el-result>
@ -375,6 +383,9 @@
</el-dialog>
</div>
</div>
<upgrade ref="upgradeRef" @complete="localListFn"/>
<cloud-build ref="cloudBuildRef" />
</template>
<script lang="ts" setup>
@ -384,16 +395,20 @@ import { getAddonLocal, uninstallAddon, installAddon, preInstallCheck, cloudInst
import { downloadVersion, getAuthinfo, setAuthinfo } from '@/app/api/module'
import { ElMessage, ElMessageBox, ElNotification, FormInstance, FormRules } from 'element-plus'
import { img } from '@/utils/common'
import { Terminal, api as terminalApi } from 'vue-web-terminal'
import 'vue-web-terminal/lib/theme/dark.css'
import { Terminal, TerminalFlash } from 'vue-web-terminal'
import { findFirstValidRoute } from '@/router/routers'
import storage from '@/utils/storage'
import { useRouter, useRoute } from 'vue-router'
import useUserStore from '@/stores/modules/user'
import Upgrade from '@/app/components/upgrade/index.vue'
import CloudBuild from '@/app/components/cloud-build/index.vue'
const router = useRouter()
const route = useRoute()
const activeName = ref(storage.get('storeActiveName') || 'installed')
const upgradeRef = ref(null)
const cloudBuildRef = ref(null)
const loading = ref<Boolean>(true)
const downloading = ref('')
const installAfterTips = ref<string[]>([])
@ -535,6 +550,30 @@ const installStep = ref(1)
//
const installCheckResult = ref({})
let flashInterval = null
const terminalFlash = new TerminalFlash()
const onExecCmd = (key, command, success, failed, name)=> {
if (command == '开始安装插件') {
success(terminalFlash)
const frames = makeIterator(['/', '——', '\\', '|'])
flashInterval = setInterval(() => {
terminalFlash.flush('> ' + frames.next().value)
}, 150)
}
}
function makeIterator(array: string[]) {
var nextIndex = 0
return {
next() {
if ((nextIndex + 1) == array.length) {
nextIndex = 0
}
return { value: array[nextIndex++] }
}
}
}
/**
* 安装
* @param key
@ -612,7 +651,6 @@ const handleInstall = () => {
installAddon({ addon: currAddon.value }).then(res => {
installStep.value = 3
localListFn()
userStore.getAppList()
localInstalling.value = false
if (res.data.length) installAfterTips.value = res.data
}).catch((res) => {
@ -637,7 +675,7 @@ const handleCloudInstall = () => {
cloudInstallAddon({ addon: currAddon.value }).then(res => {
installStep.value = 2
terminalRef.value.execute('clear')
terminalRef.value.pushMessage({ content: '开始安装插件', class: 'info' })
terminalRef.value.execute('开始安装插件')
getInstallTask()
cloudInstalling.value = false
}).catch((res) => {
@ -718,6 +756,37 @@ const uninstallAddonFn = (key: string) => {
}
}
/**
* 插件升级
* @param key
*/
const upgradeAddonFn = (key: string) => {
upgradeRef.value?.open(key)
}
/**
* 云编译
*/
const handleCloudBuild = () => {
if (!authCode.value) {
authElMessageBox()
return
}
if (cloudBuildRef.value.cloudBuildTask) {
cloudBuildRef.value?.open()
return
}
ElMessageBox.confirm(t('cloudBuildTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
cloudBuildRef.value?.open()
})
}
const handleUninstallAddon = (key: string) => {
preUninstallCheck(key).then(({ data }) => {
if (data.is_pass) {
@ -761,6 +830,8 @@ const installShowDialogClose = (done: () => {}) => {
activeNameTabFn('installed')
location.reload()
} else done()
flashInterval && clearInterval(flashInterval)
}
//

View File

@ -11,14 +11,13 @@
</el-form-item>
<el-form-item :label="t('cashWithdrawalAmount')" v-if="formData.is_open" prop="min">
<el-input v-model="formData.min" class="w-60" type="number" :placeholder="t('cashWithdrawalAmountPlaceholder')" />
<el-input v-model="formData.min" type="number" class="input-width" :placeholder="t('cashWithdrawalAmountPlaceholder')" />
</el-form-item>
<el-form-item :label="t('commissionRatio')" v-if="formData.is_open" prop="rate">
<el-input v-model="formData.rate" type="number" class="w-60" :placeholder="t('commissionRatioPlaceholder')" />
<el-input v-model="formData.rate" type="number" class="input-width" :placeholder="t('commissionRatioPlaceholder')" />
<span class="ml-2">%</span>
</el-form-item>
<el-form-item :label="t('audit')" v-if="formData.is_open" class="items-center">
<el-radio-group v-model="formData.is_auto_verify">
<el-radio label="0" size="large">{{t('manualAudit')}}</el-radio>

View File

@ -5,6 +5,8 @@
<h3 class="panel-title">{{ t('mapSetting') }}</h3>
<el-form-item :label="t('mapKey')" prop="site_name">
<el-input v-model="formData.key" class="input-width" clearable />
<span class="ml-2 cursor-pointer tutorial-btn" @click="tutorialFn">{{ t('clickTutorial') }}</span>
<span class="ml-2 cursor-pointer secret-btn" @click="secretlFn">{{ t('clickSecretKey') }}</span>
</el-form-item>
</el-card>
</el-form>
@ -46,6 +48,29 @@ const save = async (formEl: FormInstance | undefined) => {
})
}
</script>
/**
* 点击访问教程
*/
const tutorialFn = ()=>{
window.open('https://www.kancloud.cn/niucloud/niucloud-admin-develop/3214217');
}
<style lang="scss" scoped></style>
/**
* 点击访问腾讯地图
*/
const secretlFn = ()=>{
window.open('https://lbs.qq.com/dev/console/key/manage');
}
</script>
<style lang="scss" scoped>
.tutorial-btn{
color:var(--el-color-primary);
}
.secret-btn{
color:var(--el-color-primary);
}
</style>

View File

@ -126,5 +126,33 @@
"passwordPlaceholder": "请输入新密码",
"originalPasswordHint": "原始密码不能为空",
"newPasswordHint": "请输入确认密码",
"doubleCipherHint": "两次新密码不同"
"doubleCipherHint": "两次新密码不同",
"upgrade": {
"upgradeButton": "立即升级",
"title": "升级",
"upgradingTips": "有正在执行的升级任务,",
"clickView": "点击查看",
"dirPermission": "目录读写权限",
"path": "路径",
"demand": "要求",
"readable": "可读",
"write": "可写",
"upgradeSuccess": "升级成功",
"localBuild": "手动编译",
"cloudBuild": "云编译",
"showDialogCloseTips": "升级任务尚未完成,关闭将取消升级,是否要继续关闭?",
"upgradeCompleteTips": "升级完成后还需要编译admin wap web端可选择云编译或者是手动编译"
},
"cloudbuild": {
"title": "云编译",
"executingTips": "有正在执行的编译任务,",
"clickView": "点击查看",
"dirPermission": "目录读写权限",
"path": "路径",
"demand": "要求",
"readable": "可读",
"write": "可写",
"cloudbuildSuccess": "编译完成",
"showDialogCloseTips": "编译任务尚未完成,关闭将取消编译,是否要继续关闭?"
}
}

View File

@ -3,6 +3,7 @@
<!-- :class="['h-full px-[10px]',{'layout-header border-b border-color': !dark}]" -->
<div class="flex items-center text-[14px] leading-[1]">
<span class="iconfont icontuodong !text-[25px] cursor-pointer mr-[6px]" @click="toLink('/admin/index')"></span>
<span class="mx-2 text-[#4F5563] mx-[15px]">|</span>
<span class="cursor-pointer" @click="toLink('/admin/setting/website/system','setting_manage')">控制台</span>
<span class="mx-2 text-[#4F5563] mx-[15px]">|</span>
<span class="cursor-pointer" @click="toLink('/admin/site/list','site_manage')">站点</span>

View File

@ -1,3 +1,4 @@
{
"hello_world.pages.index": "hello_world index"
"pages.index": "hello_world index",
"pages.info": "hello_world info"
}

View File

@ -1,3 +1,4 @@
{
"hello_world.pages.index": "hello_world首页"
"pages.index": "hello_world首页",
"pages.info": "hello_world详情"
}

View File

@ -100,6 +100,7 @@ class Request extends \think\Request
{
if ($site_id > 0) {
static::$site_id = (int)$site_id;
return $site_id;
} else {
return static::$site_id ?? $this->defaultSiteId();
}
@ -114,6 +115,7 @@ class Request extends \think\Request
{
if (!empty($username)) {
static::$auth_info['username'] = $username;
return $username;
} else {
return static::$auth_info['username'] ?? '';
}
@ -128,6 +130,7 @@ class Request extends \think\Request
public function appType(string $app_type = ''){
if (!empty($app_type)) {
static::$auth_info['app_type'] = $app_type;
return $app_type;
} else {
return static::$auth_info['app_type'] ?? '';
}

View File

@ -0,0 +1,71 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的saas管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud-admin.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\adminapi\controller\addon;
use app\service\admin\upgrade\UpgradeService;
use core\base\BaseAdminController;
use think\Response;
class Upgrade extends BaseAdminController
{
/**
* 更新插件
* @param $app_key
* @return Response
*/
public function upgrade($addon = ''){
return success(data:(new UpgradeService())->upgrade($addon));
}
/**
* 执行升级
* @param $app_key
* @return Response
*/
public function execute($addon = ''){
return success(data:(new UpgradeService())->execute());
}
/**
* 获取升级内容
* @param $addon
* @return Response
*/
public function getUpgradeContent($addon = '') {
return success(data:(new UpgradeService())->getUpgradeContent($addon));
}
/**
* 获取正在进行的升级任务
* @return Response
*/
public function getUpgradeTask() {
return success(data:(new UpgradeService())->getUpgradeTask());
}
/**
* 升级前环境检测
* @param $addon
* @return Response
*/
public function upgradePreCheck($addon = '') {
return success(data:(new UpgradeService())->upgradePreCheck($addon));
}
/**
* 清除
* @return Response
*/
public function clearUpgradeTask() {
return success(data:(new UpgradeService())->clearUpgradeTask());
}
}

View File

@ -11,15 +11,9 @@
namespace app\adminapi\controller\login;
use addon\cms\app\model\article\CmsArticle;
use app\model\sys\SysUser;
use app\service\admin\auth\ConfigService;
use app\service\admin\auth\LoginService;
use app\service\admin\site\SiteService;
use app\service\core\upgrade\CoreBackupService;
use app\service\core\upgrade\CoreRestoreService;
use app\service\core\upgrade\CoreUpgradeService;
use app\service\core\weapp\CoreWeappCloudService;
use app\service\admin\upgrade\UpgradeService;
use core\base\BaseAdminController;
use think\Response;
@ -68,6 +62,13 @@ class Login extends BaseAdminController
}
public function test(){
(new CoreUpgradeService())->execute();
$code_dir = project_path() . 'upgrade/658a7ee7aec7d/download/code/0.0.6';
$file_path = project_path() . 'upgrade/658a7ee7aec7d/download/code/0.0.6.txt';
$change = array_filter(explode("\n", file_get_contents($file_path)));
foreach ($change as &$item) {
list($operation, $md5, $file) = $item = explode(' ', $item);
}
(new UpgradeService())->installDepend($code_dir, array_column($change, 2));
}
}

View File

@ -0,0 +1,58 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的saas管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud-admin.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\adminapi\controller\niucloud;
use app\service\core\niucloud\CoreCloudBuildService;
use core\base\BaseAdminController;
class Cloud extends BaseAdminController
{
/**
* 云编译
* @return \think\Response
*/
public function build() {
return success(data:(new CoreCloudBuildService())->cloudBuild());
}
/**
* 获取云编译日志
* @return \think\Response
*/
public function getBuildLog() {
return success(data:(new CoreCloudBuildService())->getBuildLog());
}
/**
* 获取云编译任务
* @return \think\Response
*/
public function getBuildTask() {
return success(data:(new CoreCloudBuildService())->getBuildTask());
}
/**
* 清除云编译任务
* @return \think\Response
*/
public function clearBuildTask() {
return success(data:(new CoreCloudBuildService())->clearTask());
}
/**
* 编译前环境检测
* @return \think\Response
*/
public function buildPreCheck() {
return success(data:(new CoreCloudBuildService())->buildPreCheck());
}
}

View File

@ -52,4 +52,13 @@ class Module extends BaseAdminController
public function getFrameworkLastVersion() {
return success(data:(new NiucloudService())->getFrameworkLastVersion());
}
/**
* 获取框架最新版本
* @return Response
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getFrameworkVersionList() {
return success(data:(new NiucloudService())->getFrameworkVersionList());
}
}

View File

@ -52,7 +52,6 @@ class Pay extends BaseAdminController
/**
* 审核拒绝
* @param string $out_trade_no
* @return void
*/
public function refuse(string $out_trade_no){
$reason = input('reason', '');

View File

@ -53,8 +53,6 @@ Route::group(function () {
// 取消安装任务
Route::put('addon/install/cancel/:addon', 'addon.Addon/cancleInstall');
Route::post('addon/upgrade/[:addon]', 'addon.Addon/upgrade');
/******************************************************************开发插件 *******************************************************/
//开发插件列表
Route::get('addon_develop', 'addon.AddonDevelop/lists');

View File

@ -39,7 +39,19 @@ Route::group('niucloud', function () {
Route::put('addon/status/:version_id', 'addon.Addon/setStatus');
// 获取框架最新版本
Route::get('framework/newversion', 'niucloud.Module/getFrameworkLastVersion');
// 获取框架版本更新记录
Route::get('framework/version/list', 'niucloud.Module/getFrameworkVersionList');
// 云编译
Route::post('build', 'niucloud.Cloud/build');
// 获取编译任务
Route::get('build', 'niucloud.Cloud/getBuildTask');
// 获取云编译
Route::get('build/log', 'niucloud.Cloud/getBuildLog');
// 清除编译任务
Route::post('build/clear', 'niucloud.Cloud/clearBuildTask');
// 编译前环境检测
Route::get('build/check', 'niucloud.Cloud/buildPreCheck');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,

View File

@ -0,0 +1,38 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的saas管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud-admin.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
use app\adminapi\middleware\AdminCheckRole;
use app\adminapi\middleware\AdminCheckToken;
use app\adminapi\middleware\AdminLog;
use think\facade\Route;
/**
* 路由
*/
Route::group('', function () {
// 获取正在进行的升级任务
Route::get('upgrade/task', 'addon.Upgrade/getUpgradeTask');
// 执行升级
Route::post('upgrade/execute', 'addon.Upgrade/execute');
// 清除升级任务
Route::post('upgrade/clear', 'addon.Upgrade/clearUpgradeTask');
// 升级环境检测
Route::get('upgrade/check/[:addon]', 'addon.Upgrade/upgradePreCheck');
// 升级
Route::post('upgrade/[:addon]', 'addon.Upgrade/upgrade');
// 获取升级内容
Route::get('upgrade/[:addon]', 'addon.Upgrade/getUpgradeContent');
})->middleware([
AdminCheckToken::class,
AdminCheckRole::class,
AdminLog::class
]);

View File

@ -41,6 +41,9 @@ class Area extends BaseApiController
*
* @return void
*/
/**
* 通过编码查询地址信息
*/
public function areaByAreaCode(string $code) {
return success((new AreaService())->getAreaByAreaCode($code));
}

View File

@ -11,13 +11,13 @@ use app\service\core\upload\CoreImageService;
/**
* 接口操作成功,返回信息
* @param string $msg
* @param array|string $msg
* @param array|string|bool|null $data
* @param int $code
* @param int $http_code
* @return Response
*/
function success($msg = 'SUCCESS', array|string|bool|null $data = [], int $code = 1, int $http_code = 200): Response
function success(array|string $msg = 'SUCCESS', array|string|bool|null $data = [], int $code = 1, int $http_code = 200): Response
{
if (is_array($msg)) {
$data = $msg;
@ -237,7 +237,7 @@ function path_to_url($path)
/**
* 链接转化路径
* @param $url
* @return void
* @return string
*/
function url_to_path($url)
{

View File

@ -304,10 +304,6 @@ class Index extends BaseInstall
return fail('平台两次密码输入不一样,请重新输入');
}
// if ($site_name == '' || $site_username == '' || $site_password == '') {
// return fail('站点信息不能为空!');
// }
if($site_username == $username) {
return fail('站点账号不能跟平台账号一致');
}
@ -348,17 +344,7 @@ class Index extends BaseInstall
//修改自增主键默认值
Db::execute("alter table ".env('database.prefix', '')."site auto_increment = 100000");
//获取默认套餐
// $group_id = (new SiteGroupService())->addAllMenuGroup();
//
// $data = [
// 'site_name' => $site_name,
// 'real_name' => '',
// 'group_id' => $group_id,
// 'expire_time' => 0,
// 'username' => $site_username,
// 'password' => $site_password,
// ];
// (new SiteService())->add($data);
$fp = fopen($this->lock_file, 'wb');
if (!$fp) {
$this->setSuccessLog([ "写入失败,请检查目录" . dirname(__FILE__, 2) . "是否可写入!'", 'error' ]);

View File

@ -776,7 +776,7 @@ CREATE TABLE `sys_user` (
`head_img` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',
`real_name` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '实际姓名',
`last_ip` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '最后一次登录ip',
`last_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '最后一次登录ip',
`last_time` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '最后一次登录时间',
`create_time` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '添加时间',
`login_count` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '登录次数',
@ -791,7 +791,7 @@ CREATE TABLE `sys_user` (
DROP TABLE IF EXISTS `sys_user_log`;
CREATE TABLE `sys_user_log` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '管理员操作记录ID',
`ip` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '登录IP',
`ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '登录IP',
`site_id` int(11) NOT NULL DEFAULT 0 COMMENT '站点id',
`uid` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '管理员id',
`username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '管理员姓名',

View File

@ -22,7 +22,7 @@ class CheckJob extends BaseJob
{
file_put_contents($file, time());
//todo 部署一个十五秒后再校验一次删除这个文件
CheckDeleteJob::invoke(['file' => $file], secs: 8);
CheckDeleteJob::dispatch(['file' => $file], secs: 8);
return true;
}
}

View File

@ -243,4 +243,13 @@ return [
/********************************************************* home端专用 **************************************/
'USER_ROLE_NOT_HAS_SITE' => '当前登录用户下没有此项站点',
// 云服务
'CLOUD_WEAPP_COMPILE_NOT_EXIST' => '未找到微信小程序编译包',
'WEAPP_APPID_EMPTY' => '还没有配置微信小程序',
'UPLOAD_KEY_EMPTY' => '还没有配置微信小程序代码上传秘钥',
'UPLOAD_KEY_NOT_EXIST' => '未找到微信小程序代码上传秘钥',
'NEED_TO_AUTHORIZE_FIRST' => '使用云服务需先进行授权',
'WEAPP_UPLOADING' => '小程序有正在上传的版本,请等待上一版本上传完毕后再进行操作',
'CLOUD_BUILD_TASK_EXIST' => '已有正在执行中的编译任务'
];

View File

@ -45,7 +45,9 @@ return [
'status_experience' => '体验期',
'status_expire' => '已到期',
'status_close' => '已停止',
'pay' => '支付',
'refund' => '退款',
'transfer' => '转账',
],
// 站点
'dict_site_index' => [

View File

@ -23,7 +23,6 @@ use app\service\core\addon\CoreAddonInstallService;
use app\service\core\addon\CoreAddonService;
use app\service\core\niucloud\CoreModuleService;
use app\service\core\site\CoreSiteService;
use app\service\core\upgrade\CoreUpgradeService;
use core\base\BaseAdminService;
use Exception;
use think\db\exception\DbException;
@ -58,7 +57,6 @@ class AddonService extends BaseAdminService
/**
* 安装插件
* @param string $addon
* @return Response
*/
public function install(string $addon)
{
@ -68,7 +66,6 @@ class AddonService extends BaseAdminService
/**
* 云安装插件
* @param string $addon
* @return Response
*/
public function cloudInstall(string $addon)
{
@ -97,7 +94,6 @@ class AddonService extends BaseAdminService
/**
* 安装插件检测安装环境
* @param string $addon
* @return Response
*/
public function installCheck(string $addon)
{
@ -107,7 +103,6 @@ class AddonService extends BaseAdminService
/**
* 取消安装任务
* @param string $addon
* @return Response
*/
public function cancleInstall(string $addon)
{
@ -156,7 +151,6 @@ class AddonService extends BaseAdminService
* 设置插件状态
* @param int $id
* @param int $status
* @return null
*/
public function setStatus(int $id, int $status){
return (new CoreAddonService())->setStatus($id, $status);
@ -171,14 +165,6 @@ class AddonService extends BaseAdminService
return (new CoreAddonDownloadService())->download($app_key, $version);
}
/**
* 更新应用
* @param string $app_key
* @return null
*/
public function upgrade(string $app_key = ''){
return (new CoreUpgradeService())->upgrade($app_key);
}
/**
* 查询已安装应用

View File

@ -417,6 +417,8 @@ class GenerateService extends BaseAdminService
} catch ( Exception $e) {
throw new AdminException($e->getMessage());
}
}else{
return [];
}
}

View File

@ -138,6 +138,8 @@ class MenuSqlGenerator extends BaseGenerator
$dir = $this->rootDir . '/niucloud/addon/'.$this->addonName.'/sql/';
$this->checkDir($dir);
return $dir;
}else{
return '';
}
}

View File

@ -179,7 +179,6 @@ class WebApiGenerator extends BaseGenerator
/**
* 获取文件生成到插件中
* @return void
*/
public function getAddonObjectOutDir() {
$dir = $this->rootDir . '/niucloud/addon/'.$this->addonName.'/admin/api/';

View File

@ -143,7 +143,6 @@ class WebEditLangGenerator extends BaseGenerator
/**
* 获取文件生成到插件中
* @return void
*/
public function getAddonObjectOutDir() {
$dir = $this->rootDir . '/niucloud/addon/'.$this->addonName.'/admin/lang/zh-cn/';

View File

@ -98,7 +98,6 @@ class WebEditPageGenerator extends BaseGenerator
/**
* 调用字典方法
* @return void
*/
public function getDictList()
{
@ -431,7 +430,6 @@ class WebEditPageGenerator extends BaseGenerator
/**
* 获取文件生成到插件中
* @return void
*/
public function getAddonObjectOutDir() {
$dir = $this->rootDir . '/niucloud/addon/'.$this->addonName.'/admin/views/'. $this->moduleName . '/';

View File

@ -416,7 +416,6 @@ class WebIndexGenerator extends BaseGenerator
/**
* 获取文件生成到插件中
* @return void
*/
public function getAddonObjectOutDir() {
$dir = $this->rootDir . '/niucloud/addon/'.$this->addonName.'/admin/views/'. $this->moduleName . '/';
@ -482,7 +481,6 @@ class WebIndexGenerator extends BaseGenerator
/**
* 调用字典方法
* @return void
*/
public function getDictList()
{

View File

@ -136,7 +136,6 @@ class WebLangGenerator extends BaseGenerator
/**
* 获取文件生成到插件中
* @return void
*/
public function getAddonObjectOutDir() {
$dir = $this->rootDir . '/niucloud/addon/'.$this->addonName.'/admin/lang/zh-cn/';

View File

@ -1,5 +1,5 @@
<el-form-item :label="t('{LOWER_COLUMN_NAME}')" {PROP}>
<el-checkbox-group v-model="formData.{COLUMN_NAME}" :placeholder="t('{LOWER_COLUMN_NAME}Placeholder')">
<el-form-item :label="t('{LCASE_COLUMN_NAME}')" {PROP}>
<el-checkbox-group v-model="formData.{COLUMN_NAME}" :placeholder="t('{LCASE_COLUMN_NAME}Placeholder')">
<el-checkbox label="1">选项1</el-checkbox>
<el-checkbox label="2">选项2</el-checkbox>
</el-checkbox-group>

View File

@ -1,5 +1,5 @@
<el-form-item :label="t('{LOWER_COLUMN_NAME}')" {PROP}>
<el-radio-group v-model="formData.{COLUMN_NAME}" :placeholder="t('{LOWER_COLUMN_NAME}Placeholder')">
<el-form-item :label="t('{LCASE_COLUMN_NAME}')" {PROP}>
<el-radio-group v-model="formData.{COLUMN_NAME}" :placeholder="t('{LCASE_COLUMN_NAME}Placeholder')">
<el-radio label="1">
选项1
</el-radio>

View File

@ -1,5 +1,5 @@
<el-form-item :label="t('{LOWER_COLUMN_NAME}')" {PROP}>
<el-select v-model="formData.{COLUMN_NAME}" clearable :placeholder="t('{LOWER_COLUMN_NAME}Placeholder')" class="input-width">
<el-form-item :label="t('{LCASE_COLUMN_NAME}')" {PROP}>
<el-select v-model="formData.{COLUMN_NAME}" clearable :placeholder="t('{LCASE_COLUMN_NAME}Placeholder')" class="input-width">
<el-option
label="选项1"
value="1"

View File

@ -1,5 +1,5 @@
<el-form-item :label="t('{LOWER_COLUMN_NAME}')" prop="{COLUMN_NAME}">
<el-select class="" v-model="{LOWER_CLASS_NAME}TableData.searchParam.{COLUMN_NAME}" clearable :placeholder="t('{LOWER_COLUMN_NAME}Placeholder')">
<el-form-item :label="t('{LCASE_COLUMN_NAME}')" prop="{COLUMN_NAME}">
<el-select class="" v-model="{LCASE_CLASS_NAME}TableData.searchParam.{COLUMN_NAME}" clearable :placeholder="t('{LCASE_COLUMN_NAME}Placeholder')">
<el-option label="全部" value=""></el-option>
</el-select>

View File

@ -136,7 +136,6 @@ class MemberService extends BaseAdminService
* @param int $member_id
* @param string $field
* @param $data
* @return null
*/
public function modify(int $member_id, string $field, $data)
{
@ -146,7 +145,6 @@ class MemberService extends BaseAdminService
/**
* 组合整理数据
* @param $data
* @return void
*/
public function makeUp($data){
//会员标签

View File

@ -69,11 +69,15 @@ class NiucloudService extends BaseAdminService
/**
* 获取框架最新版本
* @return void
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getFrameworkLastVersion() {
return (new CoreModuleService())->getFrameworkLastVersion();
}
/**
* 获取框架版本更新记录
*/
public function getFrameworkVersionList() {
return (new CoreModuleService())->getFrameworkVersionList();
}
}

View File

@ -68,7 +68,6 @@ class NoticeService extends BaseAdminService
* @param string $key
* @param string $type
* @param int $status
* @return Response
*/
public function editMessageStatus(string $key, string $type, int $status)
{
@ -82,7 +81,6 @@ class NoticeService extends BaseAdminService
* @param string $key
* @param string $type
* @param array $data
* @return Response
*/
public function edit(string $key, string $type, array $data)
{

View File

@ -66,7 +66,6 @@ class PayService extends BaseAdminService
* 支付审核未通过
* @param string $out_trade_no
* @param string $reason
* @return void
*/
public function refuse(string $out_trade_no, string $reason) {
return (new CoreOfflineService())->refuse($this->site_id, $out_trade_no, $reason);

View File

@ -44,7 +44,7 @@ class RefundService extends BaseAdminService
/**
* 获取退款详情
* @param string $refund_no
* @return void
* @return array
*/
public function getDetail(string $refund_no){
$field = 'id,refund_no,out_trade_no,type,channel,money,reason,status,create_time,refund_time,close_time,fail_reason,voucher,trade_type,trade_id,refund_type,main_type,main_id';
@ -58,7 +58,7 @@ class RefundService extends BaseAdminService
/**
* 支付审核通过
* @param array $data
* @return null
* @return bool
*/
public function refund(array $data) {
return (new CoreRefundService())->refund($this->site_id, $data['refund_no'], $data['voucher'], $data['refund_type'], PayDict::USER, $this->uid);

View File

@ -19,6 +19,7 @@ use app\service\admin\sys\MenuService;
use app\service\core\addon\CoreAddonService;
use core\base\BaseAdminService;
use core\exception\AdminException;
use core\exception\CommonException;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
@ -123,7 +124,7 @@ class SiteGroupService extends BaseAdminService
$count = (new Site())->where([['group_id', '=', $group_id]])->count();
if($count > 0)
{
throw new AdminException('SITE_GROUP_IS_EXIST');
throw new CommonException('SITE_GROUP_IS_EXIST');
}
$res = $this->model->where([['group_id', '=', $group_id]])->delete();

View File

@ -163,7 +163,6 @@ class SiteService extends BaseAdminService
/**
* 删除站点
* @param int $site_id
* @return void
*/
public function del(int $site_id) {
Db::startTrans();
@ -302,7 +301,7 @@ class SiteService extends BaseAdminService
/**
* 获取站点的插件
* @return void
* @return array
*/
public function getSiteAddons(array $where) {
$site_addon = (new CoreSiteService())->getAddonKeysBySiteId($this->site_id);

View File

@ -11,6 +11,7 @@
namespace app\service\admin\stat;
use app\model\site\Site;
use app\service\admin\site\SiteGroupService;
use app\service\admin\site\SiteService;
use app\service\admin\sys\SystemService;
@ -101,7 +102,7 @@ class StatService extends BaseAdminService
for ($i = 1; $i <= 7; $i++){
$item_day = date('Y-m-d', strtotime('+' . $i - 7 . ' days', $time));
$data['site_stat']['date'][] = $item_day;
$data['site_stat']['value'][] = (new SiteService())->getCount(['create_time' => get_start_and_end_time_by_day($item_day)]);
$data['site_stat']['value'][] = (new Site())->where([['create_time','between',get_start_and_end_time_by_day($item_day)]])->count();
}
$man_count = (new CoreMemberService())->getCount(['sex' => '1']);
$woman_count = (new CoreMemberService())->getCount(['sex' => '2']);
@ -127,4 +128,4 @@ class StatService extends BaseAdminService
}
}
}

View File

@ -41,7 +41,6 @@ class AttachmentService extends BaseAdminService
/**
* 新增素材
* @param array $data
* @return mixed
*/
public function add(array $data)
{

View File

@ -600,7 +600,6 @@ class MenuService extends BaseAdminService
/**
* 查询菜单类型为目录的菜单
* @param string $addon
* @return void
*/
public function getMenuByTypeDir(string $addon = 'system') {
$cache_name = 'menu_api_by_type_dir' . $addon;

View File

@ -172,7 +172,6 @@ class SystemService extends BaseAdminService
/**
* 设置布局
* @param string $key
* @return void
*/
public function setLayout(string $key) {
$layouts = array_column(event('SiteLayout'), 'key');

View File

@ -0,0 +1,103 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的saas管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud-admin.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\admin\upgrade;
use app\dict\addon\AddonDict;
use core\util\DbBackup;
/**
* 框架及插件升级备份
* @package app\service\core\upgrade
*/
class BackupService extends UpgradeService
{
/**
* 备份代码
* @return void
*/
public function backupCode() {
$backup_dir = $this->upgrade_dir .$this->upgrade_task['key'] . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR . 'code' . DIRECTORY_SEPARATOR;
// 创建目录
dir_mkdir($backup_dir);
// 备份admin
dir_copy($this->root_path . 'admin', $backup_dir . 'admin', exclude_dirs:[ '.vscode', 'node_modules', 'dist']);
// 备份uni-app
dir_copy($this->root_path . 'uni-app', $backup_dir . 'uni-app', exclude_dirs:['node_modules', 'dist']);
// 备份web
dir_copy($this->root_path . 'web', $backup_dir . 'web', exclude_dirs:['node_modules', '.nuxt', '.output']);
// 备份niucloud
$niucloud_dir = $backup_dir . 'niucloud' . DIRECTORY_SEPARATOR;
if ($this->upgrade_task['upgrade']['app_key'] == AddonDict::FRAMEWORK_KEY) {
dir_copy($this->root_path . 'niucloud', $niucloud_dir, exclude_dirs:['addon', 'config', 'public', 'vendor', 'runtime']);
// 备份版本文件
$version_file = $this->root_path . 'niucloud' .DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'version.php';
$to_version_file = $niucloud_dir . 'config' . DIRECTORY_SEPARATOR . 'version.php';
file_copy($version_file, $to_version_file);
} else {
$addon = $this->upgrade_task['upgrade']['app_key'];
$addon_dir = $this->root_path . 'niucloud' . DIRECTORY_SEPARATOR . 'addon' . DIRECTORY_SEPARATOR . $addon;
$to_addon_dir = $niucloud_dir . 'addon' . DIRECTORY_SEPARATOR . $addon;
dir_copy($addon_dir, $to_addon_dir);
}
// 备份前端文件
if (is_dir(public_path() . 'admin')) {
dir_copy(public_path() . 'admin', $niucloud_dir . 'public' . DIRECTORY_SEPARATOR . 'admin');
}
if (is_dir(public_path() . 'wap')) {
dir_copy(public_path() . 'wap', $niucloud_dir . 'public' . DIRECTORY_SEPARATOR . 'wap');
}
if (is_dir(public_path() . 'web')) {
dir_copy(public_path() . 'web', $niucloud_dir . 'public' . DIRECTORY_SEPARATOR . 'web');
}
return true;
}
/**
* 备份数据库
* @return void
*/
public function backupSql() {
$backup_dir = $this->upgrade_dir .$this->upgrade_task['key'] . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR . 'sql' . DIRECTORY_SEPARATOR;
// 创建目录
dir_mkdir($backup_dir);
$db = new DbBackup([
'path' => $backup_dir,//数据库备份路径
'part' => 1048576,//数据库备份卷大小
'compress' => 0,//数据库备份文件是否启用压缩 0不压缩 1 压缩
'level' => 9 //数据库备份文件压缩级别 1普通 4 一般 9最高
]);
$prefix = config('database.connections.'.config('database.default'))['prefix'];
if ($this->upgrade_task['upgrade']['app_key'] == AddonDict::FRAMEWORK_KEY) {
// 不需要备份的表
$noot_need_backup = ["{$prefix}sys_user_log", "{$prefix}jobs", "{$prefix}jobs_failed"];
$tables = array_diff(array_column($db->dataList(), 'name'), $noot_need_backup);
} else {
$tables = [];
$table_prefix = "{$prefix}{$this->upgrade_task['upgrade']['app_key']}";
foreach ($db->dataList() as $table) {
if (strpos($table['name'], $table_prefix) === 0) {
$tables[] = $table['name'];
}
}
}
foreach ($tables as $table) {
$db->setFile()->backup($table);
}
return true;
}
}

View File

@ -0,0 +1,64 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\admin\upgrade;
/**
* 执行sql文件
*/
trait ExecuteSqlTrait
{
public function getSqlQuery($sql_data)
{
$sql_data = preg_replace("/TYPE=(InnoDB|MyISAM|MEMORY)( DEFAULT CHARSET=[^; ]+)?/", "ENGINE=\\1 DEFAULT CHARSET=utf8", $sql_data);
$sql_data = str_replace("\r", "\n", $sql_data);
$sql_query = [];
$num = 0;
$sql_arr = explode(";\n", trim($sql_data));
unset($sql);
foreach ($sql_arr as $sql) {
$sql_query[ $num ] = '';
$sqls = explode("\n", trim($sql));
$sqls = array_filter($sqls);
foreach ($sqls as $query) {
$str1 = $query[0] ?? '';
if ($str1 != '#' && $str1 != '-')
$sql_query[ $num ] .= $query;
}
$num++;
}
return $sql_query;
}
/**
* 处理sql增加表前缀
* @param $sql
* @return void
*/
public function handleSqlPrefix($sql, $prefix) {
if (str_contains($sql, 'CREATE TABLE')) {
$match_item = preg_match('/CREATE TABLE [`]?(\\w+)[`]?/is', $sql, $match_data);
} elseif (str_contains($sql, 'ALTER TABLE')) {
$match_item = preg_match('/ALTER TABLE [`]?(\\w+)[`]?/is', $sql, $match_data);
} elseif (str_contains($sql, 'INSERT INTO')) {
$match_item = preg_match('/INSERT INTO [`]?(\\w+)[`]?/is', $sql, $match_data);
} else {
$match_item = 0;
}
if ($match_item > 0) {
$table_name = $match_data[ 1 ];
$new_table_name = $prefix . $table_name;
$sql = implode($new_table_name, explode($table_name, $sql, 2));
}
return $sql;
}
}

View File

@ -0,0 +1,59 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的saas管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud-admin.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\admin\upgrade;
use core\util\DbBackup;
/**
* 框架及插件升级恢复备份
* @package app\service\core\upgrade
*/
class RestoreService extends UpgradeService
{
/**
* 恢复代码备份
* @return true
*/
public function restoreCode() {
$backup_dir = $this->upgrade_dir .$this->upgrade_task['key'] . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR . 'code' . DIRECTORY_SEPARATOR;
if (is_dir($backup_dir)) {
// 删除前端文件
if (is_dir(public_path() . 'admin')) del_target_dir(public_path() . 'admin', true);
if (is_dir(public_path() . 'wap')) del_target_dir(public_path() . 'wap', true);
if (is_dir(public_path() . 'web')) del_target_dir(public_path() . 'web', true);
dir_copy($backup_dir, $this->root_path);
}
return true;
}
/**
* 恢复数据库备份
* @return true
*/
public function restoreSql() {
$backup_dir = $this->upgrade_dir .$this->upgrade_task['key'] . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR . 'sql' . DIRECTORY_SEPARATOR;
if (is_dir($backup_dir)) {
$db = new DbBackup([
'path' => $backup_dir //数据库备份路径
]);
$file_list = $db->fileList();
if (!empty($file_list)) {
foreach ($file_list as $file) {
$db->setFile($file)->import(0, $file['time']);
}
}
}
return true;
}
}

View File

@ -0,0 +1,524 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的saas管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud-admin.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\admin\upgrade;
use app\dict\addon\AddonDict;
use app\model\addon\Addon;
use app\service\admin\install\InstallSystemService;
use app\service\core\addon\CoreAddonCloudService;
use app\service\core\addon\CoreAddonService;
use app\service\core\addon\CoreDependService;
use app\service\core\addon\WapTrait;
use app\service\core\menu\CoreMenuService;
use app\service\core\niucloud\CoreModuleService;
use app\service\core\schedule\CoreScheduleInstallService;
use core\base\BaseAdminService;
use core\exception\CommonException;
use core\util\niucloud\BaseNiucloudClient;
use think\facade\Cache;
use think\facade\Db;
/**
* 框架及插件升级
* @package app\service\core\upgrade
*/
class UpgradeService extends BaseAdminService
{
use WapTrait;
use ExecuteSqlTrait;
protected $upgrade_dir;
protected $root_path;
protected $cache_key = 'upgrade';
protected $upgrade_task = null;
protected $addon = '';
private $steps = [
'requestUpgrade' => ['step' => 'requestUpgrade', 'title' => '请求升级'],
'downloadFile' => ['step' => 'downloadFile', 'title' => '下载更新文件'],
'backupCode' => ['step' => 'backupCode', 'title' => '备份源码'],
'backupSql' => ['step' => 'backupSql', 'title' => '备份数据库'],
'coverCode' => ['step' => 'coverCode', 'title' => '合并更新文件'],
'handleUniapp' => ['step' => 'handleUniapp', 'title' => '处理uniapp'],
'refreshMenu' => ['step' => 'refreshMenu', 'title' => '刷新菜单'],
'installSchedule' => ['step' => 'installSchedule', 'title' => '安装计划任务'],
'upgradeComplete' => ['step' => 'upgradeComplete', 'title' => '升级完成']
];
public function __construct()
{
parent::__construct();
$this->root_path = dirname(root_path()) . DIRECTORY_SEPARATOR;
$this->upgrade_dir = $this->root_path . 'upgrade' . DIRECTORY_SEPARATOR;
$this->upgrade_task = Cache::get($this->cache_key);
}
/**
* 升级前环境检测
* @param string $addon
* @return void
*/
public function upgradePreCheck(string $addon = '') {
$niucloud_dir = $this->root_path . 'niucloud' . DIRECTORY_SEPARATOR;
$admin_dir = $this->root_path . 'admin' . DIRECTORY_SEPARATOR;
$web_dir = $this->root_path . 'web' . DIRECTORY_SEPARATOR;
$wap_dir = $this->root_path . 'uni-app' . DIRECTORY_SEPARATOR;
try {
if (!is_dir($admin_dir)) throw new CommonException('ADMIN_DIR_NOT_EXIST');
if (!is_dir($web_dir)) throw new CommonException('WEB_DIR_NOT_EXIST');
if (!is_dir($wap_dir)) throw new CommonException('UNIAPP_DIR_NOT_EXIST');
} catch (\Exception $e) {
if (strpos($e->getMessage(), 'open basedir') !== false) {
throw new CommonException('OPEN_BASEDIR_ERROR');
}
throw new CommonException($e->getMessage());
}
$data = [
// 目录检测
'dir' => [
// 要求可读权限
'is_readable' => [],
// 要求可写权限
'is_write' => []
]
];
$data['dir']['is_readable'][] = ['dir' => str_replace(project_path(), '', $niucloud_dir), 'status' => is_readable($niucloud_dir)];
$data['dir']['is_readable'][] = ['dir' => str_replace(project_path(), '', $admin_dir), 'status' => is_readable($admin_dir)];
$data['dir']['is_readable'][] = ['dir' => str_replace(project_path(), '', $web_dir), 'status' => is_readable($web_dir)];
$data['dir']['is_readable'][] = ['dir' => str_replace(project_path(), '', $wap_dir), 'status' => is_readable($wap_dir)];
$data['dir']['is_write'][] = ['dir' => str_replace(project_path(), '', $niucloud_dir), 'status' => is_write($niucloud_dir) ];
$data['dir']['is_write'][] = ['dir' => str_replace(project_path(), '', $admin_dir), 'status' => is_write($admin_dir) ];
$data['dir']['is_write'][] = ['dir' => str_replace(project_path(), '', $web_dir), 'status' => is_write($web_dir) ];
$data['dir']['is_write'][] = ['dir' => str_replace(project_path(), '', $wap_dir), 'status' => is_write($wap_dir) ];
$check_res = array_merge(
array_column($data['dir']['is_readable'], 'status'),
array_column($data['dir']['is_write'], 'status')
);
// 是否通过校验
$data['is_pass'] = !in_array(false, $check_res);
return $data;
}
/**
* 升级
* @param $addon
* @return array
*/
public function upgrade(string $addon = '') {
if ($this->upgrade_task) throw new CommonException('UPGRADE_TASK_EXIST');
$upgrade = [
'product_key' => BaseNiucloudClient::PRODUCT,
'framework_version' => config('version.version')
];
if (!$addon) {
$upgrade['app_key'] = AddonDict::FRAMEWORK_KEY;
$upgrade['version'] = config('version.version');
} else {
$upgrade['app_key'] = $addon;
$upgrade['version'] = (new Addon())->where([ ['key', '=', $addon] ])->value('version');
}
$response = (new CoreAddonCloudService())->upgradeAddon($upgrade);
if (isset($response['code']) && $response['code'] == 0) throw new CommonException($response['msg']);
try {
$key = uniqid();
$upgrade_dir = $this->upgrade_dir . $key . DIRECTORY_SEPARATOR;
if (!is_dir($upgrade_dir)) {
dir_mkdir($upgrade_dir);
}
$upgrade_tsak = [
'key' => $key,
'upgrade' => $upgrade,
'step' => 'requestUpgrade',
'executed' => ['requestUpgrade'],
'log' => [ $this->steps['requestUpgrade']['title'] ],
'params' => ['token' => $response['token'] ],
'upgrade_content' => $this->getUpgradeContent($addon)
];
Cache::set($this->cache_key, $upgrade_tsak);
return $upgrade_tsak;
} catch (\Exception $e) {
if (strpos($e->getMessage(), 'open_basedir') !== false) {
throw new CommonException('OPEN_BASEDIR_ERROR');
}
throw new CommonException($e->getMessage());
}
}
/**
* 执行升级
* @return true
*/
public function execute() {
if (!$this->upgrade_task) return true;
$steps = array_keys($this->steps);
$index = array_search($this->upgrade_task['step'], $steps);
$step = $steps[ $index + 1 ] ?? '';
$params = $this->upgrade_task['params'] ?? [];
if ($step) {
try {
$res = $this->$step(...$params);
if (is_array($res)) {
$this->upgrade_task['params'] = $res;
} else {
$this->upgrade_task['step'] = $step;
$this->upgrade_task['params'] = [];
$this->upgrade_task['executed'][] = $step;
$this->upgrade_task['log'][] = $this->steps[$step]['title'];
}
Cache::set($this->cache_key, $this->upgrade_task);
} catch (\Exception $e) {
$this->upgrade_task['step'] = $step;
$this->upgrade_task['error'] = $e->getMessage();
$this->upgradeErrorHandle();
}
return true;
} else {
return true;
}
}
/**
* 下载升级文件
* @param string $token
* @param string $dir
* @param int $index
* @param $step
* @return true|null
*/
public function downloadFile(string $token, string $dir = '', int $index = -1, $step = 0, $length = 0) {
if (!$dir) {
$dir = $this->upgrade_dir .$this->upgrade_task['key'] . DIRECTORY_SEPARATOR . 'download' . DIRECTORY_SEPARATOR;
dir_mkdir($dir);
}
$res = (new CoreAddonCloudService())->downloadUpgradeFile($token, $dir, $index, $step, $length);
return $res;
}
/**
* 备份源码
* @return true
*/
public function backupCode() {
(new BackupService())->backupCode();
return true;
}
/**
* 备份数据库
* @return true
*/
public function backupSql() {
(new BackupService())->backupSql();
return true;
}
/**
* 覆盖更新升级的代码
* @return void
*/
public function coverCode($index = 0) {
$this->upgrade_task['is_cover'] = 1;
$version_list = array_reverse($this->upgrade_task['upgrade_content']['version_list']);
$code_dir = $this->upgrade_dir .$this->upgrade_task['key'] . DIRECTORY_SEPARATOR . 'download' . DIRECTORY_SEPARATOR . 'code' . DIRECTORY_SEPARATOR;
$version_item = $version_list[$index];
$version_no = $version_item['version_no'];
// 获取文件变更记录
if (file_exists($code_dir . $version_no . '.txt')) {
$change = array_filter(explode("\n", file_get_contents($code_dir . $version_no . '.txt')));
foreach ($change as &$item) {
list($operation, $md5, $file) = $item = explode(' ', $item);
if ($operation == '-') @unlink($this->root_path . $file);
}
// 合并依赖
$this->installDepend($code_dir . $version_no, array_column($change, 2));
}
// 覆盖文件
if (is_dir($code_dir . $version_no)) {
dir_copy($code_dir . $version_no, $this->root_path);
}
$upgrade_file_dir = 'v' . str_replace('.', '', $version_no);
$addon = $this->upgrade_task['upgrade']['app_key'];
if ($addon == AddonDict::FRAMEWORK_KEY) {
$class_path = "\\app\\upgrade\\{$upgrade_file_dir}\\Upgrade";
$sql_file = root_path() . 'app' . DIRECTORY_SEPARATOR . 'upgrade' . DIRECTORY_SEPARATOR . $upgrade_file_dir . DIRECTORY_SEPARATOR . 'upgrade.sql';
} else {
$class_path = "\\addon\\{$addon}\\app\\upgrade\\{$upgrade_file_dir}\\Upgrade";
$sql_file = root_path() . 'addon' . DIRECTORY_SEPARATOR . $addon . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'upgrade' . DIRECTORY_SEPARATOR . $upgrade_file_dir . DIRECTORY_SEPARATOR . 'upgrade.sql';
}
// 执行升级sql
if (file_exists($sql_file)) {
$this->executeSql($sql_file);
}
// 执行升级方法
if (class_exists($class_path)) {
(new $class_path())->handle();
}
$index ++;
if ($index < count($version_list)) {
return compact('index');
} else {
return true;
}
}
/**
* 合并依赖
* @param string $version_no
* @return void
*/
public function installDepend(string $dir, array $change_files) {
$addon = $this->upgrade_task['upgrade']['app_key'];
$depend_service = new CoreDependService();
if ($addon == AddonDict::FRAMEWORK_KEY) {
$composer = '/niucloud/composer.json';
$admin_package = '/admin/package.json';
$web_package = '/web/package.json';
$uniapp_package = '/uni-app/package.json';
} else {
$composer = "/niucloud/addon/{$addon}/package/composer.json";
$admin_package = "/niucloud/addon/{$addon}/package/admin-package.json";
$web_package = "/niucloud/addon/{$addon}/package/web-package.json";
$uniapp_package = "/niucloud/addon/{$addon}/package/uni-app-package.json";
}
if (in_array($composer, $change_files)) {
$original = $depend_service->getComposerContent();
$new = $depend_service->jsonFileToArray($dir . $composer);
if (isset($new['require'])) $original['require'] = array_merge($original['require'], $new['require']);
if (isset($new['require_dev'])) $original['require_dev'] = array_merge($original['require_dev'], $new['require_dev']);
$depend_service->writeArrayToJsonFile($original, $dir . $composer);
}
if (in_array($admin_package, $change_files)) {
$original = $depend_service->getNpmContent('admin');
$new = $depend_service->jsonFileToArray($dir . $admin_package);
if (isset($new['dependencies'])) $original['dependencies'] = array_merge($original['dependencies'], $new['dependencies']);
if (isset($new['devDependencies'])) $original['devDependencies'] = array_merge($original['devDependencies'], $new['devDependencies']);
$depend_service->writeArrayToJsonFile($original, $dir . $admin_package);
}
if (in_array($web_package, $change_files)) {
$original = $depend_service->getNpmContent('web');
$new = $depend_service->jsonFileToArray($dir . $web_package);
if (isset($new['dependencies'])) $original['dependencies'] = array_merge($original['dependencies'], $new['dependencies']);
if (isset($new['devDependencies'])) $original['devDependencies'] = array_merge($original['devDependencies'], $new['devDependencies']);
$depend_service->writeArrayToJsonFile($original, $dir . $web_package);
}
if (in_array($uniapp_package, $change_files)) {
$original = $depend_service->getNpmContent('uni-app');
$new = $depend_service->jsonFileToArray($dir . $uniapp_package);
if (isset($new['dependencies'])) $original['dependencies'] = array_merge($original['dependencies'], $new['dependencies']);
if (isset($new['devDependencies'])) $original['devDependencies'] = array_merge($original['devDependencies'], $new['devDependencies']);
$depend_service->writeArrayToJsonFile($original, $dir . $uniapp_package);
}
}
/**
* 处理手机端
* @param string $verson_no
* @return true
*/
public function handleUniapp() {
$code_dir = $this->upgrade_dir .$this->upgrade_task['key'] . DIRECTORY_SEPARATOR . 'download' . DIRECTORY_SEPARATOR . 'code' . DIRECTORY_SEPARATOR;
dir_copy($code_dir . 'uni-app', $this->root_path . 'uni-app');
$addon_list = (new CoreAddonService())->getInstallAddonList();
if (!empty($addon_list)) {
foreach ($addon_list as $addon => $item) {
$this->addon = $addon;
// 编译 diy-group 自定义组件代码文件
$this->compileDiyComponentsCode($this->root_path . 'uni-app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR, $addon);
// 编译 fixed-group 固定模板组件代码文件
$this->compileFixedComponentsCode($this->root_path . 'uni-app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR, $addon);
// 编译 pages.json 页面路由代码文件
$this->installPageCode($this->root_path . 'uni-app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR);
// 编译 加载插件标题语言包
$this->compileLocale($this->root_path . 'uni-app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR, $addon);
}
}
return true;
}
/**
* 执行升级sql
* @param string $sql_file
* @return true
*/
private function executeSql(string $sql_file) {
$sql_content = file_get_contents($sql_file);
if (!empty($sql_content)) {
$prefix = config('database.connections.mysql.prefix');
$sql_data = array_filter($this->getSqlQuery($sql_content));
if (!empty($sql_data)) {
foreach ($sql_data as $sql) {
$sql = $prefix ? $this->handleSqlPrefix($sql, $prefix) : $sql;
Db::query($sql);
}
}
}
return true;
}
/**
* 刷新菜单
* @return void
*/
public function refreshMenu() {
if ($this->upgrade_task['upgrade']['app_key'] == AddonDict::FRAMEWORK_KEY) {
(new InstallSystemService())->installMenu();
} else {
(new CoreMenuService())->refreshAddonMenu($this->upgrade_task['upgrade']['app_key']);
}
return true;
}
/**
* 安装计划任务
* @return true
*/
public function installSchedule() {
if ($this->upgrade_task['upgrade']['app_key'] == AddonDict::FRAMEWORK_KEY) {
(new CoreScheduleInstallService())->installSystemSchedule();
} else {
(new CoreScheduleInstallService())->installAddonSchedule($this->upgrade_task['upgrade']['app_key']);
}
return true;
}
/**
* 更新完成
* @return void
*/
public function upgradeComplete() {
$addon = $this->upgrade_task['upgrade']['app_key'];
if ($addon != AddonDict::FRAMEWORK_KEY) {
$core_addon_service = new CoreAddonService();
$install_data = $core_addon_service->getAddonConfig($addon);
$install_data['icon'] = 'addon/' . $addon . '/icon.png';
$core_addon_service->set($install_data);
}
$this->clearUpgradeTask(5);
return true;
}
/**
* 升级出错之后的处理
* @return true|void
*/
public function upgradeErrorHandle() {
try {
if (isset($this->upgrade_task['is_cover'])) {
$restore_service = (new RestoreService());
$restore_service->restoreCode();
$restore_service->restoreSql();
}
$this->clearUpgradeTask(5);
return true;
} catch (\Exception $e) {
return true;
}
}
/**
* 获取升级内容
* @param string $addon
* @return array|\core\util\niucloud\Response|object|\Psr\Http\Message\ResponseInterface
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getUpgradeContent(string $addon = '') {
$upgrade = [
'product_key' => BaseNiucloudClient::PRODUCT
];
if (!$addon) {
$upgrade['app_key'] = AddonDict::FRAMEWORK_KEY;
$upgrade['version'] = config('version.version');
} else {
$upgrade['app_key'] = $addon;
$upgrade['version'] = (new Addon())->where([ ['key', '=', $addon] ])->value('version');
}
return (new CoreModuleService())->getUpgradeContent($upgrade)['data'] ?? [];
}
/**
* 获取正在进行的升级任务
* @return mixed|null
*/
public function getUpgradeTask() {
return $this->upgrade_task;
}
/**
* 清除升级任务
* @return true
*/
public function clearUpgradeTask(int $delayed = 0) {
if ($delayed) {
Cache::set($this->cache_key, $this->upgrade_task, $delayed);
} else {
Cache::set($this->cache_key, null);
}
return true;
}
/**
* 获取插件定义的package目录
* @param string $addon
* @return string
*/
public function geAddonPackagePath(string $addon)
{
return root_path() . 'addon' .DIRECTORY_SEPARATOR . $addon . DIRECTORY_SEPARATOR . 'package' . DIRECTORY_SEPARATOR;
}
}

View File

@ -33,7 +33,6 @@ class WeappVersionService extends BaseAdminService
/**
* 添加小程序版本
* @param array $data
* @return mixed
*/
public function add(array $data)
{

View File

@ -271,7 +271,6 @@ class LoginService extends BaseApiService
* 重置密码
* @param string $mobile
* @param string $password
* @return null
*/
public function resetPassword(string $mobile, string $password){
$member_service = new MemberService();

View File

@ -68,7 +68,7 @@ class MemberCashOutAccountService extends BaseApiService
/**
* 添加提现账号
* @param array $data
* @return void
* @return int
*/
public function add(array $data)
{

View File

@ -36,7 +36,6 @@ class MemberService extends BaseApiService
/**
* 新增会员
* @return void
*/
public function add(array $data){
$data['site_id'] = $this->site_id;

View File

@ -78,7 +78,7 @@ class CoreAddonBaseService extends BaseCoreService
* 读取json文件转化成数组返回
* @param $json_file_path //json文件目录
*/
protected function jsonFileToArray(string $json_file_path)
public function jsonFileToArray(string $json_file_path)
{
if (file_exists($json_file_path)) {
$content_json = @file_get_contents($json_file_path);
@ -93,7 +93,7 @@ class CoreAddonBaseService extends BaseCoreService
* @param string $file_path
* @return true
*/
protected function writeArrayToJsonFile(array $content, string $file_path)
public function writeArrayToJsonFile(array $content, string $file_path)
{
$content_json = json_encode($content, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
$content_json = preg_replace('/\[\]/', '{}', $content_json);

View File

@ -278,9 +278,60 @@ class CoreAddonCloudService extends CoreCloudBaseService
'token' => $action_token['data']['token'] ?? ''
];
// 获取文件大小
$response = (new CloudService())->request('HEAD','cloud/upgrade?' . http_build_query($query), [
'headers' => ['Range' => 'bytes=0-']
]);
$response = (new CloudService())->httpGet('cloud/upgrade?' . http_build_query($query));
$response['token'] = $query['token'];
return $response;
}
/**
* 下载升级文件
* @param string $dir
* @param string $token
* @return void
*/
public function downloadUpgradeFile(string $token, string $dir = '', int $index = -1, $step = 0, $length = 0) {
$query = [
'authorize_code' => $this->auth_code,
'token' => $token
];
$chunk_size = 1 * 1024 * 1024;
if ($index == -1) {
$response = (new CloudService())->request('HEAD','cloud/upgrade/download?' . http_build_query($query), [
'headers' => ['Range' => 'bytes=0-']
]);
$length = $response->getHeader('Content-range');
$length = (int)explode("/", $length[0])[1];
$step = (int)ceil($length / $chunk_size);
$index++;
return compact('token', 'dir', 'index', 'step', 'length');
} else {
$zip_file = $dir . 'upgrade.zip';
$zip_resource = fopen($zip_file, 'a');
if ($index < $step) {
$start = $index * $chunk_size;
$end = ($index + 1) * $chunk_size;
$end = min($end, $length);
$response = (new CloudService())->request('GET','cloud/upgrade/download?' . http_build_query($query), [
'headers' => ['Range' => "bytes={$start}-{$end}"]
]);
fwrite($zip_resource, $response->getBody());
fclose($zip_resource);
$index++;
return compact('token', 'dir', 'index', 'step', 'length');
} else {
$zip = new \ZipArchive();
if ($zip->open($zip_file) === true) {
dir_mkdir($dir . 'code');
$zip->extractTo($dir . 'code');
$zip->close();
}
return true;
}
}
}
}

View File

@ -89,7 +89,7 @@ class CoreAddonDevelopService extends CoreAddonBaseService
'menu' => [
[
'name' => 'admin.php',
'vm' => 'system' . DIRECTORY_SEPARATOR . 'menu.vm'
'vm' => 'system' . DIRECTORY_SEPARATOR . 'admin_menu.vm'
],
[
'name' => 'site.php',

View File

@ -108,9 +108,16 @@ class CoreAddonInstallService extends CoreAddonBaseService
$to_resource_dir = public_path() . 'addon' . DIRECTORY_SEPARATOR . $this->addon . DIRECTORY_SEPARATOR;
if (!is_dir($this->root_path . 'admin' . DIRECTORY_SEPARATOR)) throw new CommonException('ADMIN_DIR_NOT_EXIST');
if (!is_dir($this->root_path . 'web' . DIRECTORY_SEPARATOR)) throw new CommonException('WEB_DIR_NOT_EXIST');
if (!is_dir($this->root_path . 'uni-app' . DIRECTORY_SEPARATOR)) throw new CommonException('UNIAPP_DIR_NOT_EXIST');
try {
if (!is_dir($this->root_path . 'admin' . DIRECTORY_SEPARATOR)) throw new CommonException('ADMIN_DIR_NOT_EXIST');
if (!is_dir($this->root_path . 'web' . DIRECTORY_SEPARATOR)) throw new CommonException('WEB_DIR_NOT_EXIST');
if (!is_dir($this->root_path . 'uni-app' . DIRECTORY_SEPARATOR)) throw new CommonException('UNIAPP_DIR_NOT_EXIST');
} catch (\Exception $e) {
if (strpos($e->getMessage(), 'open basedir') !== false) {
throw new CommonException('OPEN_BASEDIR_ERROR');
}
throw new CommonException($e->getMessage());
}
// 配置文件
$package_path = $this->install_addon_path . 'package' . DIRECTORY_SEPARATOR;

View File

@ -0,0 +1,4 @@
<?php
return [
];

View File

@ -0,0 +1,271 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的saas管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud-admin.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\core\niucloud;
use app\model\addon\Addon;
use app\service\core\addon\CoreAddonDevelopDownloadService;
use app\service\core\addon\CoreAddonInstallService;
use core\base\BaseCoreService;
use core\exception\CommonException;
use core\util\niucloud\CloudService;
use think\facade\Cache;
/**
* 应用管理服务层
*/
class CoreCloudBuildService extends BaseCoreService
{
private $cache_key = 'cloud_build_task';
private $build_task;
public function __construct()
{
parent::__construct();
$this->root_path = project_path();
$this->build_task = Cache::get($this->cache_key);
$this->auth_code = (new CoreNiucloudConfigService())->getNiucloudConfig()['auth_code'] ?? '';
}
/**
* 编译前环境检测
* @return array|array[]
*/
public function buildPreCheck() {
$niucloud_dir = $this->root_path . 'niucloud' . DIRECTORY_SEPARATOR;
$admin_dir = $this->root_path . 'admin' . DIRECTORY_SEPARATOR;
$web_dir = $this->root_path . 'web' . DIRECTORY_SEPARATOR;
$wap_dir = $this->root_path . 'uni-app' . DIRECTORY_SEPARATOR;
try {
if (!is_dir($admin_dir)) throw new CommonException('ADMIN_DIR_NOT_EXIST');
if (!is_dir($web_dir)) throw new CommonException('WEB_DIR_NOT_EXIST');
if (!is_dir($wap_dir)) throw new CommonException('UNIAPP_DIR_NOT_EXIST');
} catch (\Exception $e) {
if (strpos($e->getMessage(), 'open basedir') !== false) {
throw new CommonException('OPEN_BASEDIR_ERROR');
}
throw new CommonException($e->getMessage());
}
$data = [
// 目录检测
'dir' => [
// 要求可读权限
'is_readable' => [],
// 要求可写权限
'is_write' => []
]
];
$data['dir']['is_readable'][] = ['dir' => str_replace(project_path(), '', $niucloud_dir), 'status' => is_readable($niucloud_dir)];
$data['dir']['is_readable'][] = ['dir' => str_replace(project_path(), '', $admin_dir), 'status' => is_readable($admin_dir)];
$data['dir']['is_readable'][] = ['dir' => str_replace(project_path(), '', $web_dir), 'status' => is_readable($web_dir)];
$data['dir']['is_readable'][] = ['dir' => str_replace(project_path(), '', $wap_dir), 'status' => is_readable($wap_dir)];
$data['dir']['is_write'][] = ['dir' => str_replace(project_path(), '', $niucloud_dir), 'status' => is_write($niucloud_dir) ];
$data['dir']['is_write'][] = ['dir' => str_replace(project_path(), '', $admin_dir), 'status' => is_write($admin_dir) ];
$data['dir']['is_write'][] = ['dir' => str_replace(project_path(), '', $web_dir), 'status' => is_write($web_dir) ];
$data['dir']['is_write'][] = ['dir' => str_replace(project_path(), '', $wap_dir), 'status' => is_write($wap_dir) ];
$check_res = array_merge(
array_column($data['dir']['is_readable'], 'status'),
array_column($data['dir']['is_write'], 'status')
);
// 是否通过校验
$data['is_pass'] = !in_array(false, $check_res);
return $data;
}
/**
* 云编译
* @param $addon
* @return void
*/
public function cloudBuild() {
if ($this->build_task) throw new CommonException('CLOUD_BUILD_TASK_EXIST');
// 上传任务key
$task_key = uniqid();
// 此次上传任务临时目录
$temp_dir = runtime_path() . 'backup' . DIRECTORY_SEPARATOR . 'cloud_build' . DIRECTORY_SEPARATOR . $task_key . DIRECTORY_SEPARATOR;
$package_dir = $temp_dir . 'package' . DIRECTORY_SEPARATOR;
dir_mkdir($package_dir);
// 拷贝composer文件
file_put_contents($package_dir . 'composer.json', file_get_contents(root_path() . 'composer.json'));
// 拷贝手机端文件
$wap_is_compile = (new Addon())->where([ ['compile', 'like', '%wap%'] ])->field('id')->findOrEmpty();
if ($wap_is_compile->isEmpty()) {
dir_copy($this->root_path . 'uni-app', $package_dir . 'uni-app', exclude_dirs:['node_modules', 'unpackage', 'dist']);
}
// 拷贝admin端文件
$admin_is_compile = (new Addon())->where([ ['compile', 'like', '%admin%'] ])->field('id')->findOrEmpty();
if ($admin_is_compile->isEmpty()) {
dir_copy($this->root_path . 'admin', $package_dir . 'admin', exclude_dirs:['node_modules', 'dist', '.vscode', '.idea']);
}
// 拷贝web端文件
$web_is_compile = (new Addon())->where([ ['compile', 'like', '%web%'] ])->field('id')->findOrEmpty();
if ($web_is_compile->isEmpty()) {
dir_copy($this->root_path . 'web', $package_dir . 'web', exclude_dirs:['node_modules', '.output', '.nuxt']);
}
$zip_file = $temp_dir . DIRECTORY_SEPARATOR . 'build.zip';
(new CoreAddonDevelopDownloadService(''))->compressToZip($package_dir, $zip_file);
$query = [
'authorize_code' => $this->auth_code,
'timestamp' => time()
];
$response = (new CloudService())->httpPost('cloud/build?' . http_build_query($query), [
'multipart' => [
[
'name' => 'file',
'contents' => fopen($zip_file, 'r'),
'filename' => 'build.zip'
]
],
'timeout' => 50.0
]);
if (isset($response['code']) && $response['code'] == 0) throw new CommonException($response['msg']);
$this->build_task = [
'task_key' => $task_key,
'timestamp' => $query['timestamp']
];
Cache::set($this->cache_key, $this->build_task);
return $this->build_task;
}
/**
* 获取编译任务
* @return mixed
*/
public function getBuildTask() {
return $this->build_task;
}
/**
* 获取编译执行日志
* @return void
*/
public function getBuildLog() {
if (!$this->build_task) return;
$query = [
'authorize_code' => $this->auth_code,
'timestamp' => $this->build_task['timestamp']
];
$build_log = (new CloudService())->httpGet('cloud/get_build_logs?' . http_build_query($query));
if (isset($build_log['data']) && isset($build_log['data'][0]) && is_array($build_log['data'][0])) {
$last = end($build_log['data'][0]);
if ($last['percent'] == 100 && $last['code'] == 1) {
$build_log['data'][0] = $this->buildSuccess($build_log['data'][0]);
}
}
return $build_log;
}
/**
* 编译完成
* @param array $log
* @return array
*/
public function buildSuccess(array $log) {
try {
$query = [
'authorize_code' => $this->auth_code,
'timestamp' => $this->build_task['timestamp']
];
$chunk_size = 1 * 1024 * 1024;
$temp_dir = runtime_path() . 'backup' . DIRECTORY_SEPARATOR . 'cloud_build' . DIRECTORY_SEPARATOR . $this->build_task['task_key'] . DIRECTORY_SEPARATOR;
if (!isset($this->build_task['index'])) {
$response = (new CloudService())->request('HEAD','cloud/build_download?' . http_build_query($query), [
'headers' => ['Range' => 'bytes=0-']
]);
$length = $response->getHeader('Content-range');
$length = (int)explode("/", $length[0])[1];
$step = (int)ceil($length / $chunk_size);
$this->build_task = array_merge($this->build_task, ['step' => $step, 'index' => 0, 'length' => $length]);
Cache::set($this->cache_key, $this->build_task);
} else {
$zip_file = $temp_dir . 'download.zip';
$zip_resource = fopen($zip_file, 'a');
if (($this->build_task['index'] + 1) <= $this->build_task['step']) {
$start = $this->build_task['index'] * $chunk_size;
$end = ($this->build_task['index'] + 1) * $chunk_size;
$end = min($end, $this->build_task['length']);
$response = (new CloudService())->request('GET','cloud/build_download?' . http_build_query($query), [
'headers' => ['Range' => "bytes={$start}-{$end}"]
]);
fwrite($zip_resource, $response->getBody());
fclose($zip_resource);
$this->build_task['index'] += 1;
Cache::set($this->cache_key, $this->build_task);
$log[] = [ 'code' => 1, 'action' => '编译包下载中,已下载' . round($this->build_task['index'] / $this->build_task['step'] * 100) . '%', 'percent' => '100' ];
} else {
// 解压文件
$zip = new \ZipArchive();
if ($zip->open($zip_file) === true) {
dir_mkdir($temp_dir . 'download');
$zip->extractTo($temp_dir . 'download');
$zip->close();
if (is_dir($temp_dir . 'download' . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . 'admin')) {
del_target_dir(public_path() .'admin', true);
}
if (is_dir($temp_dir . 'download' . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . 'web')) {
del_target_dir(public_path() .'web', true);
}
if (is_dir($temp_dir . 'download' . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . 'wap')) {
del_target_dir(public_path() .'wap', true);
}
dir_copy($temp_dir . 'download', root_path());
// 处理编译之后的文件
(new CoreAddonInstallService(''))->handleBuildFile();
$this->clearTask();
} else {
$log[] = [ 'code' => 0, 'msg' => '编译包解压失败', 'action' => '编译包解压', 'percent' => '100' ];
}
}
}
} catch (\Exception $e) {
$log[] = [ 'code' => 0, 'msg' => $e->getMessage(), 'action' => '', 'percent' => '100' ];
$this->clearTask();
}
return $log;
}
/**
* 清除任务
* @return void
*/
public function clearTask() {
if (!$this->build_task) return;
$temp_dir = runtime_path() . 'backup' . DIRECTORY_SEPARATOR . 'cloud_build' . DIRECTORY_SEPARATOR . $this->build_task['task_key'] . DIRECTORY_SEPARATOR;;
@del_target_dir($temp_dir, true);
Cache::set($this->cache_key, null);
}
}

View File

@ -85,6 +85,16 @@ class CoreModuleService extends BaseNiucloudClient
return $this->httpGet('member_app_action/'.$action, $data);
}
/**
* 获取升级内容
* @param $data
* @return array|\core\util\niucloud\Response|object|ResponseInterface
* @throws GuzzleException
*/
public function getUpgradeContent($data) {
return $this->httpGet('member_app_upgrade/content', $data);
}
/**
* 校验key是否被占用
* @param $key
@ -104,4 +114,13 @@ class CoreModuleService extends BaseNiucloudClient
public function getFrameworkLastVersion() {
return $this->httpGet('store/framework/lastversion', ['product_key' => self::PRODUCT])['data'] ?? false;
}
/**
* 获取框架版本更新记录
* @return false|mixed
* @throws GuzzleException
*/
public function getFrameworkVersionList() {
return $this->httpGet('store/framework/version', ['product_key' => self::PRODUCT])['data'] ?? false;
}
}

View File

@ -41,6 +41,6 @@ class NoticeService extends BaseCoreService
$template = (new CoreNoticeService())->getInfo($site_id, $key);
if(empty($template)) return false;
return Notice::invoke(['site_id' => $site_id, 'key' => $key, 'data' => $data, 'template' => $template], is_async:$template['async']);
return Notice::dispatch(['site_id' => $site_id, 'key' => $key, 'data' => $data, 'template' => $template], is_async:$template['async']);
}
}

View File

@ -232,7 +232,7 @@ class CorePayService extends BaseCoreService
'channel' => $channel
]
);
PayReturnTo::invoke(['site_id' => $site_id, 'out_trade_no' => $out_trade_no], secs: 15);
PayReturnTo::dispatch(['site_id' => $site_id, 'out_trade_no' => $out_trade_no], secs: 15);
}
return $pay_result;
}

View File

@ -117,7 +117,6 @@ class CoreOfflineService extends BaseCoreService
* @param int $site_id
* @param string $out_trade_no
* @param string $reason
* @return void
*/
public function refuse(int $site_id, string $out_trade_no, string $reason = '') {
$pay = (new CorePayService())->findPayInfoByOutTradeNo($site_id, $out_trade_no);

View File

@ -1,103 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的saas管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud-admin.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\core\upgrade;
use app\dict\addon\AddonDict;
use core\util\DbBackup;
/**
* 框架及插件升级备份
* @package app\service\core\upgrade
*/
class CoreBackupService extends CoreUpgradeService
{
/**
* 备份代码
* @return void
*/
public function backupCode() {
$backup_dir = $this->upgrade_dir .$this->upgrade_task['key'] . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR . 'code' . DIRECTORY_SEPARATOR;
// 创建目录
dir_mkdir($backup_dir);
// 备份admin
dir_copy($this->root_path . 'admin', $backup_dir . 'admin', exclude_dirs:[ '.vscode', 'node_modules', 'dist']);
// 备份uni-app
dir_copy($this->root_path . 'uni-app', $backup_dir . 'uni-app', exclude_dirs:['node_modules', 'dist']);
// 备份web
dir_copy($this->root_path . 'web', $backup_dir . 'web', exclude_dirs:['node_modules', '.nuxt', '.output']);
// 备份niucloud
$niucloud_dir = $backup_dir . 'niucloud' . DIRECTORY_SEPARATOR;
if ($this->upgrade_task['upgrade']['app_key'] == AddonDict::FRAMEWORK_KEY) {
dir_copy($this->root_path . 'niucloud', $niucloud_dir, exclude_dirs:['addon', 'config', 'public', 'vendor', 'runtime']);
// 备份版本文件
$version_file = $this->root_path . 'niucloud' .DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'version.php';
$to_version_file = $niucloud_dir . 'config' . DIRECTORY_SEPARATOR . 'version.php';
file_copy($version_file, $to_version_file);
} else {
$addon = $this->upgrade_task['upgrade']['app_key'];
$addon_dir = $this->root_path . 'niucloud' . DIRECTORY_SEPARATOR . 'addon' . DIRECTORY_SEPARATOR . $addon;
$to_addon_dir = $niucloud_dir . 'addon' . DIRECTORY_SEPARATOR . $addon;
dir_copy($addon_dir, $to_addon_dir);
}
// 备份前端文件
if (is_dir(public_path() . 'admin')) {
dir_copy(public_path() . 'admin', $niucloud_dir . 'public' . DIRECTORY_SEPARATOR . 'admin');
}
if (is_dir(public_path() . 'wap')) {
dir_copy(public_path() . 'wap', $niucloud_dir . 'public' . DIRECTORY_SEPARATOR . 'wap');
}
if (is_dir(public_path() . 'web')) {
dir_copy(public_path() . 'web', $niucloud_dir . 'public' . DIRECTORY_SEPARATOR . 'web');
}
return true;
}
/**
* 备份数据库
* @return void
*/
public function backupSql() {
$backup_dir = $this->upgrade_dir .$this->upgrade_task['key'] . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR . 'sql' . DIRECTORY_SEPARATOR;
// 创建目录
dir_mkdir($backup_dir);
$db = new DbBackup([
'path' => $backup_dir,//数据库备份路径
'part' => 1048576,//数据库备份卷大小
'compress' => 0,//数据库备份文件是否启用压缩 0不压缩 1 压缩
'level' => 9 //数据库备份文件压缩级别 1普通 4 一般 9最高
]);
$prefix = config('database.connections.'.config('database.default'))['prefix'];
if ($this->upgrade_task['upgrade']['app_key'] == AddonDict::FRAMEWORK_KEY) {
// 不需要备份的表
$noot_need_backup = ["{$prefix}sys_user_log", "{$prefix}jobs", "{$prefix}jobs_failed"];
$tables = array_diff(array_column($db->dataList(), 'name'), $noot_need_backup);
} else {
$tables = [];
$table_prefix = "{$prefix}{$this->upgrade_task['upgrade']['app_key']}";
foreach ($db->dataList() as $table) {
if (strpos($table['name'], $table_prefix) === 0) {
$tables[] = $table['name'];
}
}
}
foreach ($tables as $table) {
$db->setFile()->backup($table);
}
return true;
}
}

View File

@ -1,59 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的saas管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud-admin.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\core\upgrade;
use core\util\DbBackup;
/**
* 框架及插件升级恢复备份
* @package app\service\core\upgrade
*/
class CoreRestoreService extends CoreUpgradeService
{
/**
* 恢复代码备份
* @return true
*/
public function restoreCode() {
$backup_dir = $this->upgrade_dir .$this->upgrade_task['key'] . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR . 'code' . DIRECTORY_SEPARATOR;
if (is_dir($backup_dir)) {
// 删除前端文件
if (is_dir(public_path() . 'admin')) del_target_dir(public_path() . 'admin', true);
if (is_dir(public_path() . 'wap')) del_target_dir(public_path() . 'wap', true);
if (is_dir(public_path() . 'web')) del_target_dir(public_path() . 'web', true);
dir_copy($backup_dir, $this->root_path);
}
return true;
}
/**
* 恢复数据库备份
* @return true
*/
public function restoreSql() {
$backup_dir = $this->upgrade_dir .$this->upgrade_task['key'] . DIRECTORY_SEPARATOR . 'backup' . DIRECTORY_SEPARATOR . 'sql' . DIRECTORY_SEPARATOR;
if (is_dir($backup_dir)) {
$db = new DbBackup([
'path' => $backup_dir //数据库备份路径
]);
$file_list = $db->fileList();
if (!empty($file_list)) {
foreach ($file_list as $file) {
$db->setFile($file)->import(0, $file['time']);
}
}
}
return true;
}
}

View File

@ -1,164 +0,0 @@
<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的saas管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud-admin.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\core\upgrade;
use app\dict\addon\AddonDict;
use app\model\addon\Addon;
use app\service\core\addon\CoreAddonCloudService;
use core\base\BaseCoreService;
use core\exception\CommonException;
use core\util\niucloud\BaseNiucloudClient;
use think\facade\Cache;
/**
* 框架及插件升级
* @package app\service\core\upgrade
*/
class CoreUpgradeService extends BaseCoreService
{
protected $upgrade_dir;
protected $root_path;
protected $cache_key = 'upgrade';
protected $upgrade_task = null;
private $steps = [
'downloadFile' => ['step' => 'downloadFile', 'title' => '下载更新文件'],
'backupCode' => ['step' => 'backupCode', 'title' => '备份源码'],
'backupSql' => ['step' => 'backupSql', 'title' => '备份数据库'],
'coverCode' => ['step' => 'coverCode', 'title' => '合并更新文件'],
'executeUpgrade' => ['step' => 'executeUpgrade', 'title' => '执行升级方法']
];
public function __construct()
{
parent::__construct();
$this->root_path = dirname(root_path()) . DIRECTORY_SEPARATOR;
$this->upgrade_dir = $this->root_path . 'upgrade' . DIRECTORY_SEPARATOR;
$this->upgrade_task = Cache::get($this->cache_key);
}
/**
* 升级
* @param $addon
* @return array
*/
public function upgrade(string $addon = '') {
// if ($this->upgrade_task) throw new CommonException('UPGRADE_TASK_EXIST');
$upgrade = [
'product_key' => BaseNiucloudClient::PRODUCT
];
if (!$addon) {
$upgrade['app_key'] = AddonDict::FRAMEWORK_KEY;
$upgrade['version'] = config('version.version');
} else {
$upgrade['app_key'] = $addon;
$upgrade['version'] = (new Addon())->where([ ['key', '=', $addon] ])->value('version');
}
$response = (new CoreAddonCloudService())->upgradeAddon($upgrade);
if (isset($response['code']) && $response['code'] == 0) throw new CommonException($response['msg']);
try {
$key = uniqid();
$upgrade_dir = $this->upgrade_dir . $key . DIRECTORY_SEPARATOR;
if (!is_dir($upgrade_dir)) {
dir_mkdir($upgrade_dir);
}
$upgrade_tsak = [
'key' => $key,
'upgrade' => $upgrade,
'step' => 'downloadFile',
'executed' => ['downloadFile'],
'log' => [ $this->steps['downloadFile']['title'] ]
];
Cache::set($this->cache_key, $upgrade_tsak);
return $upgrade_tsak;
} catch (\Exception $e) {
if (strpos($e->getMessage(), 'open_basedir') !== false) {
throw new CommonException('OPEN_BASEDIR_ERROR');
}
throw new CommonException($e->getMessage());
}
}
/**
* 执行升级
* @return true
*/
public function execute() {
$steps = array_keys($this->steps);
$index = array_search($this->upgrade_task['step'], $steps);
$step = $steps[ $index + 1 ] ?? '';
if ($step) {
try {
$this->$step();
$this->upgrade_task['step'] = $step;
$this->upgrade_task['executed'][] = $step;
} catch (\Exception $e) {
$this->upgrade_task['error'] = $e->getMessage();
}
Cache::set($this->cache_key, $this->upgrade_task);
return true;
} else {
return true;
}
}
/**
* 备份源码
* @return true
*/
public function backupCode() {
(new CoreBackupService())->backupCode();
return true;
}
/**
* 备份数据库
* @return true
*/
public function backupSql() {
(new CoreBackupService())->backupSql();
return true;
}
public function coverCode() {
}
public function executeUpgrade() {
}
/**
* 升级出错之后的处理
* @return true|void
*/
public function upgradeErrorHandle() {
try {
$restore_service = (new CoreRestoreService());
$restore_service->restoreCode();
$restore_service->restoreSql();
return true;
} catch (\Exception $e) {
return true;
}
}
}

View File

@ -47,7 +47,6 @@ class CoreWeappCloudService extends CoreCloudBaseService
/**
* 上传小程序
* @param $addon
* @return void
*/
public function uploadWeapp(array $data) {
if (!request()->isSsl()) throw new CommonException('CURR_SITE_IS_NOT_OPEN_SSL');

View File

@ -37,7 +37,6 @@
"qiniu/php-sdk": "v7.4.0",
"qcloud/cos-sdk-v5": "v2.6.2",
"aliyuncs/oss-sdk-php": "v2.6.0",
"tencentcloud/tencentcloud-sdk-php": "v3.0.885",
"alibabacloud/client": "v1.5.32",
"rmccue/requests": "v2.0.5",
"ext-json": "*",
@ -53,7 +52,8 @@
"yunwuxin/think-cron": "v3.0.5",
"nesbot/carbon": "2.66.0",
"ext-zip": "*",
"mjaschen/phpgeo": "4.2.0"
"mjaschen/phpgeo": "4.2.0",
"tencentcloud/sms": "^3.0"
},
"require-dev": {
"symfony/var-dumper": "v4.4.47",

291
niucloud/composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "200584e34d312fccad002d61ce5e76e7",
"content-hash": "83c78a7fc88cb1fc320fd2052a6856c9",
"packages": [
{
"name": "adbario/php-dot-notation",
@ -996,16 +996,16 @@
},
{
"name": "guzzlehttp/psr7",
"version": "2.6.0",
"version": "2.6.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "8bd7c33a0734ae1c5d074360512beb716bef3f77"
"reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/8bd7c33a0734ae1c5d074360512beb716bef3f77",
"reference": "8bd7c33a0734ae1c5d074360512beb716bef3f77",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221",
"reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221",
"shasum": "",
"mirrors": [
{
@ -1025,9 +1025,9 @@
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.1",
"bamarni/composer-bin-plugin": "^1.8.2",
"http-interop/http-factory-tests": "^0.9",
"phpunit/phpunit": "^8.5.29 || ^9.5.23"
"phpunit/phpunit": "^8.5.36 || ^9.6.15"
},
"suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
@ -1098,7 +1098,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/2.6.0"
"source": "https://github.com/guzzle/psr7/tree/2.6.2"
},
"funding": [
{
@ -1114,20 +1114,20 @@
"type": "tidelift"
}
],
"time": "2023-08-03T15:06:02+00:00"
"time": "2023-12-03T20:05:35+00:00"
},
{
"name": "guzzlehttp/uri-template",
"version": "v1.0.1",
"version": "v1.0.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/uri-template.git",
"reference": "b945d74a55a25a949158444f09ec0d3c120d69e2"
"reference": "61bf437fc2197f587f6857d3ff903a24f1731b5d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/uri-template/zipball/b945d74a55a25a949158444f09ec0d3c120d69e2",
"reference": "b945d74a55a25a949158444f09ec0d3c120d69e2",
"url": "https://api.github.com/repos/guzzle/uri-template/zipball/61bf437fc2197f587f6857d3ff903a24f1731b5d",
"reference": "61bf437fc2197f587f6857d3ff903a24f1731b5d",
"shasum": "",
"mirrors": [
{
@ -1141,15 +1141,11 @@
"symfony/polyfill-php80": "^1.17"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.1",
"phpunit/phpunit": "^8.5.19 || ^9.5.8",
"uri-template/tests": "1.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\UriTemplate\\": "src"
@ -1188,7 +1184,7 @@
],
"support": {
"issues": "https://github.com/guzzle/uri-template/issues",
"source": "https://github.com/guzzle/uri-template/tree/v1.0.1"
"source": "https://github.com/guzzle/uri-template/tree/v1.0.2"
},
"funding": [
{
@ -1204,7 +1200,7 @@
"type": "tidelift"
}
],
"time": "2021-10-07T12:57:01+00:00"
"time": "2023-08-27T10:19:19+00:00"
},
{
"name": "intervention/image",
@ -1350,16 +1346,16 @@
},
{
"name": "laravel/serializable-closure",
"version": "v1.3.1",
"version": "v1.3.3",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
"reference": "e5a3057a5591e1cfe8183034b0203921abe2c902"
"reference": "3dbf8a8e914634c48d389c1234552666b3d43754"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/e5a3057a5591e1cfe8183034b0203921abe2c902",
"reference": "e5a3057a5591e1cfe8183034b0203921abe2c902",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3dbf8a8e914634c48d389c1234552666b3d43754",
"reference": "3dbf8a8e914634c48d389c1234552666b3d43754",
"shasum": "",
"mirrors": [
{
@ -1412,7 +1408,7 @@
"issues": "https://github.com/laravel/serializable-closure/issues",
"source": "https://github.com/laravel/serializable-closure"
},
"time": "2023-07-14T13:56:28+00:00"
"time": "2023-11-08T14:08:06+00:00"
},
{
"name": "maennchen/zipstream-php",
@ -1698,16 +1694,16 @@
},
{
"name": "monolog/monolog",
"version": "2.9.1",
"version": "2.9.2",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "f259e2b15fb95494c83f52d3caad003bbf5ffaa1"
"reference": "437cb3628f4cf6042cc10ae97fc2b8472e48ca1f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/f259e2b15fb95494c83f52d3caad003bbf5ffaa1",
"reference": "f259e2b15fb95494c83f52d3caad003bbf5ffaa1",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/437cb3628f4cf6042cc10ae97fc2b8472e48ca1f",
"reference": "437cb3628f4cf6042cc10ae97fc2b8472e48ca1f",
"shasum": "",
"mirrors": [
{
@ -1790,7 +1786,7 @@
],
"support": {
"issues": "https://github.com/Seldaek/monolog/issues",
"source": "https://github.com/Seldaek/monolog/tree/2.9.1"
"source": "https://github.com/Seldaek/monolog/tree/2.9.2"
},
"funding": [
{
@ -1802,20 +1798,20 @@
"type": "tidelift"
}
],
"time": "2023-02-06T13:44:46+00:00"
"time": "2023-10-27T15:25:26+00:00"
},
{
"name": "mtdowling/jmespath.php",
"version": "2.6.1",
"version": "2.7.0",
"source": {
"type": "git",
"url": "https://github.com/jmespath/jmespath.php.git",
"reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb"
"reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/9b87907a81b87bc76d19a7fb2d61e61486ee9edb",
"reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb",
"url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/bbb69a935c2cbb0c03d7f481a238027430f6440b",
"reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b",
"shasum": "",
"mirrors": [
{
@ -1825,12 +1821,12 @@
]
},
"require": {
"php": "^5.4 || ^7.0 || ^8.0",
"php": "^7.2.5 || ^8.0",
"symfony/polyfill-mbstring": "^1.17"
},
"require-dev": {
"composer/xdebug-handler": "^1.4 || ^2.0",
"phpunit/phpunit": "^4.8.36 || ^7.5.15"
"composer/xdebug-handler": "^3.0.3",
"phpunit/phpunit": "^8.5.33"
},
"bin": [
"bin/jp.php"
@ -1838,7 +1834,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.6-dev"
"dev-master": "2.7-dev"
}
},
"autoload": {
@ -1854,6 +1850,11 @@
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
@ -1867,22 +1868,22 @@
],
"support": {
"issues": "https://github.com/jmespath/jmespath.php/issues",
"source": "https://github.com/jmespath/jmespath.php/tree/2.6.1"
"source": "https://github.com/jmespath/jmespath.php/tree/2.7.0"
},
"time": "2021-06-14T00:11:39+00:00"
"time": "2023-08-25T10:54:48+00:00"
},
{
"name": "myclabs/php-enum",
"version": "1.8.3",
"version": "1.8.4",
"source": {
"type": "git",
"url": "https://github.com/myclabs/php-enum.git",
"reference": "b942d263c641ddb5190929ff840c68f78713e937"
"reference": "a867478eae49c9f59ece437ae7f9506bfaa27483"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/php-enum/zipball/b942d263c641ddb5190929ff840c68f78713e937",
"reference": "b942d263c641ddb5190929ff840c68f78713e937",
"url": "https://api.github.com/repos/myclabs/php-enum/zipball/a867478eae49c9f59ece437ae7f9506bfaa27483",
"reference": "a867478eae49c9f59ece437ae7f9506bfaa27483",
"shasum": "",
"mirrors": [
{
@ -1904,7 +1905,10 @@
"autoload": {
"psr-4": {
"MyCLabs\\Enum\\": "src/"
}
},
"classmap": [
"stubs/Stringable.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -1923,7 +1927,7 @@
],
"support": {
"issues": "https://github.com/myclabs/php-enum/issues",
"source": "https://github.com/myclabs/php-enum/tree/1.8.3"
"source": "https://github.com/myclabs/php-enum/tree/1.8.4"
},
"funding": [
{
@ -1935,7 +1939,7 @@
"type": "tidelift"
}
],
"time": "2021-07-05T08:18:36+00:00"
"time": "2022-08-04T09:53:51+00:00"
},
{
"name": "nesbot/carbon",
@ -2047,16 +2051,16 @@
},
{
"name": "overtrue/socialite",
"version": "4.8.1",
"version": "4.9.0",
"source": {
"type": "git",
"url": "https://github.com/overtrue/socialite.git",
"reference": "470b781f288fbb24c8b105cfdada215d83d84d4b"
"reference": "dcbb1eed948fe036e6de8cdf0b125f5af1bc73fb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/overtrue/socialite/zipball/470b781f288fbb24c8b105cfdada215d83d84d4b",
"reference": "470b781f288fbb24c8b105cfdada215d83d84d4b",
"url": "https://api.github.com/repos/overtrue/socialite/zipball/dcbb1eed948fe036e6de8cdf0b125f5af1bc73fb",
"reference": "dcbb1eed948fe036e6de8cdf0b125f5af1bc73fb",
"shasum": "",
"mirrors": [
{
@ -2114,7 +2118,7 @@
],
"support": {
"issues": "https://github.com/overtrue/socialite/issues",
"source": "https://github.com/overtrue/socialite/tree/4.8.1"
"source": "https://github.com/overtrue/socialite/tree/4.9.0"
},
"funding": [
{
@ -2122,20 +2126,20 @@
"type": "github"
}
],
"time": "2023-07-17T08:56:49+00:00"
"time": "2023-09-01T11:01:34+00:00"
},
{
"name": "php-di/invoker",
"version": "2.3.3",
"version": "2.3.4",
"source": {
"type": "git",
"url": "https://github.com/PHP-DI/Invoker.git",
"reference": "cd6d9f267d1a3474bdddf1be1da079f01b942786"
"reference": "33234b32dafa8eb69202f950a1fc92055ed76a86"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/cd6d9f267d1a3474bdddf1be1da079f01b942786",
"reference": "cd6d9f267d1a3474bdddf1be1da079f01b942786",
"url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/33234b32dafa8eb69202f950a1fc92055ed76a86",
"reference": "33234b32dafa8eb69202f950a1fc92055ed76a86",
"shasum": "",
"mirrors": [
{
@ -2175,7 +2179,7 @@
],
"support": {
"issues": "https://github.com/PHP-DI/Invoker/issues",
"source": "https://github.com/PHP-DI/Invoker/tree/2.3.3"
"source": "https://github.com/PHP-DI/Invoker/tree/2.3.4"
},
"funding": [
{
@ -2183,7 +2187,7 @@
"type": "github"
}
],
"time": "2021-12-13T09:22:56+00:00"
"time": "2023-09-08T09:24:21+00:00"
},
{
"name": "php-di/php-di",
@ -2601,16 +2605,16 @@
},
{
"name": "psr/http-client",
"version": "1.0.1",
"version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-client.git",
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
"url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
"shasum": "",
"mirrors": [
{
@ -2621,7 +2625,7 @@
},
"require": {
"php": "^7.0 || ^8.0",
"psr/http-message": "^1.0"
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"extra": {
@ -2641,7 +2645,7 @@
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP clients",
@ -2653,9 +2657,9 @@
"psr-18"
],
"support": {
"source": "https://github.com/php-fig/http-client/tree/master"
"source": "https://github.com/php-fig/http-client"
},
"time": "2020-06-29T06:28:15+00:00"
"time": "2023-09-23T14:17:50+00:00"
},
{
"name": "psr/http-factory",
@ -3220,16 +3224,16 @@
},
{
"name": "symfony/cache",
"version": "v5.4.25",
"version": "v5.4.32",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache.git",
"reference": "e2013521c0f07473ae69a01fce0af78fc3ec0f23"
"reference": "2553faca77502a4f68dc93cd2f3b9ec650751e40"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/cache/zipball/e2013521c0f07473ae69a01fce0af78fc3ec0f23",
"reference": "e2013521c0f07473ae69a01fce0af78fc3ec0f23",
"url": "https://api.github.com/repos/symfony/cache/zipball/2553faca77502a4f68dc93cd2f3b9ec650751e40",
"reference": "2553faca77502a4f68dc93cd2f3b9ec650751e40",
"shasum": "",
"mirrors": [
{
@ -3263,7 +3267,7 @@
"require-dev": {
"cache/integration-tests": "dev-master",
"doctrine/cache": "^1.6|^2.0",
"doctrine/dbal": "^2.13.1|^3.0",
"doctrine/dbal": "^2.13.1|^3|^4",
"predis/predis": "^1.1",
"psr/simple-cache": "^1.0|^2.0",
"symfony/config": "^4.4|^5.0|^6.0",
@ -3303,7 +3307,7 @@
"psr6"
],
"support": {
"source": "https://github.com/symfony/cache/tree/v5.4.25"
"source": "https://github.com/symfony/cache/tree/v5.4.32"
},
"funding": [
{
@ -3319,7 +3323,7 @@
"type": "tidelift"
}
],
"time": "2023-06-22T08:06:06+00:00"
"time": "2023-11-24T13:04:07+00:00"
},
{
"name": "symfony/cache-contracts",
@ -3736,16 +3740,16 @@
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
"reference": "42292d99c55abe617799667f454222c54c60e229"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
"reference": "42292d99c55abe617799667f454222c54c60e229",
"shasum": "",
"mirrors": [
{
@ -3766,7 +3770,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -3805,7 +3809,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
},
"funding": [
{
@ -3821,20 +3825,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-07-28T09:04:16+00:00"
},
{
"name": "symfony/polyfill-php73",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php73.git",
"reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9"
"reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9",
"reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fe2f306d1d9d346a7fee353d0d5012e401e984b5",
"reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5",
"shasum": "",
"mirrors": [
{
@ -3849,7 +3853,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -3890,7 +3894,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-php73/tree/v1.28.0"
},
"funding": [
{
@ -3906,20 +3910,20 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
"shasum": "",
"mirrors": [
{
@ -3934,7 +3938,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -3979,7 +3983,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
},
"funding": [
{
@ -3995,7 +3999,7 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/process",
@ -4512,17 +4516,17 @@
"time": "2023-01-13T08:34:10+00:00"
},
{
"name": "tencentcloud/tencentcloud-sdk-php",
"version": "3.0.885",
"name": "tencentcloud/common",
"version": "3.0.1055",
"source": {
"type": "git",
"url": "https://github.com/TencentCloud/tencentcloud-sdk-php.git",
"reference": "0e7905b882c574e1eb305105c4307847d78a0464"
"url": "https://github.com/tencentcloud-sdk-php/common.git",
"reference": "b81fa74542b652b3ab92926d1fcc832b420bddc9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/TencentCloud/tencentcloud-sdk-php/zipball/0e7905b882c574e1eb305105c4307847d78a0464",
"reference": "0e7905b882c574e1eb305105c4307847d78a0464",
"url": "https://api.github.com/repos/tencentcloud-sdk-php/common/zipball/b81fa74542b652b3ab92926d1fcc832b420bddc9",
"reference": "b81fa74542b652b3ab92926d1fcc832b420bddc9",
"shasum": "",
"mirrors": [
{
@ -4532,20 +4536,14 @@
]
},
"require": {
"guzzlehttp/guzzle": "^6.3 || ^7.0",
"guzzlehttp/guzzle": "^6.3||^7.0",
"php": ">=5.6.0"
},
"require-dev": {
"phpunit/phpunit": "^9.5"
},
"type": "library",
"autoload": {
"psr-4": {
"TencentCloud\\": "./src/TencentCloud"
},
"classmap": [
"src/QcloudApi/QcloudApi.php"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@ -4553,19 +4551,68 @@
],
"authors": [
{
"name": "coolli",
"name": "tencentcloudapi",
"email": "tencentcloudapi@tencent.com",
"homepage": "https://cloud.tencent.com/document/sdk/PHP",
"role": "Developer"
}
],
"description": "TencentCloudApi php sdk",
"homepage": "https://github.com/TencentCloud/tencentcloud-sdk-php",
"homepage": "https://github.com/tencentcloud-sdk-php/common",
"support": {
"issues": "https://github.com/TencentCloud/tencentcloud-sdk-php/issues",
"source": "https://github.com/TencentCloud/tencentcloud-sdk-php/tree/3.0.885"
"issues": "https://github.com/tencentcloud-sdk-php/common/issues",
"source": "https://github.com/tencentcloud-sdk-php/common/tree/3.0.1055"
},
"time": "2023-05-16T00:05:17+00:00"
"time": "2023-12-27T20:24:22+00:00"
},
{
"name": "tencentcloud/sms",
"version": "3.0.1055",
"source": {
"type": "git",
"url": "https://github.com/tencentcloud-sdk-php/sms.git",
"reference": "d108e6f95a1e99916f49fa46b376222443f2b9f2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tencentcloud-sdk-php/sms/zipball/d108e6f95a1e99916f49fa46b376222443f2b9f2",
"reference": "d108e6f95a1e99916f49fa46b376222443f2b9f2",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"tencentcloud/common": "3.0.1055"
},
"type": "library",
"autoload": {
"psr-4": {
"TencentCloud\\": "./src/TencentCloud"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "tencentcloudapi",
"email": "tencentcloudapi@tencent.com",
"homepage": "https://github.com/tencentcloud-sdk-php/sms",
"role": "Developer"
}
],
"description": "TencentCloudApi php sdk sms",
"homepage": "https://github.com/tencentcloud-sdk-php/sms",
"support": {
"issues": "https://github.com/tencentcloud-sdk-php/sms/issues",
"source": "https://github.com/tencentcloud-sdk-php/sms/tree/3.0.1055"
},
"time": "2023-12-27T20:47:08+00:00"
},
{
"name": "topthink/framework",
@ -5453,16 +5500,16 @@
"packages-dev": [
{
"name": "symfony/polyfill-php72",
"version": "v1.27.0",
"version": "v1.28.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "869329b1e9894268a8a61dabb69153029b7a8c97"
"reference": "70f4aebd92afca2f865444d30a4d2151c13c3179"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97",
"reference": "869329b1e9894268a8a61dabb69153029b7a8c97",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179",
"reference": "70f4aebd92afca2f865444d30a4d2151c13c3179",
"shasum": "",
"mirrors": [
{
@ -5477,7 +5524,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.27-dev"
"dev-main": "1.28-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -5515,7 +5562,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0"
"source": "https://github.com/symfony/polyfill-php72/tree/v1.28.0"
},
"funding": [
{
@ -5531,7 +5578,7 @@
"type": "tidelift"
}
],
"time": "2022-11-03T14:55:06+00:00"
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/var-dumper",

View File

@ -1,6 +1,6 @@
<?php
return [
'version' => '0.0.6',
'code' => '202312220001'
'version' => '0.0.7',
'code' => '202401010001'
];

View File

@ -58,8 +58,6 @@ abstract class BaseService
* @param string $field
* @param string $order
* @param array $append
* @param null $with //数组可以是数组 function($query) use ($with){$query->with($with);}
* @param null $each //闭包匿名函数 function($item, $key){$item['nickname'] = 'think';return $item;}
* @return array
* @throws DbException
*/
@ -86,7 +84,6 @@ abstract class BaseService
/**
* 分页数据查询传入model查询后结果
* @param $model BaseModel
* @param null $each
* @return array
* @throws DbException
*/
@ -112,8 +109,6 @@ abstract class BaseService
* @param string $field
* @param string $order
* @param array $append
* @param null $with
* @param null $each
* @return array
* @throws DbException
*/

View File

@ -28,7 +28,7 @@ class Dispatch
* @param bool $is_async
* @return mixed
*/
public static function invoke($action, array $data = [], int $secs = 0, string $queue_name = null, bool $is_async = true)
public static function dispatch($action, array $data = [], int $secs = 0, string $queue_name = null, bool $is_async = true)
{
$class = static::class;//调用主调类
if ($is_async) {

View File

@ -0,0 +1,525 @@
<?php
namespace core\util;
use think\facade\Db;
class DbBackup
{
/**
* 文件指针
* @var resource
*/
private $fp;
/**
* 备份文件信息 part - 卷号name - 文件名
* @var array
*/
private $file;
/**
* 当前打开文件大小
* @var integer
*/
private $size = 0;
/**
* 数据库配置
* @var integer
*/
private $dbconfig = array();
/**
* 备份配置
* @var integer
*/
private $config = array(
// 数据库备份路径
'path' => './backup/',
// 数据库备份卷大小
'part' => 20971520,
// 数据库备份文件是否启用压缩 0不压缩 1 压缩
'compress' => 0,
// 数据库备份文件压缩级别 1普通 4 一般 9最高
'level' => 9,
);
/**
* 数据库备份构造方法
* @param array $file 备份或还原的文件信息
* @param array $config 备份配置信息
*/
public function __construct($config = [])
{
$this->config = is_array($config) && !empty($config) ? array_merge($this->config, $config) : $this->config;
//初始化文件名
$this->setFile();
//初始化数据库连接参数
$this->setDbConn();
//检查文件是否可写
if (!$this->checkPath($this->config['path'])) {
throw new \Exception("The current directory is not writable");
}
}
/**
* 设置脚本运行超时时间
* 0表示不限制,支持连贯操作
*/
public function setTimeout($time = null)
{
if (!is_null($time)) {
set_time_limit($time) || ini_set("max_execution_time", $time);
}
return $this;
}
/**
* 设置数据库连接必备参数
* @param array $dbconfig 数据库连接配置信息
* @return object
*/
public function setDbConn($dbconfig = [])
{
if (empty($dbconfig)) {
$this->dbconfig = config('database.connections.'.config('database.default'));
} else {
$this->dbconfig = $dbconfig;
}
return $this;
}
/**
* 设置备份文件名
*
* @param Array $file 文件名字
* @return object
*/
public function setFile($file = null)
{
if (is_null($file)) {
$this->file = ['name' => date('Ymd-His'), 'part' => 1];
} else {
if (!array_key_exists("name", $file) && !array_key_exists("part", $file)) {
$this->file = $file['1'];
} else {
$this->file = $file;
}
}
return $this;
}
/**
* 数据库表列表
*
* @param null $table
* @param int $type
* @return array
*/
public function dataList($table = null, $type = 1)
{
if (is_null($table)) {
$list = Db::query("SHOW TABLE STATUS");
} else {
if ($type) {
$list = Db::query("SHOW FULL COLUMNS FROM {$table}");
} else {
$list = Db::query("show columns from {$table}");
}
}
return array_map('array_change_key_case', $list);
}
/**
* 数据库备份文件列表
*
* @return array
*/
public function fileList()
{
if (!is_dir($this->config['path'])) {
mkdir($this->config['path'], 0755, true);
}
$path = realpath($this->config['path']);
$flag = \FilesystemIterator::KEY_AS_FILENAME;
$glob = new \FilesystemIterator($path, $flag);
$list = array();
foreach ($glob as $name => $file) {
if (preg_match('/^\\d{8,8}-\\d{6,6}-\\d+\\.sql(?:\\.gz)?$/', $name)) {
$name1 = $name;
$name = sscanf($name, '%4s%2s%2s-%2s%2s%2s-%d');
$date = "{$name[0]}-{$name[1]}-{$name[2]}";
$time = "{$name[3]}:{$name[4]}:{$name[5]}";
$part = $name[6];
if (isset($list["{$date} {$time}"])) {
$info = $list["{$date} {$time}"];
$info['part'] = max($info['part'], $part);
$info['size'] = $info['size'] + $file->getSize();
} else {
$info['part'] = $part;
$info['size'] = $file->getSize();
}
$extension = strtoupper(pathinfo($file->getFilename(), PATHINFO_EXTENSION));
$info['name'] = $name1;
$info['compress'] = $extension === 'SQL' ? '-' : $extension;
$info['time'] = strtotime("{$date} {$time}");
$list["{$date} {$time}"] = $info;
}
}
return $list;
}
/**
* 获取文件名称
*
* @param string $type
* @param int $time
* @return array|false|mixed|string
* @throws \Exception
*/
public function getFile($type = '', $time = 0)
{
//
if (!is_numeric($time)) {
throw new \Exception("{$time} Illegal data type");
}
switch ($type) {
case 'time':
$name = date('Ymd-His', $time).'-*.sql*';
$path = realpath($this->config['path']).DIRECTORY_SEPARATOR.$name;
return glob($path);
break;
case 'timeverif':
$name = date('Ymd-His', $time).'-*.sql*';
$path = realpath($this->config['path']).DIRECTORY_SEPARATOR.$name;
$files = glob($path);
$list = array();
foreach ($files as $name) {
$basename = basename($name);
$match = sscanf($basename, '%4s%2s%2s-%2s%2s%2s-%d');
$gz = preg_match('/^\\d{8,8}-\\d{6,6}-\\d+\\.sql.gz$/', $basename);
$list[$match[6]] = array($match[6], $name, $gz);
}
$last = end($list);
if (count($list) === $last[0]) {
return $list;
} else {
throw new \Exception("File {$files['0']} may be damaged, please check again");
}
break;
case 'pathname':
return "{$this->config['path']}{$this->file['name']}-{$this->file['part']}.sql";
break;
case 'filename':
return "{$this->file['name']}-{$this->file['part']}.sql";
break;
case 'filepath':
return $this->config['path'];
break;
default:
$arr = array(
'pathname' => "{$this->config['path']}{$this->file['name']}-{$this->file['part']}.sql",
'filename' => "{$this->file['name']}-{$this->file['part']}.sql",
'filepath' => $this->config['path'], 'file' => $this->file
);
return $arr;
}
}
/**
* 删除备份文件
*
* @param $time
* @return mixed
* @throws \Exception
*/
public function delFile($time)
{
if ($time) {
$file = $this->getFile('time', $time);
array_map("unlink", $file);
$file = $this->getFile('time', $time);
if (count($file)) {
throw new \Exception("File ".implode('##', $file)." deleted failed");
} else {
return $time;
}
} else {
throw new \Exception("{$time} Time parameter is incorrect");
}
}
/**
* 下载备份
*
* @param string $time
* @param integer $part
* @return array|mixed|string
*/
public function downloadFile($time, $part = 0)
{
$file = $this->getFile('time', $time);
$fileName = $file[$part];
if (file_exists($fileName)) {
ob_end_clean();
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Length: '.filesize($fileName));
header('Content-Disposition: attachment; filename='.basename($fileName));
readfile($fileName);
} else {
throw new \Exception("{$time} File is abnormal");
}
}
/**
* 导入表
*
* @param $start
* @param $time
* @return array|false|int
* @throws Exception
*/
public function import($start, $time)
{
//还原数据
$this->file = $this->getFile('time', $time);
if ($this->config['compress']) {
$gz = gzopen($this->file[0], 'r');
$size = 0;
} else {
$size = filesize($this->file[0]);
$gz = fopen($this->file[0], 'r');
}
$sql = '';
if ($start) {
$this->config['compress'] ? gzseek($gz, $start) : fseek($gz, $start);
}
for ($i = 0; $i < 1000; $i++) {
$sql .= $this->config['compress'] ? gzgets($gz) : fgets($gz);
if (preg_match('/.*;$/', trim($sql))) {
if (false !== Db::query($sql)) {
$start += strlen($sql);
} else {
return false;
}
$sql = '';
} elseif ($this->config['compress'] ? gzeof($gz) : feof($gz)) {
return 0;
}
}
return array($start, $size);
}
/**
* 写入初始数据
*
* @return boolean true - 写入成功false - 写入失败
*/
public function backupInit()
{
$sql = "-- -----------------------------\n";
$sql .= "-- Think MySQL Data Transfer \n";
$sql .= "-- \n";
$sql .= "-- Host : ".$this->dbconfig['hostname']."\n";
$sql .= "-- Port : ".$this->dbconfig['hostport']."\n";
$sql .= "-- Database : ".$this->dbconfig['database']."\n";
$sql .= "-- \n";
$sql .= "-- Part : #{$this->file['part']}\n";
$sql .= "-- Date : ".date("Y-m-d H:i:s")."\n";
$sql .= "-- -----------------------------\n\n";
$sql .= "SET FOREIGN_KEY_CHECKS = 0;\n\n";
return $this->write($sql);
}
/**
* 查询单条
* @param $sql
* @return array|mixed
*/
public function selectOne($sql) {
$result = Db::query($sql);
return $result[0] ?? [];
}
/**
* 备份表结构
*
* @param string $table 表名
* @param integer $start 起始行数
* @return boolean false - 备份失败
*/
public function backup($table, $start = 0)
{
// 备份表结构
if (0 == $start) {
$result = $this->selectOne("SHOW CREATE TABLE `{$table}`");
$sql = "\n";
$sql .= "-- -----------------------------\n";
$sql .= "-- Table structure for `{$table}`\n";
$sql .= "-- -----------------------------\n";
$sql .= "DROP TABLE IF EXISTS `{$table}`;\n";
$sql .= trim($result['Create Table']).";\n\n";
if (false === $this->write($sql)) {
return false;
}
}
//数据总数
$result = $this->selectOne("SELECT COUNT(*) AS count FROM `{$table}`");
$count = $result['count'];
//备份表数据
if ($count) {
//写入数据注释
if (0 == $start) {
$sql = "-- -----------------------------\n";
$sql .= "-- Records of `{$table}`\n";
$sql .= "-- -----------------------------\n";
$this->write($sql);
}
//备份数据记录
$result = Db::query("SELECT * FROM `{$table}` LIMIT {$start}, 1000");
$sql = "INSERT INTO `{$table}` VALUES\n";
foreach ($result as $index => $row) {
$row = array_map('addslashes', $row);
$sql .= "('".str_replace(array("\r", "\n"), array('\\r', '\\n'),
implode("', '", $row))."')";
$sql .= $index < (count($result) - 1) ? ",\n" : ";\n";
}
if (false === $this->write($sql)) {
return false;
}
//还有更多数据
if ($count > $start + 1000) {
return $this->backup($table, $start + 1000);
}
}
//备份下一表
return true;
}
/**
* 优化表
*
* @param String $tables 表名
* @return String $tables
*/
public function optimize($tables = null)
{
if ($tables) {
if (is_array($tables)) {
$tables = implode('`,`', $tables);
$list = db ::select("OPTIMIZE TABLE `{$tables}`");
} else {
$list = Db::query("OPTIMIZE TABLE `{$tables}`");
}
if ($list) {
return $tables;
} else {
throw new \Exception("data sheet'{$tables}'Repair mistakes please try again!");
}
} else {
throw new \Exception("Please specify the table to be repaired!");
}
}
/**
* 修复表
*
* @param String $tables 表名
* @return String $tables
*/
public function repair($tables = null)
{
if ($tables) {
if (is_array($tables)) {
$tables = implode('`,`', $tables);
$list = Db::query("REPAIR TABLE `{$tables}`");
} else {
$list = Db::query("REPAIR TABLE `{$tables}`");
}
if ($list) {
return $list;
} else {
throw new \Exception("data sheet'{$tables}'Repair mistakes please try again!");
}
} else {
throw new \Exception("Please specify the table to be repaired!");
}
}
/**
* 写入SQL语句
*
* @param string $sql 要写入的SQL语句
* @return boolean true - 写入成功false - 写入失败!
*/
private function write($sql)
{
$size = strlen($sql);
//由于压缩原因无法计算出压缩后的长度这里假设压缩率为50%
//一般情况压缩率都会高于50%
$size = $this->config['compress'] ? $size / 2 : $size;
$this->open($size);
return $this->config['compress'] ? @gzwrite($this->fp, $sql) : @fwrite($this->fp, $sql);
}
/**
* 打开一个卷,用于写入数据
*
* @param integer $size 写入数据的大小
*/
private function open($size)
{
if ($this->fp) {
$this->size += $size;
if ($this->size > $this->config['part']) {
$this->config['compress'] ? @gzclose($this->fp) : @fclose($this->fp);
$this->fp = null;
$this->file['part']++;
session('backup_file', $this->file);
$this->backupInit();
}
} else {
$backuppath = $this->config['path'];
$filename = "{$backuppath}{$this->file['name']}-{$this->file['part']}.sql";
if ($this->config['compress']) {
$filename = "{$filename}.gz";
$this->fp = @gzopen($filename, "a{$this->config['level']}");
} else {
$this->fp = @fopen($filename, 'a');
}
$this->size = filesize($filename) + $size;
}
}
/**
* 检查目录是否可写
*
* @param string $path 目录
* @return boolean
*/
protected function checkPath($path)
{
if (is_dir($path)) {
return true;
}
if (mkdir($path, 0755, true)) {
return true;
} else {
return false;
}
}
/**
* 析构方法,用于关闭文件资源
*/
public function __destruct()
{
if ($this->fp) {
$this->config['compress'] ? @gzclose($this->fp) : @fclose($this->fp);
}
}
}

View File

@ -0,0 +1 @@
import{d,r as l,o as i,c as p,a as t,b as u,e as x,w as m,u as v,f,E as h,p as g,g as I,h as w,i as S,t as b,_ as B}from"./index-9d601459.js";/* empty css */const k=""+new URL("error-da01d378.png",import.meta.url).href,o=e=>(g("data-v-d967cd15"),e=e(),I(),e),y={class:"error"},C={class:"flex items-center"},E=o(()=>t("div",null,[t("img",{class:"w-[300px]",src:k})],-1)),N={class:"text-left ml-[100px]"},R=o(()=>t("div",{class:"error-text text-[28px] font-bold"},"404错误",-1)),U=o(()=>t("div",{class:"text-[#222] text-[20px] mt-[15px]"},"哎呀,出错了!您访问的页面不存在...",-1)),V=o(()=>t("div",{class:"text-[#c4c2c2] text-[12px] mt-[5px]"},"尝试检查URL的错误然后点击浏览器刷新按钮。",-1)),L={class:"mt-[40px]"},$=d({__name:"404",setup(e){let s=null;const a=l(5),c=f();return s=setInterval(()=>{a.value===0?(clearInterval(s),c.go(-1)):a.value--},1e3),i(()=>{s&&clearInterval(s)}),(r,n)=>{const _=h;return w(),p("div",y,[t("div",C,[u(r.$slots,"content",{},()=>[E],!0),t("div",N,[R,U,V,t("div",L,[x(_,{class:"bottom",onClick:n[0]||(n[0]=D=>v(c).go(-1))},{default:m(()=>[S(b(a.value)+" 秒后返回上一页",1)]),_:1})])])])])}}});const q=B($,[["__scopeId","data-v-d967cd15"]]);export{q as default};

View File

@ -1 +0,0 @@
import{d,r as l,S as i,f as p,g as t,h as m,y as u,x,u as v,b9 as f,ba as h,e as g,A as I,B as b}from"./base-d79f9f62.js";/* empty css */import{b as S}from"./vue-router-fc35ac55.js";import{E as B}from"./index-03649f16.js";import{_ as w}from"./_plugin-vue_export-helper-c27b6911.js";import"./index-2048a34f.js";import"./index-bdfee32a.js";import"./index-427f5a83.js";import"./index-71aec1df.js";const y=""+new URL("error-da01d378.png",import.meta.url).href,s=e=>(f("data-v-d967cd15"),e=e(),h(),e),k={class:"error"},C={class:"flex items-center"},E=s(()=>t("div",null,[t("img",{class:"w-[300px]",src:y})],-1)),N={class:"text-left ml-[100px]"},R=s(()=>t("div",{class:"error-text text-[28px] font-bold"},"404错误",-1)),U=s(()=>t("div",{class:"text-[#222] text-[20px] mt-[15px]"},"哎呀,出错了!您访问的页面不存在...",-1)),V=s(()=>t("div",{class:"text-[#c4c2c2] text-[12px] mt-[5px]"},"尝试检查URL的错误然后点击浏览器刷新按钮。",-1)),L={class:"mt-[40px]"},$=d({__name:"404",setup(e){let o=null;const r=l(5),a=S();return o=setInterval(()=>{r.value===0?(clearInterval(o),a.go(-1)):r.value--},1e3),i(()=>{o&&clearInterval(o)}),(n,c)=>{const _=B;return g(),p("div",k,[t("div",C,[m(n.$slots,"content",{},()=>[E],!0),t("div",N,[R,U,V,t("div",L,[u(_,{class:"bottom",onClick:c[0]||(c[0]=A=>v(a).go(-1))},{default:x(()=>[I(b(r.value)+" 秒后返回上一页",1)]),_:1})])])])])}}});const K=w($,[["__scopeId","data-v-d967cd15"]]);export{K as default};

View File

@ -1 +0,0 @@
import{b4 as l,b1 as c,aZ as K,aK as _,aV as d,b5 as L,aE as k,aD as R,b6 as w,b7 as y,b2 as W,b8 as u}from"./base-d79f9f62.js";var q=l(c,"WeakMap");const j=q;var N=9007199254740991;function z(t){return typeof t=="number"&&t>-1&&t%1==0&&t<=N}function Z(t){return t!=null&&z(t.length)&&!K(t)}var H=Object.prototype;function X(t){var r=t&&t.constructor,e=typeof r=="function"&&r.prototype||H;return t===e}function Y(t,r){for(var e=-1,n=Array(t);++e<t;)n[e]=r(e);return n}var J="[object Arguments]";function $(t){return _(t)&&d(t)==J}var V=Object.prototype,Q=V.hasOwnProperty,tt=V.propertyIsEnumerable,et=$(function(){return arguments}())?$:function(t){return _(t)&&Q.call(t,"callee")&&!tt.call(t,"callee")};const rt=et;function at(){return!1}var D=typeof exports=="object"&&exports&&!exports.nodeType&&exports,x=D&&typeof module=="object"&&module&&!module.nodeType&&module,nt=x&&x.exports===D,S=nt?c.Buffer:void 0,ot=S?S.isBuffer:void 0,st=ot||at;const it=st;var ct="[object Arguments]",ut="[object Array]",pt="[object Boolean]",ft="[object Date]",gt="[object Error]",bt="[object Function]",yt="[object Map]",lt="[object Number]",dt="[object Object]",ht="[object RegExp]",Tt="[object Set]",jt="[object String]",vt="[object WeakMap]",mt="[object ArrayBuffer]",At="[object DataView]",_t="[object Float32Array]",wt="[object Float64Array]",$t="[object Int8Array]",xt="[object Int16Array]",St="[object Int32Array]",Ot="[object Uint8Array]",Pt="[object Uint8ClampedArray]",Et="[object Uint16Array]",It="[object Uint32Array]",a={};a[_t]=a[wt]=a[$t]=a[xt]=a[St]=a[Ot]=a[Pt]=a[Et]=a[It]=!0;a[ct]=a[ut]=a[mt]=a[pt]=a[At]=a[ft]=a[gt]=a[bt]=a[yt]=a[lt]=a[dt]=a[ht]=a[Tt]=a[jt]=a[vt]=!1;function Mt(t){return _(t)&&z(t.length)&&!!a[d(t)]}function Ct(t){return function(r){return t(r)}}var F=typeof exports=="object"&&exports&&!exports.nodeType&&exports,g=F&&typeof module=="object"&&module&&!module.nodeType&&module,Bt=g&&g.exports===F,T=Bt&&L.process,Ut=function(){try{var t=g&&g.require&&g.require("util").types;return t||T&&T.binding&&T.binding("util")}catch{}}();const O=Ut;var P=O&&O.isTypedArray,kt=P?Ct(P):Mt;const zt=kt;var Vt=Object.prototype,Dt=Vt.hasOwnProperty;function Ft(t,r){var e=k(t),n=!e&&rt(t),s=!e&&!n&&it(t),p=!e&&!n&&!s&&zt(t),f=e||n||s||p,h=f?Y(t.length,String):[],G=h.length;for(var o in t)(r||Dt.call(t,o))&&!(f&&(o=="length"||s&&(o=="offset"||o=="parent")||p&&(o=="buffer"||o=="byteLength"||o=="byteOffset")||R(o,G)))&&h.push(o);return h}function Gt(t,r){return function(e){return t(r(e))}}var Kt=Gt(Object.keys,Object);const Lt=Kt;var Rt=Object.prototype,Wt=Rt.hasOwnProperty;function qt(t){if(!X(t))return Lt(t);var r=[];for(var e in Object(t))Wt.call(t,e)&&e!="constructor"&&r.push(e);return r}function Nt(t){return Z(t)?Ft(t):qt(t)}function Zt(t,r){for(var e=-1,n=r.length,s=t.length;++e<n;)t[s+e]=r[e];return t}function Ht(){this.__data__=new w,this.size=0}function Xt(t){var r=this.__data__,e=r.delete(t);return this.size=r.size,e}function Yt(t){return this.__data__.get(t)}function Jt(t){return this.__data__.has(t)}var Qt=200;function te(t,r){var e=this.__data__;if(e instanceof w){var n=e.__data__;if(!y||n.length<Qt-1)return n.push([t,r]),this.size=++e.size,this;e=this.__data__=new W(n)}return e.set(t,r),this.size=e.size,this}function b(t){var r=this.__data__=new w(t);this.size=r.size}b.prototype.clear=Ht;b.prototype.delete=Xt;b.prototype.get=Yt;b.prototype.has=Jt;b.prototype.set=te;function ee(t,r){for(var e=-1,n=t==null?0:t.length,s=0,p=[];++e<n;){var f=t[e];r(f,e,t)&&(p[s++]=f)}return p}function re(){return[]}var ae=Object.prototype,ne=ae.propertyIsEnumerable,E=Object.getOwnPropertySymbols,oe=E?function(t){return t==null?[]:(t=Object(t),ee(E(t),function(r){return ne.call(t,r)}))}:re;const se=oe;function ie(t,r,e){var n=r(t);return k(t)?n:Zt(n,e(t))}function je(t){return ie(t,Nt,se)}var ce=l(c,"DataView");const v=ce;var ue=l(c,"Promise");const m=ue;var pe=l(c,"Set");const A=pe;var I="[object Map]",fe="[object Object]",M="[object Promise]",C="[object Set]",B="[object WeakMap]",U="[object DataView]",ge=u(v),be=u(y),ye=u(m),le=u(A),de=u(j),i=d;(v&&i(new v(new ArrayBuffer(1)))!=U||y&&i(new y)!=I||m&&i(m.resolve())!=M||A&&i(new A)!=C||j&&i(new j)!=B)&&(i=function(t){var r=d(t),e=r==fe?t.constructor:void 0,n=e?u(e):"";if(n)switch(n){case ge:return U;case be:return I;case ye:return M;case le:return C;case de:return B}return r});const ve=i;var he=c.Uint8Array;const me=he;export{A as S,me as U,rt as a,Zt as b,ie as c,ve as d,Ct as e,it as f,se as g,b as h,z as i,je as j,Nt as k,Z as l,zt as m,O as n,X as o,Ft as p,Gt as q,re as s};

View File

@ -1 +0,0 @@
import{aL as p,aY as O,aM as y,b1 as x}from"./base-d79f9f62.js";import{o as v,l as m,p as w,q as P,U as u}from"./_Uint8Array-bbbfd6ac.js";var i=Object.create,b=function(){function e(){}return function(n){if(!p(n))return{};if(i)return i(n);e.prototype=n;var r=new e;return e.prototype=void 0,r}}();const A=b;function q(e,n){var r=-1,t=e.length;for(n||(n=Array(t));++r<t;)n[r]=e[r];return n}function N(e,n,r,t){var g=!r;r||(r={});for(var a=-1,h=n.length;++a<h;){var o=n[a],s=t?t(r[o],e[o],o,r,e):void 0;s===void 0&&(s=e[o]),g?O(r,o,s):y(r,o,s)}return r}function C(e){var n=[];if(e!=null)for(var r in Object(e))n.push(r);return n}var L=Object.prototype,U=L.hasOwnProperty;function I(e){if(!p(e))return C(e);var n=v(e),r=[];for(var t in e)t=="constructor"&&(n||!U.call(e,t))||r.push(t);return r}function Y(e){return m(e)?w(e,!0):I(e)}var K=P(Object.getPrototypeOf,Object);const T=K;var d=typeof exports=="object"&&exports&&!exports.nodeType&&exports,f=d&&typeof module=="object"&&module&&!module.nodeType&&module,B=f&&f.exports===d,l=B?x.Buffer:void 0,c=l?l.allocUnsafe:void 0;function D(e,n){if(n)return e.slice();var r=e.length,t=c?c(r):new e.constructor(r);return e.copy(t),t}function E(e){var n=new e.constructor(e.byteLength);return new u(n).set(new u(e)),n}function F(e,n){var r=n?E(e.buffer):e.buffer;return new e.constructor(r,e.byteOffset,e.length)}function G(e){return typeof e.constructor=="function"&&!v(e)?A(T(e)):{}}export{E as a,F as b,N as c,q as d,D as e,T as g,G as i,Y as k};

View File

@ -1 +0,0 @@
import{aL as i,aD as t,aX as f}from"./base-d79f9f62.js";import{l as n}from"./_Uint8Array-bbbfd6ac.js";function p(e,a,r){if(!i(r))return!1;var s=typeof a;return(s=="number"?n(r)&&t(a,r.length):s=="string"&&a in r)?f(r[a],e):!1}export{p as i};

View File

@ -1 +0,0 @@
const s=(t,r)=>{const o=t.__vccOpts||t;for(const[c,e]of r)o[c]=e;return o};export{s as _};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
import{d as j,f as I,r as v,v as S,a1 as N,h as T,c as q,a as e,t as o,u as n,N as a,e as s,w as t,aI as w,i as b,R as B,aJ as z,aK as F,a2 as R,E as $,al as M,am as D,an as J,V as K,ao as P,_ as Q}from"./index-9d601459.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import{g as U}from"./wechat-8f6959e9.js";const W={class:"w-full p-5 bg-white"},G={class:"flex justify-between items-center mb-[20px]"},H={class:"text-[20px]"},L={class:"p-[20px]"},O={class:"text-[16px] mb-[20px]"},X={class:"text-[14px] text-[#303133] font-[700]"},Y={class:"text-[#999]"},Z={class:"mt-[20px] mb-[40px] h-[32px]"},ee={class:"text-[14px] text-[#303133] font-[700]"},te={class:"text-[#999]"},se={class:"mt-[20px] mb-[40px] h-[32px]"},ne={class:"text-[14px] text-[#303133] font-[700]"},ae={class:"text-[#999]"},oe={class:"mt-[20px] mb-[40px] h-[32px]"},le={class:"flex justify-center"},ce={class:"w-[100%] h-[100%] flex items-center justify-center bg-[#f5f7fa]"},ie={class:"mt-[22px] text-center"},pe={class:"text-[14px] text-[#303133] font-[700]"},de=j({__name:"access",setup(_e){const _=I(),r=v("/channel/wechat"),c=v(2),p=v("");S(async()=>{const i=await U();p.value=i.data.qr_code});const y=i=>{window.open(i,"_blank")},k=i=>{_.push({path:r.value})};return(i,l)=>{const u=z,C=F,x=N("CircleCheckFilled"),f=R,m=$,h=M,E=D,g=J,A=K,V=P;return T(),q("div",W,[e("div",G,[e("span",H,o(n(a)("title")),1)]),s(C,{modelValue:r.value,"onUpdate:modelValue":l[0]||(l[0]=d=>r.value=d),class:"demo-tabs",onTabChange:k},{default:t(()=>[s(u,{label:n(a)("wechatAccessFlow"),name:"/channel/wechat"},null,8,["label"]),s(u,{label:n(a)("customMenu"),name:"/channel/wechat/menu"},null,8,["label"]),s(u,{label:n(a)("wechatTemplate"),name:"/channel/wechat/message"},null,8,["label"])]),_:1},8,["modelValue"]),e("div",L,[e("p",O,o(n(a)("wechatInlet")),1),s(V,null,{default:t(()=>[s(g,{span:20},{default:t(()=>[s(E,{direction:"vertical"},{default:t(()=>[s(h,null,w({title:t(()=>[e("p",X,o(n(a)("wechatAttestation")),1)]),description:t(()=>[e("span",Y,o(n(a)("wechatAttestation1")),1),e("div",Z,[s(m,{type:"primary",onClick:l[1]||(l[1]=d=>y("https://mp.weixin.qq.com/"))},{default:t(()=>[b(o(n(a)("clickAccess")),1)]),_:1})])]),_:2},[c.value>1?{name:"icon",fn:t(()=>[s(f,{size:"25px",class:"text-color"},{default:t(()=>[s(x)]),_:1})]),key:"0"}:c.value==1?{name:"icon",fn:t(()=>[e("div",{class:"w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center"},[e("div",{class:"h-[12px] w-[12px] bg-color rounded-full"})])]),key:"1"}:{name:"icon",fn:t(()=>[e("div",{class:"w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full"}," 1")]),key:"2"}]),1024),s(h,null,w({title:t(()=>[e("p",ee,o(n(a)("wechatSetting")),1)]),description:t(()=>[e("span",te,o(n(a)("wechatSetting1")),1),e("div",se,[s(m,{type:"primary",plain:"",onClick:l[2]||(l[2]=d=>n(_).push("/channel/wechat/config"))},{default:t(()=>[b(o(n(a)("settingInfo")),1)]),_:1})])]),_:2},[c.value>2?{name:"icon",fn:t(()=>[s(f,{size:"25px"},{default:t(()=>[s(x)]),_:1})]),key:"0"}:c.value==2?{name:"icon",fn:t(()=>[e("div",{class:"w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center"},[e("div",{class:"h-[12px] w-[12px] bg-color rounded-full"})])]),key:"1"}:{name:"icon",fn:t(()=>[e("div",{class:"w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full"}," 2")]),key:"2"}]),1024),s(h,null,w({title:t(()=>[e("p",ne,o(n(a)("wechatAccess")),1)]),description:t(()=>[e("span",ae,o(n(a)("wechatAccess")),1),e("div",oe,[s(m,{type:"primary",plain:"",onClick:l[3]||(l[3]=d=>n(_).push("/channel/wechat/course"))},{default:t(()=>[b(o(n(a)("releaseCourse")),1)]),_:1})])]),_:2},[c.value>3?{name:"icon",fn:t(()=>[s(f,{size:"25px"},{default:t(()=>[s(x)]),_:1})]),key:"0"}:c.value==3?{name:"icon",fn:t(()=>[e("div",{class:"w-[24px] h-[24px] box-border rounded-full bg-color1 flex items-center justify-center"},[e("div",{class:"h-[12px] w-[12px] bg-color rounded-full"})])]),key:"1"}:{name:"icon",fn:t(()=>[e("div",{class:"w-[24px] h-[24px] text-[#fff] bg-[#778aa3] text-center leading-[24px] rounded-full"}," 3")]),key:"2"}]),1024)]),_:1})]),_:1}),s(g,{span:4},{default:t(()=>[e("div",le,[s(A,{class:"w-[180px] h-[180px]",src:p.value?n(B)(p.value):""},{error:t(()=>[e("div",ce,[e("span",null,o(p.value?n(a)("fileErr"):n(a)("emptyQrCode")),1)])]),_:1},8,["src"])]),e("div",ie,[e("p",pe,o(n(a)("clickAccess2")),1)])]),_:1})]),_:1})])])}}});const ge=Q(de,[["__scopeId","data-v-594351e7"]]);export{ge as default};

Some files were not shown because too many files have changed in this diff Show More