mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-04-26 01:10:16 +08:00
feat(store): 实现混合仓库优先级配置功能
添加优先级配置文件支持,根据配置决定默认展示的仓库版本 新增优先级规则匹配逻辑,支持包名、分类和标签匹配 修改应用详情和合并应用的默认来源判断逻辑
This commit is contained in:
27
priority-config.example.json
Normal file
27
priority-config.example.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"_comment": "优先级配置文件 - 放在服务器 ${arch}-store/ 目录下,文件名为 priority-config.json",
|
||||||
|
"_comment2": "默认 APM 优先,此文件配置例外规则",
|
||||||
|
"_comment3": "判断优先级(从高到低):apmPriority.pkgnames > sparkPriority.pkgnames > apmPriority.categories > sparkPriority.categories > apmPriority.tags > sparkPriority.tags > 默认APM",
|
||||||
|
"_comment4": "如果服务器没有这个文件,所有应用都默认优先 APM",
|
||||||
|
|
||||||
|
"sparkPriority": {
|
||||||
|
"_comment": "优先使用 Spark 的规则(例外于默认 APM 优先)",
|
||||||
|
"pkgnames": [],
|
||||||
|
"categories": [
|
||||||
|
"development"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"prefer-spark"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"apmPriority": {
|
||||||
|
"_comment": "优先使用 APM 的规则(例外于 sparkPriority,具有更高优先级)",
|
||||||
|
"_comment2": "例如:games 分类默认优先 Spark,但其中的某些应用仍优先 APM",
|
||||||
|
"pkgnames": [],
|
||||||
|
"categories": [],
|
||||||
|
"tags": [
|
||||||
|
"prefer-apm"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/App.vue
15
src/App.vue
@@ -169,6 +169,8 @@ import {
|
|||||||
currentAppSparkInstalled,
|
currentAppSparkInstalled,
|
||||||
currentAppApmInstalled,
|
currentAppApmInstalled,
|
||||||
currentStoreMode,
|
currentStoreMode,
|
||||||
|
getHybridDefaultOrigin,
|
||||||
|
loadPriorityConfig,
|
||||||
} from "./global/storeConfig";
|
} from "./global/storeConfig";
|
||||||
import {
|
import {
|
||||||
downloads,
|
downloads,
|
||||||
@@ -473,12 +475,14 @@ const openDetail = async (app: App | Record<string, unknown>) => {
|
|||||||
if (sparkApp || apmApp) {
|
if (sparkApp || apmApp) {
|
||||||
// 如果两个仓库都有这个应用,创建合并对象
|
// 如果两个仓库都有这个应用,创建合并对象
|
||||||
if (sparkApp && apmApp) {
|
if (sparkApp && apmApp) {
|
||||||
|
// 根据优先级配置决定默认显示哪个版本
|
||||||
|
const defaultOrigin = getHybridDefaultOrigin(sparkApp);
|
||||||
finalApp = {
|
finalApp = {
|
||||||
...sparkApp, // 默认使用 Spark 的信息作为主显示
|
...(defaultOrigin === "spark" ? sparkApp : apmApp), // 根据优先级选择主显示
|
||||||
isMerged: true,
|
isMerged: true,
|
||||||
sparkApp: sparkApp,
|
sparkApp: sparkApp,
|
||||||
apmApp: apmApp,
|
apmApp: apmApp,
|
||||||
viewingOrigin: "spark", // 默认查看 Spark 版本
|
viewingOrigin: defaultOrigin, // 默认查看优先级高的版本
|
||||||
};
|
};
|
||||||
} else if (sparkApp) {
|
} else if (sparkApp) {
|
||||||
finalApp = sparkApp;
|
finalApp = sparkApp;
|
||||||
@@ -559,8 +563,10 @@ const openDetail = async (app: App | Record<string, unknown>) => {
|
|||||||
finalApp.viewingOrigin = "spark";
|
finalApp.viewingOrigin = "spark";
|
||||||
} else if (apmInstalled && !sparkInstalled) {
|
} else if (apmInstalled && !sparkInstalled) {
|
||||||
finalApp.viewingOrigin = "apm";
|
finalApp.viewingOrigin = "apm";
|
||||||
|
} else {
|
||||||
|
// 若都安装或都未安装,根据优先级配置决定默认展示
|
||||||
|
finalApp.viewingOrigin = getHybridDefaultOrigin(finalApp.sparkApp || finalApp);
|
||||||
}
|
}
|
||||||
// 若都安装或都未安装,默认展示 spark
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayAppForScreenshots =
|
const displayAppForScreenshots =
|
||||||
@@ -1113,6 +1119,9 @@ const loadCategories = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
categories.value = categoryData;
|
categories.value = categoryData;
|
||||||
|
|
||||||
|
// 加载优先级配置(从 spark 目录)
|
||||||
|
await loadPriorityConfig(arch);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`读取 categories 失败: ${error}`);
|
logger.error(`读取 categories 失败: ${error}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -441,7 +441,7 @@
|
|||||||
import { computed, useAttrs, ref, watch } from "vue";
|
import { computed, useAttrs, ref, watch } from "vue";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { useInstallFeedback, downloads } from "../global/downloadStatus";
|
import { useInstallFeedback, downloads } from "../global/downloadStatus";
|
||||||
import { APM_STORE_BASE_URL } from "../global/storeConfig";
|
import { APM_STORE_BASE_URL, getHybridDefaultOrigin } from "../global/storeConfig";
|
||||||
import type { App } from "../global/typedefinition";
|
import type { App } from "../global/typedefinition";
|
||||||
|
|
||||||
const attrs = useAttrs();
|
const attrs = useAttrs();
|
||||||
@@ -482,13 +482,20 @@ const closeMetaModal = () => {
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.app,
|
() => props.app,
|
||||||
(newApp) => {
|
(newApp: App | null) => {
|
||||||
isIconLoaded.value = false;
|
isIconLoaded.value = false;
|
||||||
if (newApp) {
|
if (newApp) {
|
||||||
if (newApp.isMerged) {
|
if (newApp.isMerged) {
|
||||||
// 若父组件已根据安装状态设置了优先展示的版本,则使用;否则默认 Spark
|
// 若父组件已根据安装状态设置了优先展示的版本,则使用
|
||||||
viewingOrigin.value =
|
// 否则根据优先级配置决定默认来源
|
||||||
newApp.viewingOrigin ?? (newApp.sparkApp ? "spark" : "apm");
|
if (newApp.viewingOrigin) {
|
||||||
|
viewingOrigin.value = newApp.viewingOrigin;
|
||||||
|
} else if (newApp.sparkApp) {
|
||||||
|
// 使用优先级配置决定默认来源
|
||||||
|
viewingOrigin.value = getHybridDefaultOrigin(newApp.sparkApp);
|
||||||
|
} else {
|
||||||
|
viewingOrigin.value = "apm";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
viewingOrigin.value = newApp.origin;
|
viewingOrigin.value = newApp.origin;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,3 +13,204 @@ export const currentAppSparkInstalled = ref(false);
|
|||||||
export const currentAppApmInstalled = ref(false);
|
export const currentAppApmInstalled = ref(false);
|
||||||
|
|
||||||
export const currentStoreMode = ref<StoreMode>("hybrid");
|
export const currentStoreMode = ref<StoreMode>("hybrid");
|
||||||
|
|
||||||
|
// 混合模式下默认优先安装的来源(当没有服务器配置或配置获取失败时使用)
|
||||||
|
export const HYBRID_DEFAULT_PRIORITY: "apm" | "spark" = "apm";
|
||||||
|
|
||||||
|
// 优先级规则配置接口
|
||||||
|
export interface PriorityRules {
|
||||||
|
// 优先使用 Spark 的规则(例外于默认 APM 优先)
|
||||||
|
sparkPriority: {
|
||||||
|
pkgnames: string[]; // 包名列表
|
||||||
|
categories: string[]; // 分类列表
|
||||||
|
tags: string[]; // 标签列表
|
||||||
|
};
|
||||||
|
// 优先使用 APM 的规则(例外于 sparkPriority,具有更高优先级)
|
||||||
|
apmPriority: {
|
||||||
|
pkgnames: string[]; // 包名列表(即使在 sparkPriority 分类中也优先 APM)
|
||||||
|
categories: string[]; // 分类列表
|
||||||
|
tags: string[]; // 标签列表
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动态获取的优先级配置(从服务器加载)
|
||||||
|
export let dynamicPriorityConfig: PriorityRules = {
|
||||||
|
sparkPriority: {
|
||||||
|
pkgnames: [],
|
||||||
|
categories: [],
|
||||||
|
tags: [],
|
||||||
|
},
|
||||||
|
apmPriority: {
|
||||||
|
pkgnames: [],
|
||||||
|
categories: [],
|
||||||
|
tags: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 标记是否已从服务器加载配置
|
||||||
|
export let isPriorityConfigLoaded = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从服务器加载优先级配置
|
||||||
|
* 配置文件路径: ${arch}-store/priority-config.json (放在 spark 下)
|
||||||
|
* @param arch 架构,如 "amd64"
|
||||||
|
*/
|
||||||
|
export async function loadPriorityConfig(arch: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const configUrl = `${APM_STORE_BASE_URL}/${arch}-store/priority-config.json`;
|
||||||
|
const response = await fetch(configUrl, {
|
||||||
|
method: "GET",
|
||||||
|
cache: "no-cache",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const config = await response.json();
|
||||||
|
// 支持新旧两种配置格式
|
||||||
|
if (config.sparkPriority || config.apmPriority) {
|
||||||
|
// 新格式:双向配置
|
||||||
|
dynamicPriorityConfig = {
|
||||||
|
sparkPriority: {
|
||||||
|
pkgnames: config.sparkPriority?.pkgnames || [],
|
||||||
|
categories: config.sparkPriority?.categories || [],
|
||||||
|
tags: config.sparkPriority?.tags || [],
|
||||||
|
},
|
||||||
|
apmPriority: {
|
||||||
|
pkgnames: config.apmPriority?.pkgnames || [],
|
||||||
|
categories: config.apmPriority?.categories || [],
|
||||||
|
tags: config.apmPriority?.tags || [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// 旧格式:只配置 sparkPriority(兼容旧配置)
|
||||||
|
dynamicPriorityConfig = {
|
||||||
|
sparkPriority: {
|
||||||
|
pkgnames: config.pkgnames || [],
|
||||||
|
categories: config.categories || [],
|
||||||
|
tags: config.tags || [],
|
||||||
|
},
|
||||||
|
apmPriority: {
|
||||||
|
pkgnames: [],
|
||||||
|
categories: [],
|
||||||
|
tags: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
isPriorityConfigLoaded = true;
|
||||||
|
console.log("[PriorityConfig] 已从服务器加载优先级配置:", dynamicPriorityConfig);
|
||||||
|
} else {
|
||||||
|
// 配置文件不存在,使用默认空配置(APM 优先)
|
||||||
|
console.log("[PriorityConfig] 服务器无配置文件,使用默认 APM 优先");
|
||||||
|
resetPriorityConfig();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 获取失败,使用默认空配置
|
||||||
|
console.warn("[PriorityConfig] 加载配置失败,使用默认 APM 优先:", error);
|
||||||
|
resetPriorityConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置优先级配置为默认值
|
||||||
|
*/
|
||||||
|
function resetPriorityConfig(): void {
|
||||||
|
dynamicPriorityConfig = {
|
||||||
|
sparkPriority: {
|
||||||
|
pkgnames: [],
|
||||||
|
categories: [],
|
||||||
|
tags: [],
|
||||||
|
},
|
||||||
|
apmPriority: {
|
||||||
|
pkgnames: [],
|
||||||
|
categories: [],
|
||||||
|
tags: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
isPriorityConfigLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查应用是否匹配规则
|
||||||
|
*/
|
||||||
|
function matchesRule(app: App, pkgnames: string[], categories: string[], tags: string[]): boolean {
|
||||||
|
// 检查包名
|
||||||
|
if (pkgnames.includes(app.pkgname)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查分类
|
||||||
|
if (categories.includes(app.category)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查标签(tags 是逗号分隔的字符串)
|
||||||
|
if (app.tags && tags.length > 0) {
|
||||||
|
const appTags = app.tags.split(";").map((t) => t.trim().toLowerCase());
|
||||||
|
for (const ruleTag of tags) {
|
||||||
|
if (appTags.includes(ruleTag.toLowerCase())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取混合模式下应用的默认优先来源
|
||||||
|
* 判断优先级(从高到低):
|
||||||
|
* 1. apmPriority.pkgnames - 强制优先 APM
|
||||||
|
* 2. sparkPriority.pkgnames - 强制优先 Spark
|
||||||
|
* 3. apmPriority.categories - 该分类优先 APM
|
||||||
|
* 4. sparkPriority.categories - 该分类优先 Spark
|
||||||
|
* 5. apmPriority.tags - 包含标签优先 APM
|
||||||
|
* 6. sparkPriority.tags - 包含标签优先 Spark
|
||||||
|
* 7. 默认 - 优先 APM
|
||||||
|
* @param app 应用信息
|
||||||
|
* @returns "apm" 或 "spark"
|
||||||
|
*/
|
||||||
|
export function getHybridDefaultOrigin(app: App): "apm" | "spark" {
|
||||||
|
const { sparkPriority, apmPriority } = dynamicPriorityConfig;
|
||||||
|
|
||||||
|
// 1. 检查 APM 优先的包名(最高优先级)
|
||||||
|
if (apmPriority.pkgnames.includes(app.pkgname)) {
|
||||||
|
return "apm";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查 Spark 优先的包名
|
||||||
|
if (sparkPriority.pkgnames.includes(app.pkgname)) {
|
||||||
|
return "spark";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 检查 APM 优先的分类
|
||||||
|
if (apmPriority.categories.includes(app.category)) {
|
||||||
|
return "apm";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 检查 Spark 优先的分类
|
||||||
|
if (sparkPriority.categories.includes(app.category)) {
|
||||||
|
return "spark";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 检查 APM 优先的标签
|
||||||
|
if (app.tags && apmPriority.tags.length > 0) {
|
||||||
|
const appTags = app.tags.split(";").map((t) => t.trim().toLowerCase());
|
||||||
|
for (const ruleTag of apmPriority.tags) {
|
||||||
|
if (appTags.includes(ruleTag.toLowerCase())) {
|
||||||
|
return "apm";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 检查 Spark 优先的标签
|
||||||
|
if (app.tags && sparkPriority.tags.length > 0) {
|
||||||
|
const appTags = app.tags.split(";").map((t) => t.trim().toLowerCase());
|
||||||
|
for (const ruleTag of sparkPriority.tags) {
|
||||||
|
if (appTags.includes(ruleTag.toLowerCase())) {
|
||||||
|
return "spark";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 默认优先 APM
|
||||||
|
return "apm";
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user