132 lines
3.7 KiB
TypeScript
132 lines
3.7 KiB
TypeScript
import { getTemplateWithFields } from './template-validator'
|
||
import { filterVisibleFieldValues, getVisibleTemplateFields, hasFieldValue } from './field-conditions'
|
||
import { parseValidationRules, validateFieldValue } from './field-validation'
|
||
|
||
type TemplateFieldLike = {
|
||
key: string
|
||
label?: string | null
|
||
type?: string | null
|
||
required?: boolean | null
|
||
conditions?: string | null
|
||
validations?: string | null
|
||
}
|
||
|
||
export type TemplateFieldValidationResult =
|
||
| {
|
||
valid: true
|
||
templateName?: string
|
||
templateFields: Record<string, unknown>
|
||
visibleFieldKeys: string[]
|
||
}
|
||
| {
|
||
valid: false
|
||
error: string
|
||
errorCode: string
|
||
details?: Record<string, unknown>
|
||
}
|
||
|
||
export { hasFieldValue } from './field-conditions'
|
||
|
||
export async function validateSubmittedTemplateFields(args: {
|
||
templateId: string | null | undefined
|
||
templateFields: Record<string, unknown>
|
||
allowUnknownKeys?: boolean
|
||
}): Promise<TemplateFieldValidationResult> {
|
||
const { templateId, templateFields, allowUnknownKeys = true } = args
|
||
|
||
if (!templateId) {
|
||
return {
|
||
valid: true,
|
||
templateFields,
|
||
visibleFieldKeys: Object.keys(templateFields),
|
||
}
|
||
}
|
||
|
||
const template = await getTemplateWithFields(templateId)
|
||
if (!template) {
|
||
return {
|
||
valid: false,
|
||
error: '模板不存在或不可用',
|
||
errorCode: 'TEMPLATE_NOT_FOUND',
|
||
}
|
||
}
|
||
|
||
const fields = (template.fields || []) as TemplateFieldLike[]
|
||
const fieldMap = new Map(fields.map((field) => [field.key, field]))
|
||
const visibleFields = getVisibleTemplateFields(fields, templateFields)
|
||
const visibleTemplateFields = filterVisibleFieldValues(fields, templateFields)
|
||
|
||
if (!allowUnknownKeys) {
|
||
const unknownKeys = Object.keys(templateFields).filter((key) => !fieldMap.has(key))
|
||
if (unknownKeys.length > 0) {
|
||
return {
|
||
valid: false,
|
||
error: `提交了模板中不存在的字段:${unknownKeys.join(', ')}`,
|
||
errorCode: 'UNKNOWN_TEMPLATE_FIELDS',
|
||
details: {
|
||
unknownKeys,
|
||
templateId,
|
||
},
|
||
}
|
||
}
|
||
}
|
||
|
||
const missingRequiredFields = visibleFields
|
||
.filter((field) => field.required)
|
||
.filter((field) => !hasFieldValue(visibleTemplateFields[field.key], field.type))
|
||
.map((field) => ({
|
||
key: field.key,
|
||
label: field.label || field.key,
|
||
type: field.type || 'text',
|
||
}))
|
||
|
||
if (missingRequiredFields.length > 0) {
|
||
return {
|
||
valid: false,
|
||
error: `模板必填字段未填写:${missingRequiredFields.map((field) => field.label).join('、')}`,
|
||
errorCode: 'TEMPLATE_REQUIRED_FIELDS_MISSING',
|
||
details: {
|
||
templateId,
|
||
missingRequiredFields,
|
||
},
|
||
}
|
||
}
|
||
|
||
const invalidFields = visibleFields
|
||
.map((field) => {
|
||
const result = validateFieldValue(
|
||
visibleTemplateFields[field.key],
|
||
field.type || 'text',
|
||
parseValidationRules(field.validations),
|
||
field.label || field.key
|
||
)
|
||
|
||
return {
|
||
key: field.key,
|
||
label: field.label || field.key,
|
||
type: field.type || 'text',
|
||
errors: result.errors,
|
||
}
|
||
})
|
||
.filter((field) => field.errors.length > 0)
|
||
|
||
if (invalidFields.length > 0) {
|
||
return {
|
||
valid: false,
|
||
error: `模板字段填写不符合规则:${invalidFields.map((field) => `${field.label}:${field.errors.join('、')}`).join(';')}`,
|
||
errorCode: 'TEMPLATE_FIELD_VALIDATION_FAILED',
|
||
details: {
|
||
templateId,
|
||
invalidFields,
|
||
},
|
||
}
|
||
}
|
||
|
||
return {
|
||
valid: true,
|
||
templateName: typeof template.name === 'string' ? template.name : undefined,
|
||
templateFields: visibleTemplateFields,
|
||
visibleFieldKeys: visibleFields.map((field) => field.key),
|
||
}
|
||
}
|