feat: 新增项目标签重置接口与n8n流程

This commit is contained in:
2026-02-22 11:58:29 +08:00
parent 93c616793d
commit 285af9a039
45 changed files with 844 additions and 11506 deletions
+86
View File
@@ -260,6 +260,88 @@ export const TagMaintenanceRequestSchema = z.object({
});
});
// ================================
// Project Tag Reset API Schemas
// ================================
const ResettableTagCategorySchema = z.enum([
"FIXED_PROJECT_TYPE",
"TECH_STACK",
"AI_PARADIGM",
"PRODUCT_FORM",
"DOMAIN_SCENARIO",
]);
const ProjectTagSelectionByCategorySchema = z.object({
FIXED_PROJECT_TYPE: z.array(z.string().trim().min(1)).min(1).max(1),
TECH_STACK: z.array(z.string().trim().min(1)).max(20).default([]),
AI_PARADIGM: z.array(z.string().trim().min(1)).max(20).default([]),
PRODUCT_FORM: z.array(z.string().trim().min(1)).max(20).default([]),
DOMAIN_SCENARIO: z.array(z.string().trim().min(1)).max(20).default([]),
});
export const ProjectTagResetItemSchema = z.object({
projectSlug: z.string().trim().min(1).max(200),
selectedTagSlugsByCategory: ProjectTagSelectionByCategorySchema,
});
export const ProjectTagResetRequestSchema = z.object({
apiKey: z.string().min(32, "Invalid API key format"),
dryRun: z.boolean().default(false),
replaceAllCategories: z.boolean().default(true),
categories: z.array(ResettableTagCategorySchema).min(1).default([
"FIXED_PROJECT_TYPE",
"TECH_STACK",
"AI_PARADIGM",
"PRODUCT_FORM",
"DOMAIN_SCENARIO",
]),
projects: z.array(ProjectTagResetItemSchema).min(1).max(100),
}).superRefine((data, ctx) => {
const seenProjectSlugs = new Set<string>();
data.projects.forEach((project, projectIndex) => {
const normalizedSlug = project.projectSlug.trim().toLowerCase();
if (seenProjectSlugs.has(normalizedSlug)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["projects", projectIndex, "projectSlug"],
message: `Duplicate projectSlug: ${project.projectSlug}`,
});
return;
}
seenProjectSlugs.add(normalizedSlug);
const categories = Object.keys(project.selectedTagSlugsByCategory) as Array<
keyof z.infer<typeof ProjectTagSelectionByCategorySchema>
>;
categories.forEach((category) => {
const selectedSlugs = project.selectedTagSlugsByCategory[category];
const seenTagSlugs = new Set<string>();
selectedSlugs.forEach((slug, slugIndex) => {
const normalizedTagSlug = slug.trim().toLowerCase();
if (seenTagSlugs.has(normalizedTagSlug)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: [
"projects",
projectIndex,
"selectedTagSlugsByCategory",
category,
slugIndex,
],
message: `Duplicate tag slug "${slug}" in category ${category}`,
});
return;
}
seenTagSlugs.add(normalizedTagSlug);
});
});
});
});
// ================================
// Types
// ================================
@@ -268,3 +350,7 @@ export type TagUpdate = z.infer<typeof TagUpdateSchema>;
export type MergeTarget = z.infer<typeof MergeTargetSchema>;
export type TagMerge = z.infer<typeof TagMergeSchema>;
export type TagMaintenanceRequest = z.infer<typeof TagMaintenanceRequestSchema>;
export type ResettableTagCategory = z.infer<typeof ResettableTagCategorySchema>;
export type ProjectTagSelectionByCategory = z.infer<typeof ProjectTagSelectionByCategorySchema>;
export type ProjectTagResetItem = z.infer<typeof ProjectTagResetItemSchema>;
export type ProjectTagResetRequest = z.infer<typeof ProjectTagResetRequestSchema>;