171 lines
3.8 KiB
JavaScript
171 lines
3.8 KiB
JavaScript
const VALID_RECORD_TYPES = ['income', 'expense'];
|
|||
|
|
const VALID_STATS_PERIODS = ['week', 'month', 'year'];
|
||
|
|
const VALID_STATS_TYPES = ['income', 'expense', 'all'];
|
||
|
|
const MONTH_PATTERN = /^\d{4}-\d{2}$/;
|
||
|
|
const DATE_TIME_PATTERN = /^\d{4}-\d{2}-\d{2}( \d{2}:\d{2})?$/;
|
||
|
|
|
||
|
|
function isNonEmptyString(value) {
|
||
|
|
return typeof value === 'string' && value.trim().length > 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
function isValidMonth(value) {
|
||
|
|
return typeof value === 'string' && MONTH_PATTERN.test(value);
|
||
|
|
}
|
||
|
|
|
||
|
|
function isValidDateTime(value) {
|
||
|
|
return typeof value === 'string' && DATE_TIME_PATTERN.test(value);
|
||
|
|
}
|
||
|
|
|
||
|
|
function normalizeNote(value) {
|
||
|
|
return value === undefined || value === null ? '' : value;
|
||
|
|
}
|
||
|
|
|
||
|
|
function validateRecordPayload(payload) {
|
||
|
|
if (!payload || typeof payload !== 'object') {
|
||
|
|
return '请求数据格式错误';
|
||
|
|
}
|
||
|
|
|
||
|
|
const { type, amount, category, date, note } = payload;
|
||
|
|
|
||
|
|
if (!VALID_RECORD_TYPES.includes(type)) {
|
||
|
|
return '账目类型错误';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!Number.isFinite(amount) || amount <= 0) {
|
||
|
|
return '金额需大于0';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!isNonEmptyString(category)) {
|
||
|
|
return '请填写分类';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!isValidDateTime(date)) {
|
||
|
|
return '日期格式错误';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (note !== undefined && note !== null && typeof note !== 'string') {
|
||
|
|
return '备注格式错误';
|
||
|
|
}
|
||
|
|
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
function validateRecurringPayload(payload) {
|
||
|
|
if (!payload || typeof payload !== 'object') {
|
||
|
|
return '请求数据格式错误';
|
||
|
|
}
|
||
|
|
|
||
|
|
const { type, amount, category, day, note, is_active } = payload;
|
||
|
|
|
||
|
|
if (!VALID_RECORD_TYPES.includes(type)) {
|
||
|
|
return '账单类型错误';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!Number.isFinite(amount) || amount <= 0) {
|
||
|
|
return '金额需大于0';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!isNonEmptyString(category)) {
|
||
|
|
return '请填写分类';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!Number.isInteger(day) || day < 1 || day > 28) {
|
||
|
|
return '日期需在1到28之间';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (note !== undefined && note !== null && typeof note !== 'string') {
|
||
|
|
return '备注格式错误';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (is_active !== undefined && ![0, 1, true, false].includes(is_active)) {
|
||
|
|
return '启用状态错误';
|
||
|
|
}
|
||
|
|
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
function validateCategoryPayload(payload) {
|
||
|
|
if (!payload || typeof payload !== 'object') {
|
||
|
|
return '请求数据格式错误';
|
||
|
|
}
|
||
|
|
|
||
|
|
const { type, name, icon } = payload;
|
||
|
|
|
||
|
|
if (!VALID_RECORD_TYPES.includes(type)) {
|
||
|
|
return '分类类型错误';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!isNonEmptyString(name)) {
|
||
|
|
return '请填写分类名称';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!isNonEmptyString(icon)) {
|
||
|
|
return '请选择分类图标';
|
||
|
|
}
|
||
|
|
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
function validateImportPayload(payload) {
|
||
|
|
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
||
|
|
return '导入数据格式错误';
|
||
|
|
}
|
||
|
|
|
||
|
|
const { records, recurring, customCategories } = payload;
|
||
|
|
|
||
|
|
if (records !== undefined) {
|
||
|
|
if (!Array.isArray(records)) {
|
||
|
|
return 'records 必须是数组';
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const record of records) {
|
||
|
|
const error = validateRecordPayload(record);
|
||
|
|
if (error) {
|
||
|
|
return `records 数据错误: ${error}`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (recurring !== undefined) {
|
||
|
|
if (!Array.isArray(recurring)) {
|
||
|
|
return 'recurring 必须是数组';
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const bill of recurring) {
|
||
|
|
const error = validateRecurringPayload(bill);
|
||
|
|
if (error) {
|
||
|
|
return `recurring 数据错误: ${error}`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (customCategories !== undefined) {
|
||
|
|
if (!Array.isArray(customCategories)) {
|
||
|
|
return 'customCategories 必须是数组';
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const category of customCategories) {
|
||
|
|
const error = validateCategoryPayload(category);
|
||
|
|
if (error) {
|
||
|
|
return `customCategories 数据错误: ${error}`;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = {
|
||
|
|
isNonEmptyString,
|
||
|
|
isValidMonth,
|
||
|
|
isValidDateTime,
|
||
|
|
normalizeNote,
|
||
|
|
validateRecordPayload,
|
||
|
|
validateRecurringPayload,
|
||
|
|
validateCategoryPayload,
|
||
|
|
validateImportPayload,
|
||
|
|
VALID_RECORD_TYPES,
|
||
|
|
VALID_STATS_PERIODS,
|
||
|
|
VALID_STATS_TYPES,
|
||
|
|
};
|