feat: 实现模板匹配按顺序匹配功能
新功能: - 添加AI分类权重字段,支持按权重顺序匹配 - 新增PriorityOrder匹配规则类型 - 实现按权重顺序的素材匹配算法 - 添加权重编辑器UI组件 数据模型扩展: - AiClassification模型添加weight字段 - SegmentMatchingRule枚举添加PriorityOrder类型 - 扩展相关的请求和响应类型定义 数据库迁移: - 创建019迁移脚本为ai_classifications表添加weight字段 - 为现有数据设置默认权重值 - 添加权重索引提高查询性能 后端服务实现: - MaterialMatchingService支持按顺序匹配逻辑 - AiClassificationService添加按权重获取分类方法 - 更新所有相关的构造函数和命令处理 前端UI优化: - SegmentMatchingRuleEditor支持按顺序匹配配置 - 新增WeightEditor组件用于权重设置 - AI分类设置页面集成权重编辑功能 - 更新TypeScript类型定义 测试验证: - 添加完整的单元测试套件 - 6个测试用例全部通过 - 验证权重排序和匹配规则逻辑 遵循promptx/tauri-desktop-app-expert开发规范 支持用户自定义分类权重,实现智能按顺序匹配
This commit is contained in:
@@ -94,6 +94,10 @@ export const SegmentMatchingRuleEditor: React.FC<SegmentMatchingRuleEditorProps>
|
||||
}
|
||||
} else if (ruleType === 'random') {
|
||||
setEditingRule(SegmentMatchingRuleHelper.createRandomMatch());
|
||||
} else if (ruleType === 'priority_order') {
|
||||
// 默认选择所有激活的AI分类
|
||||
const categoryIds = aiClassifications.map(c => c.id);
|
||||
setEditingRule(SegmentMatchingRuleHelper.createPriorityOrder(categoryIds));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -107,6 +111,23 @@ export const SegmentMatchingRuleEditor: React.FC<SegmentMatchingRuleEditorProps>
|
||||
}
|
||||
};
|
||||
|
||||
const handlePriorityOrderChange = (categoryId: string, isSelected: boolean) => {
|
||||
if (typeof editingRule === 'object' && 'PriorityOrder' in editingRule) {
|
||||
const currentCategoryIds = editingRule.PriorityOrder.category_ids;
|
||||
let newCategoryIds: string[];
|
||||
|
||||
if (isSelected) {
|
||||
// 添加分类ID
|
||||
newCategoryIds = [...currentCategoryIds, categoryId];
|
||||
} else {
|
||||
// 移除分类ID
|
||||
newCategoryIds = currentCategoryIds.filter(id => id !== categoryId);
|
||||
}
|
||||
|
||||
setEditingRule(SegmentMatchingRuleHelper.createPriorityOrder(newCategoryIds));
|
||||
}
|
||||
};
|
||||
|
||||
const getCurrentRuleType = (rule: SegmentMatchingRule): string => {
|
||||
if (SegmentMatchingRuleHelper.isFixedMaterial(rule)) {
|
||||
return 'fixed';
|
||||
@@ -114,6 +135,8 @@ export const SegmentMatchingRuleEditor: React.FC<SegmentMatchingRuleEditorProps>
|
||||
return 'ai_classification';
|
||||
} else if (SegmentMatchingRuleHelper.isRandomMatch(rule)) {
|
||||
return 'random';
|
||||
} else if (SegmentMatchingRuleHelper.isPriorityOrder(rule)) {
|
||||
return 'priority_order';
|
||||
}
|
||||
return 'fixed'; // 默认值
|
||||
};
|
||||
@@ -122,6 +145,7 @@ export const SegmentMatchingRuleEditor: React.FC<SegmentMatchingRuleEditorProps>
|
||||
{ value: 'fixed', label: '固定素材' },
|
||||
{ value: 'ai_classification', label: 'AI分类素材' },
|
||||
{ value: 'random', label: '随机匹配' },
|
||||
{ value: 'priority_order', label: '按顺序匹配' },
|
||||
];
|
||||
|
||||
const classificationOptions = aiClassifications.map(classification => ({
|
||||
@@ -141,6 +165,8 @@ export const SegmentMatchingRuleEditor: React.FC<SegmentMatchingRuleEditorProps>
|
||||
? 'bg-blue-100 text-blue-800'
|
||||
: SegmentMatchingRuleHelper.isRandomMatch(currentRule)
|
||||
? 'bg-green-100 text-green-800'
|
||||
: SegmentMatchingRuleHelper.isPriorityOrder(currentRule)
|
||||
? 'bg-purple-100 text-purple-800'
|
||||
: 'bg-gray-100 text-gray-800'
|
||||
}`}>
|
||||
{SegmentMatchingRuleHelper.getDisplayName(currentRule)}
|
||||
@@ -212,6 +238,49 @@ export const SegmentMatchingRuleEditor: React.FC<SegmentMatchingRuleEditorProps>
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{SegmentMatchingRuleHelper.isPriorityOrder(editingRule) && (
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-blue-800 mb-1">
|
||||
按权重顺序匹配的分类
|
||||
</label>
|
||||
<div className="text-xs text-gray-600 mb-2">
|
||||
系统将按照AI分类的权重顺序依次尝试匹配素材,权重高的分类优先匹配
|
||||
</div>
|
||||
<div className="space-y-1 max-h-32 overflow-y-auto border border-blue-200 rounded-md p-2 bg-white">
|
||||
{aiClassifications
|
||||
.sort((a, b) => (b.weight || 0) - (a.weight || 0)) // 按权重降序排列
|
||||
.map((classification) => {
|
||||
const currentCategoryIds = typeof editingRule === 'object' && 'PriorityOrder' in editingRule
|
||||
? editingRule.PriorityOrder.category_ids
|
||||
: [];
|
||||
const isSelected = currentCategoryIds.includes(classification.id);
|
||||
|
||||
return (
|
||||
<label key={classification.id} className="flex items-center space-x-2 cursor-pointer hover:bg-blue-50 p-1 rounded">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isSelected}
|
||||
onChange={(e) => handlePriorityOrderChange(classification.id, e.target.checked)}
|
||||
className="w-3 h-3 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
||||
/>
|
||||
<span className="text-xs text-gray-700 flex-1">
|
||||
{classification.name}
|
||||
</span>
|
||||
<span className="text-xs text-gray-500 bg-gray-100 px-1 rounded">
|
||||
权重: {classification.weight || 0}
|
||||
</span>
|
||||
</label>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{typeof editingRule === 'object' && 'PriorityOrder' in editingRule && editingRule.PriorityOrder.category_ids.length === 0 && (
|
||||
<div className="text-xs text-amber-700 bg-amber-100 border border-amber-200 p-2 rounded-md mt-2">
|
||||
⚠️ 请至少选择一个AI分类
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
|
||||
Reference in New Issue
Block a user