// 资产类型数字代码映射(纯数字编码) const EQUIPMENT_TYPE_NUMBERS: Record = { '相机': 1, '镜头': 2, '三脚架': 3, '闪光灯': 4, '摄影配饰品': 5 } /** * 生成资产编码(纯数字格式) * 格式: {类型数字}{地址ID(4位)}{序号(5位)} * 例如: 1000100001 (类型1,地址1,序号00001) * 如果没有地址,地址ID为0000 * * 确保唯一性: * 1. 查找该类型和地址的最大序号,避免并发问题 * 2. 检查生成的编码是否已存在,如果存在则递增序号重试 * 3. 最多重试10次,防止无限循环 */ export const generateEquipmentCode = async ( equipmentType: string, prisma: any, storageLocationId?: number | null ): Promise => { const typeNumber = EQUIPMENT_TYPE_NUMBERS[equipmentType] || 0 // 获取地址ID,如果没有地址则为0 const locationId = storageLocationId || 0 // 格式前缀:{类型数字}{地址ID(4位)} const typeCode = String(typeNumber) const locationCode = String(locationId).padStart(4, '0') const prefix = `${typeCode}${locationCode}` // 查找该类型和地址的所有资产,提取最大序号 // 使用这种方式比count更安全,可以处理并发情况 const existingEquipment = await prisma.equipment.findMany({ where: { equipment_type: equipmentType, storage_location_id: storageLocationId || null, equipment_code: { startsWith: prefix } }, select: { equipment_code: true }, orderBy: { equipment_code: 'desc' }, take: 1 // 只取最大的一条 }) // 计算起始序号 let startSequence = 1 if (existingEquipment.length > 0) { // 从最大编码中提取序号 const maxCode = existingEquipment[0].equipment_code if (maxCode.startsWith(prefix)) { const sequencePart = maxCode.substring(prefix.length) const maxSequence = parseInt(sequencePart, 10) if (!isNaN(maxSequence)) { startSequence = maxSequence + 1 } } } // 检查序号是否溢出(5位数字最大99999) if (startSequence > 99999) { throw new Error(`资产编码序号已溢出,该类型和地址的资产数量已超过99999`) } // 生成编码并检查唯一性,最多重试10次 const maxRetries = 10 for (let attempt = 0; attempt < maxRetries; attempt++) { const sequence = String(startSequence + attempt).padStart(5, '0') const code = `${prefix}${sequence}` // 检查编码是否已存在 const existing = await prisma.equipment.findUnique({ where: { equipment_code: code } }) if (!existing) { // 编码唯一,返回 return code } // 如果编码已存在,继续尝试下一个序号 console.warn(`资产编码 ${code} 已存在,尝试下一个序号`) } // 如果重试10次后仍然冲突,抛出错误 throw new Error(`无法生成唯一的资产编码,已重试${maxRetries}次`) } /** * 验证资产编码格式(纯数字) */ export const validateEquipmentCode = (code: string): boolean => { // 纯数字格式,长度10位:{类型1位}{地址4位}{序号5位} const pattern = /^\d{10}$/ return pattern.test(code) }