mirror of https://github.com/certd/certd
perf: 同一时间只允许一个套餐生效
parent
7f596ed315
commit
8ebf95a222
|
@ -102,6 +102,7 @@ export class Executor {
|
||||||
await this.notification("success");
|
await this.notification("success");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
await this.notification("error", e);
|
await this.notification("error", e);
|
||||||
this.logger.error("pipeline 执行失败", e);
|
this.logger.error("pipeline 执行失败", e);
|
||||||
|
|
|
@ -8,14 +8,21 @@
|
||||||
<a-col :span="24">
|
<a-col :span="24">
|
||||||
<a-card>
|
<a-card>
|
||||||
<div class="suite-intro-box">
|
<div class="suite-intro-box">
|
||||||
<div>套餐说明:多个套餐内的数量可以叠加</div>
|
<div>说明:① 同一时间只有最新购买的一个套餐生效;② 可以购买多个加量包,加量包立即生效;③ 套餐和加量包内的数量可以叠加</div>
|
||||||
<div v-if="suiteIntro" v-html="suiteIntro"></div>
|
<div v-if="suiteIntro" v-html="suiteIntro"></div>
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
<h3>套餐</h3>
|
||||||
<a-row :gutter="8" class="mt-10">
|
<a-row :gutter="8" class="mt-10">
|
||||||
<a-col v-for="item of products" :key="item.id" class="mb-10 suite-card-col">
|
<a-col v-for="item of suites" :key="item.id" class="mb-10 suite-card-col">
|
||||||
|
<product-info :product="item" @order="doOrder" />
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<h3>加量包</h3>
|
||||||
|
<a-row :gutter="8" class="mt-10">
|
||||||
|
<a-col v-for="item of addons" :key="item.id" class="mb-10 suite-card-col">
|
||||||
<product-info :product="item" @order="doOrder" />
|
<product-info :product="item" @order="doOrder" />
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
@ -31,10 +38,13 @@ import * as api from "./api";
|
||||||
import ProductInfo from "/@/views/certd/suite/product-info.vue";
|
import ProductInfo from "/@/views/certd/suite/product-info.vue";
|
||||||
import OrderModal from "/@/views/certd/suite/order-modal.vue";
|
import OrderModal from "/@/views/certd/suite/order-modal.vue";
|
||||||
|
|
||||||
const products = ref([]);
|
const suites = ref([]);
|
||||||
|
const addons = ref([]);
|
||||||
|
|
||||||
async function loadProducts() {
|
async function loadProducts() {
|
||||||
products.value = await api.ProductList();
|
const list = await api.ProductList();
|
||||||
|
suites.value = list.filter((x: any) => x.type === "suite");
|
||||||
|
addons.value = list.filter((x: any) => x.type === "addone");
|
||||||
}
|
}
|
||||||
|
|
||||||
loadProducts();
|
loadProducts();
|
||||||
|
|
|
@ -1,54 +1,71 @@
|
||||||
import { request } from "/src/api/service";
|
import { request } from "/src/api/service";
|
||||||
|
|
||||||
export function createApi() {
|
const apiPrefix = "/mine/suite";
|
||||||
const apiPrefix = "/mine/suite";
|
|
||||||
return {
|
|
||||||
async GetList(query: any) {
|
|
||||||
return await request({
|
|
||||||
url: apiPrefix + "/page",
|
|
||||||
method: "post",
|
|
||||||
data: query
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async AddObj(obj: any) {
|
export type SuiteValue = {
|
||||||
return await request({
|
max: number;
|
||||||
url: apiPrefix + "/add",
|
used: number;
|
||||||
method: "post",
|
};
|
||||||
data: obj
|
export type SuiteDetail = {
|
||||||
});
|
enabled?: boolean;
|
||||||
},
|
suites?: any[];
|
||||||
|
expiresTime?: number;
|
||||||
|
pipelineCount?: SuiteValue;
|
||||||
|
domainCount?: SuiteValue;
|
||||||
|
deployCount?: SuiteValue;
|
||||||
|
monitorCount?: SuiteValue;
|
||||||
|
};
|
||||||
|
|
||||||
async UpdateObj(obj: any) {
|
export default {
|
||||||
return await request({
|
async GetList(query: any) {
|
||||||
url: apiPrefix + "/update",
|
return await request({
|
||||||
method: "post",
|
url: apiPrefix + "/page",
|
||||||
data: obj
|
method: "post",
|
||||||
});
|
data: query
|
||||||
},
|
});
|
||||||
|
},
|
||||||
|
|
||||||
async DelObj(id: number) {
|
async AddObj(obj: any) {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/delete",
|
url: apiPrefix + "/add",
|
||||||
method: "post",
|
method: "post",
|
||||||
params: { id }
|
data: obj
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async GetObj(id: number) {
|
async UpdateObj(obj: any) {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/info",
|
url: apiPrefix + "/update",
|
||||||
method: "post",
|
method: "post",
|
||||||
params: { id }
|
data: obj
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async ListAll() {
|
|
||||||
return await request({
|
|
||||||
url: apiPrefix + "/all",
|
|
||||||
method: "post"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const pipelineGroupApi = createApi();
|
async DelObj(id: number) {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/delete",
|
||||||
|
method: "post",
|
||||||
|
params: { id }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async GetObj(id: number) {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/info",
|
||||||
|
method: "post",
|
||||||
|
params: { id }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async ListAll() {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/all",
|
||||||
|
method: "post"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async SuiteDetailGet() {
|
||||||
|
return await request({
|
||||||
|
url: `${apiPrefix}/detail`,
|
||||||
|
method: "post"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||||
import { pipelineGroupApi } from "./api";
|
import api from "./api";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import SuiteValueEdit from "/@/views/sys/suite/product/suite-value-edit.vue";
|
import SuiteValueEdit from "/@/views/sys/suite/product/suite-value-edit.vue";
|
||||||
import SuiteValue from "/@/views/sys/suite/product/suite-value.vue";
|
import SuiteValue from "/@/views/sys/suite/product/suite-value.vue";
|
||||||
|
@ -7,7 +7,6 @@ import DurationValue from "/@/views/sys/suite/product/duration-value.vue";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const api = pipelineGroupApi;
|
|
||||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
|
@ -274,6 +273,15 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
if (now < row.activeTime) {
|
if (now < row.activeTime) {
|
||||||
return <a-tag color="blue">待生效</a-tag>;
|
return <a-tag color="blue">待生效</a-tag>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//是否是当前套餐
|
||||||
|
const suites = context.detail.value.suites;
|
||||||
|
if (suites && suites.length > 0) {
|
||||||
|
const suite = suites[0];
|
||||||
|
if (suite.productType === "suite" && suite.id === row.id) {
|
||||||
|
return <a-tag color="success">当前套餐</a-tag>;
|
||||||
|
}
|
||||||
|
}
|
||||||
// 是否在激活时间和过期时间之间
|
// 是否在激活时间和过期时间之间
|
||||||
if (now > row.activeTime && (row.expiresTime == -1 || now < row.expiresTime)) {
|
if (now > row.activeTime && (row.expiresTime == -1 || now < row.expiresTime)) {
|
||||||
return <a-tag color="success">生效中</a-tag>;
|
return <a-tag color="success">生效中</a-tag>;
|
||||||
|
|
|
@ -11,18 +11,25 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineComponent, onActivated, onMounted } from "vue";
|
import { onActivated, onMounted, ref } from "vue";
|
||||||
import { useFs } from "@fast-crud/fast-crud";
|
import { useFs } from "@fast-crud/fast-crud";
|
||||||
import createCrudOptions from "./crud";
|
import createCrudOptions from "./crud";
|
||||||
import { createApi } from "./api";
|
import api, { SuiteDetail } from "/@/views/certd/suite/mine/api";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "MySuites"
|
name: "MySuites"
|
||||||
});
|
});
|
||||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: {} });
|
const detail = ref<SuiteDetail>({});
|
||||||
|
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { detail } });
|
||||||
|
|
||||||
|
async function loadSuiteDetail() {
|
||||||
|
detail.value = await api.SuiteDetailGet();
|
||||||
|
}
|
||||||
|
|
||||||
// 页面打开后获取列表数据
|
// 页面打开后获取列表数据
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
crudExpose.doRefresh();
|
await loadSuiteDetail();
|
||||||
|
await crudExpose.doRefresh();
|
||||||
});
|
});
|
||||||
onActivated(() => {
|
onActivated(() => {
|
||||||
crudExpose.doRefresh();
|
crudExpose.doRefresh();
|
||||||
|
|
|
@ -38,38 +38,16 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import SuiteValue from "/@/views/sys/suite/product/suite-value.vue";
|
import SuiteValue from "/@/views/sys/suite/product/suite-value.vue";
|
||||||
import { computed, ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { request } from "/@/api/service";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
import ExpiresTimeText from "/@/components/expires-time-text.vue";
|
import ExpiresTimeText from "/@/components/expires-time-text.vue";
|
||||||
|
import api, { SuiteDetail } from "/@/views/certd/suite/mine/api";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "SuiteCard"
|
name: "SuiteCard"
|
||||||
});
|
});
|
||||||
|
|
||||||
type SuiteValue = {
|
|
||||||
max: number;
|
|
||||||
used: number;
|
|
||||||
};
|
|
||||||
type SuiteDetail = {
|
|
||||||
enabled?: boolean;
|
|
||||||
suites?: any[];
|
|
||||||
expiresTime?: number;
|
|
||||||
pipelineCount?: SuiteValue;
|
|
||||||
domainCount?: SuiteValue;
|
|
||||||
deployCount?: SuiteValue;
|
|
||||||
monitorCount?: SuiteValue;
|
|
||||||
};
|
|
||||||
const detail = ref<SuiteDetail>({});
|
const detail = ref<SuiteDetail>({});
|
||||||
|
|
||||||
const api = {
|
|
||||||
async SuiteDetailGet() {
|
|
||||||
return await request({
|
|
||||||
url: "/mine/suite/detail",
|
|
||||||
method: "post"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
async function loadSuiteDetail() {
|
async function loadSuiteDetail() {
|
||||||
detail.value = await api.SuiteDetailGet();
|
detail.value = await api.SuiteDetailGet();
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||||
form: {
|
form: {
|
||||||
value: "suite",
|
value: "suite",
|
||||||
rules: [{ required: true, message: "此项必填" }],
|
rules: [{ required: true, message: "此项必填" }],
|
||||||
helper: "目前没区别,重复购买可叠加数量"
|
helper: "套餐:同一时间只有最新购买的一个生效\n加量包:可购买多个,购买后立即生效,不影响套餐\n套餐和加量包数量可叠加"
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 80,
|
width: 80,
|
||||||
|
|
|
@ -57,6 +57,7 @@ CREATE TABLE "cd_user_suite"
|
||||||
"deploy_count_used" integer,
|
"deploy_count_used" integer,
|
||||||
"is_present" boolean,
|
"is_present" boolean,
|
||||||
"is_bootstrap" boolean,
|
"is_bootstrap" boolean,
|
||||||
|
"is_empty" boolean,
|
||||||
"disabled" boolean NOT NULL DEFAULT (false),
|
"disabled" boolean NOT NULL DEFAULT (false),
|
||||||
"active_time" integer,
|
"active_time" integer,
|
||||||
"expires_time" integer,
|
"expires_time" integer,
|
||||||
|
|
|
@ -72,7 +72,7 @@ const development = {
|
||||||
type: 'better-sqlite3',
|
type: 'better-sqlite3',
|
||||||
database: './data/db.sqlite',
|
database: './data/db.sqlite',
|
||||||
synchronize: false, // 如果第一次使用,不存在表,有同步的需求可以写 true
|
synchronize: false, // 如果第一次使用,不存在表,有同步的需求可以写 true
|
||||||
logging: false,
|
logging: true,
|
||||||
highlightSql: false,
|
highlightSql: false,
|
||||||
|
|
||||||
// 配置实体模型 或者 entities: '/entity',
|
// 配置实体模型 或者 entities: '/entity',
|
||||||
|
|
|
@ -34,7 +34,7 @@ import { logger } from '@certd/basic';
|
||||||
import { UrlService } from './url-service.js';
|
import { UrlService } from './url-service.js';
|
||||||
import { NotificationService } from './notification-service.js';
|
import { NotificationService } from './notification-service.js';
|
||||||
import { NotificationGetter } from './notification-getter.js';
|
import { NotificationGetter } from './notification-getter.js';
|
||||||
import { UserSuiteService } from '@certd/commercial-core';
|
import { UserSuiteEntity, UserSuiteService } from '@certd/commercial-core';
|
||||||
import { CertInfoService } from '../../monitor/service/cert-info-service.js';
|
import { CertInfoService } from '../../monitor/service/cert-info-service.js';
|
||||||
|
|
||||||
const runningTasks: Map<string | number, Executor> = new Map();
|
const runningTasks: Map<string | number, Executor> = new Map();
|
||||||
|
@ -391,6 +391,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||||
async run(id: number, triggerId: string, stepId?: string) {
|
async run(id: number, triggerId: string, stepId?: string) {
|
||||||
const entity: PipelineEntity = await this.info(id);
|
const entity: PipelineEntity = await this.info(id);
|
||||||
|
|
||||||
|
let suite: UserSuiteEntity = null;
|
||||||
|
if (isComm()) {
|
||||||
|
suite = await this.userSuiteService.checkHasDeployCount(entity.userId);
|
||||||
|
}
|
||||||
|
|
||||||
const pipeline = JSON.parse(entity.content);
|
const pipeline = JSON.parse(entity.content);
|
||||||
if (!pipeline.id) {
|
if (!pipeline.id) {
|
||||||
pipeline.id = id;
|
pipeline.id = id;
|
||||||
|
@ -464,7 +469,14 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||||
// 清除该step的状态
|
// 清除该step的状态
|
||||||
executor.clearLastStatus(stepId);
|
executor.clearLastStatus(stepId);
|
||||||
}
|
}
|
||||||
await executor.run(historyId, triggerType);
|
const result = await executor.run(historyId, triggerType);
|
||||||
|
|
||||||
|
if (result === ResultType.success) {
|
||||||
|
if (isComm()) {
|
||||||
|
// 消耗成功次数
|
||||||
|
await this.userSuiteService.consumeDeployCount(suite, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error('执行失败:', e);
|
logger.error('执行失败:', e);
|
||||||
// throw e;
|
// throw e;
|
||||||
|
|
Loading…
Reference in New Issue