增加安装app时条件判断,符合条件才能安装

This commit is contained in:
jingrow 2025-04-22 23:26:08 +08:00
parent da162f3cb2
commit 70e2158dd6
6 changed files with 214 additions and 93 deletions

View File

@ -13,7 +13,7 @@
</template>
<script>
import { getCachedDocumentResource } from 'jingrow-ui';
import { getCachedDocumentResource, createResource } from 'jingrow-ui';
import { defineAsyncComponent, h } from 'vue';
import { toast } from 'vue-sonner';
import { renderDialog } from '../../utils/components';
@ -34,47 +34,114 @@ export default {
},
data() {
return {
show: true
show: true,
currentApp: null,
retryCount: 0
};
},
resources: {
// API
checkAppInstallable() {
return {
url: 'jcloud.api.site.check_app_installable',
params: {
name: this.site,
app: this.currentApp
},
auto: false
};
}
},
computed: {
$site() {
return getCachedDocumentResource('Site', this.site);
},
listOptions() {
const handleInstall = row => {
const handleInstall = async row => {
if (this.$site.installApp.loading) return;
if (row.plans) {
this.show = false;
import('./SiteAppPlanSelectDialog.vue').then(module => {
const SiteAppPlanSelectDialog = module.default;
// 便使
this.currentApp = row.app;
this.retryCount = 0
//
console.log('=== 安装应用调试信息 ===');
console.log('站点名称:', this.site);
console.log('应用ID:', this.currentApp);
console.log('站点对象:', this.$site);
console.log('站点plan:', this.$site.pg?.plan);
console.log('=== 调试信息结束 ===');
try {
// 2
let checkResult = null;
let apiCalled = false;
while (!checkResult && this.retryCount < 3) {
console.log(`正在调用check_app_installable API... (尝试 ${this.retryCount + 1}/3)`);
apiCalled = true;
const component = h(SiteAppPlanSelectDialog, {
app: row,
currentPlan: null,
onPlanSelected: plan => {
handleAppInstall(row, plan);
},
"onPlan-selected": plan => {
handleAppInstall(row, plan);
},
await this.$resources.checkAppInstallable.submit();
console.log('API调用完成状态:', {
loading: this.$resources.checkAppInstallable.loading,
error: this.$resources.checkAppInstallable.error
});
renderDialog(component);
}).catch(err => {
toast.error('加载组件失败');
this.show = true;
});
} else {
toast.promise(
this.$site.installApp.submit({
app: row.app
//
checkResult = this.$resources.checkAppInstallable.data;
console.log('API返回结果:', checkResult);
// null500ms
if (!checkResult && this.retryCount < 2) {
this.retryCount++;
console.log(`API返回null将在500ms后重试(${this.retryCount}/2)...`);
await new Promise(resolve => setTimeout(resolve, 500));
} else {
break;
}
}
//
if (!checkResult) {
console.error('API返回结果无效已达到最大重试次数');
toast.error('应用安装权限验证失败,安装已取消');
return;
}
//
console.log('应用可安装?', checkResult.installable);
console.log('required_plan_level:', checkResult.required_plan_level);
console.log('current_plan_level:', checkResult.current_plan_level);
// 退
if (checkResult.installable !== true) {
console.log('应用不可安装,显示错误信息');
const subscriptionTypeMap = {
2: 'Pro',
3: 'Business',
4: 'Enterprise',
5: 'Ultimate'
};
const requiredPlan = subscriptionTypeMap[checkResult.required_plan_level] ||
`Level ${checkResult.required_plan_level}`;
toast.error(`无法安装此应用,需要${requiredPlan}或更高级别的站点计划。请先升级您的站点计划。`);
return;
}
//
console.log('应用可安装,开始安装流程');
await toast.promise(
this.$site.installApp.submit({
app: row.app
}),
{
loading: '正在创建安装任务...',
success: jobId => {
console.log('安装任务创建成功jobId:', jobId);
this.$emit('installed');
this.show = false;
@ -87,58 +154,28 @@ export default {
id: String(jobId)
}
}).catch(err => {
console.error('导航到作业页面失败:', err);
});
} else {
}
} catch (err) {
console.error('处理作业ID时出错:', err);
}
return '应用安装任务已创建';
},
error: e => {
console.error('安装过程中出错:', e);
return getToastErrorMessage(e);
}
}
);
} catch (error) {
console.error('检查应用安装权限失败:', error);
toast.error('无法验证安装权限,安装已取消');
}
};
const handleAppInstall = (app, plan) => {
toast.promise(
this.$site.installApp.submit({
app: app.app,
plan: plan.name
}),
{
loading: '正在创建安装任务...',
success: jobId => {
this.$emit('installed');
this.show = false;
try {
if (jobId) {
router.push({
name: 'Site Job',
params: {
name: this.site,
id: String(jobId)
}
}).catch(err => {
});
} else {
}
} catch (err) {
}
return '应用安装任务已创建';
},
error: e => {
return getToastErrorMessage(e);
}
}
);
};
return {
label: '应用',
fieldname: 'app',

View File

@ -2410,3 +2410,80 @@ def get_site_config_standard_keys():
["name", "key", "title", "description", "type"],
order_by="title asc",
)
@jingrow.whitelist()
def check_app_installable(name, app):
"""检查站点是否有权限安装指定的应用
Args:
name: 站点名称
app: 应用名称
Returns:
dict: 包含是否可安装的信息
{
"installable": True/False,
"required_plan_level": 1-5, # 如果不可安装
"current_plan_level": 1-5, # 当前站点计划等级
}
"""
try:
print(f"check_app_installable被调用: name={name}, app={app}")
site = jingrow.get_pg("Site", name)
print(f"获取到站点: {site.name}, plan={site.plan}")
# 获取应用的subscription_level
app_subscription_level = jingrow.db.get_value(
"Marketplace App", app, "subscription_level")
print(f"应用{app}的subscription_level={app_subscription_level}")
# 确保subscription_level是有效值
if app_subscription_level is None or app_subscription_level == "":
app_subscription_level = 1
print(f"应用{app}的subscription_level为空使用默认值1")
else:
try:
app_subscription_level = int(app_subscription_level)
print(f"应用{app}的subscription_level转换为整数: {app_subscription_level}")
except (ValueError, TypeError) as e:
print(f"转换subscription_level出错: {e}, 使用默认值1")
app_subscription_level = 1
# 获取站点的plan_level
site_plan_level = 1
if site.plan:
plan_level = jingrow.db.get_value(
"Site Plan", site.plan, "plan_level")
print(f"站点plan={site.plan}, plan_level={plan_level}")
if plan_level is not None and plan_level != "":
try:
site_plan_level = int(plan_level)
print(f"站点plan_level转换为整数: {site_plan_level}")
except (ValueError, TypeError) as e:
print(f"转换plan_level出错: {e}, 使用默认值1")
else:
print(f"站点没有plan信息使用默认plan_level=1")
installable = site_plan_level >= app_subscription_level
print(f"安装条件判断: {site_plan_level} >= {app_subscription_level} = {installable}")
result = {
"installable": installable,
"required_plan_level": app_subscription_level,
"current_plan_level": site_plan_level
}
print(f"返回结果: {result}")
return result
except Exception as e:
print(f"check_app_installable出现异常: {e}")
# 发生异常时返回默认值,确保前端收到有效响应
return {
"installable": False,
"required_plan_level": 1,
"current_plan_level": 1,
"error": str(e)
}

View File

@ -3,7 +3,6 @@
"allow_guest_to_view": 1,
"allow_rename": 1,
"creation": "2022-02-04 19:53:27.058972",
"pagetype": "PageType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
@ -16,6 +15,7 @@
"section_break_7",
"jingrow_approved",
"subscription_type",
"subscription_level",
"column_break_10",
"categories",
"published",
@ -388,6 +388,15 @@
"fieldtype": "Table",
"label": "Localisation Apps",
"options": "Marketplace Localisation App"
},
{
"default": "1",
"fieldname": "subscription_level",
"fieldtype": "Select",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Subscription Level",
"options": "1\n2\n3\n4\n5"
}
],
"has_web_view": 1,
@ -397,25 +406,26 @@
"links": [
{
"group": "General",
"link_pagetype": "App Release Approval Request",
"link_fieldname": "marketplace_app"
"link_fieldname": "marketplace_app",
"link_pagetype": "App Release Approval Request"
},
{
"group": "App Subscription",
"link_pagetype": "Marketplace App Plan",
"link_fieldname": "app"
"link_fieldname": "app",
"link_pagetype": "Marketplace App Plan"
},
{
"group": "App Subscription",
"link_pagetype": "Marketplace App Subscription",
"link_fieldname": "app"
"link_fieldname": "app",
"link_pagetype": "Marketplace App Subscription"
}
],
"modified": "2025-02-13 17:16:24.333531",
"modified": "2025-04-22 19:06:40.765966",
"modified_by": "Administrator",
"module": "Jcloud",
"name": "Marketplace App",
"owner": "Administrator",
"pagetype": "PageType",
"permissions": [
{
"create": 1,
@ -430,6 +440,7 @@
"write": 1
}
],
"row_format": "Dynamic",
"sort_field": "modified",
"sort_order": "DESC",
"states": [

View File

@ -37,18 +37,11 @@ class MarketplaceApp(WebsiteGenerator):
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from jingrow.types import DF
from jcloud.jcloud.pagetype.marketplace_app_categories.marketplace_app_categories import (
MarketplaceAppCategories,
)
from jcloud.jcloud.pagetype.marketplace_app_screenshot.marketplace_app_screenshot import (
MarketplaceAppScreenshot,
)
from jcloud.jcloud.pagetype.marketplace_app_categories.marketplace_app_categories import MarketplaceAppCategories
from jcloud.jcloud.pagetype.marketplace_app_screenshot.marketplace_app_screenshot import MarketplaceAppScreenshot
from jcloud.jcloud.pagetype.marketplace_app_version.marketplace_app_version import MarketplaceAppVersion
from jcloud.jcloud.pagetype.marketplace_localisation_app.marketplace_localisation_app import (
MarketplaceLocalisationApp,
)
from jcloud.jcloud.pagetype.marketplace_localisation_app.marketplace_localisation_app import MarketplaceLocalisationApp
from jingrow.types import DF
after_install_script: DF.Code | None
after_uninstall_script: DF.Code | None
@ -58,8 +51,8 @@ class MarketplaceApp(WebsiteGenerator):
custom_verify_template: DF.Check
description: DF.SmallText
documentation: DF.Data | None
jingrow_approved: DF.Check
image: DF.AttachImage | None
jingrow_approved: DF.Check
localisation_apps: DF.Table[MarketplaceLocalisationApp]
long_description: DF.TextEditor | None
message: DF.TextEditor | None
@ -68,15 +61,7 @@ class MarketplaceApp(WebsiteGenerator):
poll_method: DF.Data | None
privacy_policy: DF.Data | None
published: DF.Check
review_stage: DF.Literal[
"Not Started",
"Description Missing",
"Logo Missing",
"App Release Not Reviewed",
"Ready for Review",
"Ready to Publish",
"Rejected",
]
review_stage: DF.Literal["Not Started", "Description Missing", "Logo Missing", "App Release Not Reviewed", "Ready for Review", "Ready to Publish", "Rejected"]
route: DF.Data | None
run_after_install_script: DF.Check
run_after_uninstall_script: DF.Check
@ -88,6 +73,7 @@ class MarketplaceApp(WebsiteGenerator):
status: DF.Literal["Draft", "Published", "In Review", "Attention Required", "Rejected", "Disabled"]
stop_auto_review: DF.Check
subject: DF.Data | None
subscription_level: DF.Literal["1", "2", "3", "4", "5"]
subscription_type: DF.Literal["Free", "Paid", "Freemium"]
subscription_update_hook: DF.Data | None
support: DF.Data | None

View File

@ -14,6 +14,7 @@
"column_break_5",
"price_cny",
"price_usd",
"plan_level",
"allow_downgrading_from_other_plan",
"features_section",
"cpu_time_per_day",
@ -235,11 +236,18 @@
"fieldname": "allow_downgrading_from_other_plan",
"fieldtype": "Check",
"label": "Allow Downgrading From Other Plan"
},
{
"default": "1",
"fieldname": "plan_level",
"fieldtype": "Select",
"label": "Plan Level",
"options": "1\n2\n3\n4\n5"
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-04-20 20:46:00.570720",
"modified": "2025-04-22 18:40:33.565094",
"modified_by": "Administrator",
"module": "Jcloud",
"name": "Site Plan",

View File

@ -39,6 +39,7 @@ class SitePlan(Plan):
memory: DF.Int
monitor_access: DF.Check
offsite_backups: DF.Check
plan_level: DF.Literal["1", "2", "3", "4", "5"]
plan_title: DF.Data | None
price_cny: DF.Currency
price_usd: DF.Currency
@ -52,6 +53,7 @@ class SitePlan(Plan):
dashboard_fields = (
"name",
"plan_title",
"plan_level",
"interval",
"document_type",
"document_name",