mirror of
https://github.com/certd/certd.git
synced 2026-04-28 07:57:25 +08:00
perf: 支持迁移个人数据到企业项目中
This commit is contained in:
@@ -127,6 +127,21 @@ export const certdResources = [
|
|||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "certd.sysResources.currentProject",
|
||||||
|
name: "ProjectMemberManager",
|
||||||
|
path: "/certd/project/detail",
|
||||||
|
component: "/certd/project/detail/index.vue",
|
||||||
|
meta: {
|
||||||
|
show: () => {
|
||||||
|
const projectStore = useProjectStore();
|
||||||
|
return projectStore.isEnterprise;
|
||||||
|
},
|
||||||
|
isMenu: true,
|
||||||
|
icon: "ion:apps",
|
||||||
|
auth: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "certd.settings",
|
title: "certd.settings",
|
||||||
name: "MineSetting",
|
name: "MineSetting",
|
||||||
@@ -263,21 +278,6 @@ export const certdResources = [
|
|||||||
isMenu: false,
|
isMenu: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: "certd.sysResources.projectMemberManager",
|
|
||||||
name: "ProjectMemberManager",
|
|
||||||
path: "/certd/project/detail",
|
|
||||||
component: "/certd/project/detail/index.vue",
|
|
||||||
meta: {
|
|
||||||
show: () => {
|
|
||||||
const projectStore = useProjectStore();
|
|
||||||
return projectStore.isEnterprise;
|
|
||||||
},
|
|
||||||
isMenu: true,
|
|
||||||
icon: "ion:apps",
|
|
||||||
auth: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useFormWrapper } from "@fast-crud/fast-crud";
|
import { useFormWrapper } from "@fast-crud/fast-crud";
|
||||||
|
import { merge } from "lodash-es";
|
||||||
|
|
||||||
export type FormOptionReq = {
|
export type FormOptionReq = {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -7,6 +8,7 @@ export type FormOptionReq = {
|
|||||||
body?: any;
|
body?: any;
|
||||||
initialForm?: any;
|
initialForm?: any;
|
||||||
zIndex?: number;
|
zIndex?: number;
|
||||||
|
wrapper?: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useFormDialog() {
|
export function useFormDialog() {
|
||||||
@@ -14,19 +16,23 @@ export function useFormDialog() {
|
|||||||
|
|
||||||
async function openFormDialog(req: FormOptionReq) {
|
async function openFormDialog(req: FormOptionReq) {
|
||||||
function createCrudOptions() {
|
function createCrudOptions() {
|
||||||
|
const warpper = merge(
|
||||||
|
{
|
||||||
|
zIndex: req.zIndex,
|
||||||
|
title: req.title,
|
||||||
|
saveRemind: false,
|
||||||
|
slots: {
|
||||||
|
"form-body-top": req.body,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
req.wrapper
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
crudOptions: {
|
crudOptions: {
|
||||||
columns: req.columns,
|
columns: req.columns,
|
||||||
form: {
|
form: {
|
||||||
initialForm: req.initialForm,
|
initialForm: req.initialForm,
|
||||||
wrapper: {
|
wrapper: warpper,
|
||||||
zIndex: req.zIndex,
|
|
||||||
title: req.title,
|
|
||||||
saveRemind: false,
|
|
||||||
slots: {
|
|
||||||
"form-body-top": req.body,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
async afterSubmit() {},
|
async afterSubmit() {},
|
||||||
async doSubmit({ form }: any) {
|
async doSubmit({ form }: any) {
|
||||||
if (req.onSubmit) {
|
if (req.onSubmit) {
|
||||||
|
|||||||
@@ -73,3 +73,17 @@ export async function ApproveJoin(form: any) {
|
|||||||
data: form,
|
data: form,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function GetSelfResources() {
|
||||||
|
return await request({
|
||||||
|
url: "/enterprise/transfer/selfResources",
|
||||||
|
method: "post",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function TransferResources() {
|
||||||
|
return await request({
|
||||||
|
url: "/enterprise/transfer/doTransfer",
|
||||||
|
method: "post",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,8 +9,12 @@
|
|||||||
<fs-values-format :model-value="project.permission" :dict="projectPermissionDict"></fs-values-format>
|
<fs-values-format :model-value="project.permission" :dict="projectPermissionDict"></fs-values-format>
|
||||||
<a-divider type="vertical"></a-divider>
|
<a-divider type="vertical"></a-divider>
|
||||||
<fs-values-format :model-value="project.status" :dict="projectMemberStatusDict"></fs-values-format> -->
|
<fs-values-format :model-value="project.status" :dict="projectMemberStatusDict"></fs-values-format> -->
|
||||||
|
<a-divider type="vertical"></a-divider>
|
||||||
|
<a-button class="mr-5" type="primary" @click="openTransferDialog">个人数据迁移</a-button>
|
||||||
|
<a-button v-if="userStore.isAdmin" type="primary" @click="goProjectManager">{{ t("certd.project.projectManage") }}</a-button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="more"></div>
|
||||||
</template>
|
</template>
|
||||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||||
<template #pagination-left>
|
<template #pagination-left>
|
||||||
@@ -29,11 +33,13 @@ import createCrudOptions from "./crud";
|
|||||||
import { message, Modal } from "ant-design-vue";
|
import { message, Modal } from "ant-design-vue";
|
||||||
import { DeleteBatch } from "./api";
|
import { DeleteBatch } from "./api";
|
||||||
import { useI18n } from "/src/locales";
|
import { useI18n } from "/src/locales";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { useProjectStore } from "/@/store/project";
|
import { useProjectStore } from "/@/store/project";
|
||||||
import { request } from "/@/api/service";
|
import { request } from "/@/api/service";
|
||||||
import { useDicts } from "../../dicts";
|
import { useDicts } from "../../dicts";
|
||||||
import { useCrudPermission } from "/@/plugin/permission";
|
import { useCrudPermission } from "/@/plugin/permission";
|
||||||
|
import { useUserStore } from "/@/store/user";
|
||||||
|
import { useTransfer } from "./use";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -49,6 +55,14 @@ if (!projectId) {
|
|||||||
projectId = projectStore.currentProject?.id;
|
projectId = projectStore.currentProject?.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
function goProjectManager() {
|
||||||
|
router.push(`/sys/enterprise/project`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { openTransferDialog } = useTransfer();
|
||||||
|
|
||||||
const { projectPermissionDict, projectMemberStatusDict, userDict } = useDicts();
|
const { projectPermissionDict, projectMemberStatusDict, userDict } = useDicts();
|
||||||
|
|
||||||
const project: Ref<any> = ref({});
|
const project: Ref<any> = ref({});
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
import { dict } from "@fast-crud/fast-crud";
|
|
||||||
import { useDicts } from "../../dicts";
|
|
||||||
import { useFormDialog } from "/@/use/use-dialog";
|
|
||||||
|
|
||||||
export function useApprove() {
|
|
||||||
const { openFormDialog } = useFormDialog();
|
|
||||||
const { projectPermissionDict, projectMemberStatusDict, userDict } = useDicts();
|
|
||||||
function openApproveDialog({ id, permission, onSubmit }: { id: any; permission: any; onSubmit: any }) {
|
|
||||||
openFormDialog({
|
|
||||||
title: "审批加入申请",
|
|
||||||
columns: {
|
|
||||||
permission: {
|
|
||||||
title: "成员权限",
|
|
||||||
type: "dict-select",
|
|
||||||
dict: projectPermissionDict,
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
title: "审批结果",
|
|
||||||
type: "dict-radio",
|
|
||||||
dict: dict({
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
label: "通过",
|
|
||||||
value: "approved",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "拒绝",
|
|
||||||
value: "rejected",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onSubmit: onSubmit,
|
|
||||||
initialForm: {
|
|
||||||
id: id,
|
|
||||||
permission: permission,
|
|
||||||
status: "approved",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
openApproveDialog,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
import { dict } from "@fast-crud/fast-crud";
|
||||||
|
import { useDicts } from "../../dicts";
|
||||||
|
import { useFormDialog } from "/@/use/use-dialog";
|
||||||
|
import * as api from "./api";
|
||||||
|
import { useProjectStore } from "/@/store/project";
|
||||||
|
import { message, Modal } from "ant-design-vue";
|
||||||
|
import { Ref, ref } from "vue";
|
||||||
|
export function useApprove() {
|
||||||
|
const { openFormDialog } = useFormDialog();
|
||||||
|
const { projectPermissionDict, projectMemberStatusDict, userDict } = useDicts();
|
||||||
|
function openApproveDialog({ id, permission, onSubmit }: { id: any; permission: any; onSubmit: any }) {
|
||||||
|
openFormDialog({
|
||||||
|
title: "审批加入申请",
|
||||||
|
columns: {
|
||||||
|
permission: {
|
||||||
|
title: "成员权限",
|
||||||
|
type: "dict-select",
|
||||||
|
dict: projectPermissionDict,
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
title: "审批结果",
|
||||||
|
type: "dict-radio",
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
label: "通过",
|
||||||
|
value: "approved",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "拒绝",
|
||||||
|
value: "rejected",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onSubmit: onSubmit,
|
||||||
|
initialForm: {
|
||||||
|
id: id,
|
||||||
|
permission: permission,
|
||||||
|
status: "approved",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
openApproveDialog,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useTransfer() {
|
||||||
|
const { openFormDialog } = useFormDialog();
|
||||||
|
|
||||||
|
async function doTransfer() {
|
||||||
|
Modal.confirm({
|
||||||
|
title: "请确认",
|
||||||
|
content: () => (
|
||||||
|
<div>
|
||||||
|
<p>确认迁移个人资源数据到当前项目?</p>
|
||||||
|
<p class="text-red-500">注意;此操作不可逆,一旦迁移,数据将无法还原回个人用户名下。</p>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
okText: "确认",
|
||||||
|
okType: "primary",
|
||||||
|
onOk: async () => {
|
||||||
|
await api.TransferResources();
|
||||||
|
message.success("迁移成功");
|
||||||
|
await loadMyResources();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const selfResources: Ref = ref({});
|
||||||
|
|
||||||
|
const projectStore = useProjectStore();
|
||||||
|
|
||||||
|
async function loadMyResources() {
|
||||||
|
selfResources.value = await api.GetSelfResources();
|
||||||
|
}
|
||||||
|
async function openTransferDialog() {
|
||||||
|
await loadMyResources();
|
||||||
|
openFormDialog({
|
||||||
|
title: "迁移我的个人资源到当前企业项目",
|
||||||
|
wrapper: {
|
||||||
|
buttons: {
|
||||||
|
ok: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
reset: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
body() {
|
||||||
|
return (
|
||||||
|
<div class="p-8">
|
||||||
|
<div class="flex flex-row items-center justify-evenly w-full">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg font-bold">我原有的个人资源数量</h3>
|
||||||
|
<div class="mt-4">
|
||||||
|
<p>流水线:{selfResources.value.pipeline}</p>
|
||||||
|
<p>证书:{selfResources.value.certInfo}</p>
|
||||||
|
<p>授权:{selfResources.value.access}</p>
|
||||||
|
<p>站点监控:{selfResources.value.siteMonitor}</p>
|
||||||
|
<p>通知:{selfResources.value.notification}</p>
|
||||||
|
<p>站点监控分组:{selfResources.value.group}</p>
|
||||||
|
<p>流水线分组:{selfResources.value.pipelineGroup}</p>
|
||||||
|
<p>流水线模版:{selfResources.value.template}</p>
|
||||||
|
<p>域名:{selfResources.value.domain}</p>
|
||||||
|
<p>子域名托管:{selfResources.value.subdomain}</p>
|
||||||
|
<p>cname记录:{selfResources.value.cnameRecord}</p>
|
||||||
|
<p>其他:{selfResources.value.addon}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-2xl font-bold"> 迁移到→ </div>
|
||||||
|
<div>项目:"{projectStore.currentProject?.name}"</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row items-center justify-center w-full">
|
||||||
|
<a-button type="primary" onClick={doTransfer}>
|
||||||
|
确认迁移
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
openTransferDialog,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { BaseController, Constants } from '@certd/lib-server';
|
||||||
|
import { Controller, Inject, Post, Provide } from '@midwayjs/core';
|
||||||
|
import { TransferService } from '../../../modules/sys/enterprise/service/transfer-service.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
@Controller('/api/enterprise/transfer')
|
||||||
|
export class TransferController extends BaseController {
|
||||||
|
@Inject()
|
||||||
|
service: TransferService;
|
||||||
|
|
||||||
|
|
||||||
|
getService(): TransferService {
|
||||||
|
return this.service;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 我自己的资源
|
||||||
|
* @param body
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
@Post('/selfResources', { summary: Constants.per.authOnly })
|
||||||
|
async selfResources() {
|
||||||
|
const userId = this.getUserId();
|
||||||
|
const res = await this.service.getUserResources(userId);
|
||||||
|
return this.ok(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 迁移项目
|
||||||
|
* @param body
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
@Post('/doTransfer', { summary: Constants.per.authOnly })
|
||||||
|
async doTransfer() {
|
||||||
|
const {projectId} = await this.getProjectUserIdRead();
|
||||||
|
const userId = this.getUserId();
|
||||||
|
await this.service.transferAll(userId,projectId);
|
||||||
|
return this.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import { isEnterprise } from "@certd/lib-server";
|
||||||
|
import { ApplicationContext, IMidwayContainer, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||||
|
|
||||||
|
@Provide()
|
||||||
|
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||||
|
export class TransferService {
|
||||||
|
@ApplicationContext()
|
||||||
|
appCtx: IMidwayContainer;
|
||||||
|
|
||||||
|
|
||||||
|
async getServices() {
|
||||||
|
const getService = async (key: string) => {
|
||||||
|
return await this.appCtx.getAsync(key);
|
||||||
|
}
|
||||||
|
const serviceNames = [
|
||||||
|
"pipeline",
|
||||||
|
"certInfo",
|
||||||
|
"siteInfo",
|
||||||
|
"domain",
|
||||||
|
"cnameRecord",
|
||||||
|
"group",
|
||||||
|
"pipelineGroup",
|
||||||
|
"notification",
|
||||||
|
"subDomain",
|
||||||
|
"template",
|
||||||
|
"openKey",
|
||||||
|
"siteIp",
|
||||||
|
"access",
|
||||||
|
]
|
||||||
|
|
||||||
|
const services: any = {}
|
||||||
|
for (const key of serviceNames) {
|
||||||
|
services[key] = await getService(`${key}Service`);
|
||||||
|
}
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户资源
|
||||||
|
* @param userId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async getUserResources(userId: number) {
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
userId,
|
||||||
|
}
|
||||||
|
|
||||||
|
const services = await this.getServices();
|
||||||
|
|
||||||
|
const counts: any = {}
|
||||||
|
let totalCount = 0;
|
||||||
|
for (const key of Object.keys(services)) {
|
||||||
|
const count = await services[key].repository.count({ where: query });
|
||||||
|
counts[key] = count;
|
||||||
|
totalCount += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
...counts,
|
||||||
|
totalCount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async transferAll(userId: number, projectId: number) {
|
||||||
|
|
||||||
|
if (!isEnterprise()) {
|
||||||
|
throw new Error('当前非企业模式,不支持资源迁移到项目');
|
||||||
|
}
|
||||||
|
if (projectId === 0) {
|
||||||
|
throw new Error('项目ID不能为0');
|
||||||
|
}
|
||||||
|
if (userId == null) {
|
||||||
|
throw new Error('用户ID不能为空');
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
userId,
|
||||||
|
}
|
||||||
|
const services = await this.getServices();
|
||||||
|
for (const key of Object.keys(services)) {
|
||||||
|
await services[key].repository.update(query, {
|
||||||
|
userId: -1,
|
||||||
|
projectId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user