package employeecollection import ( "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" ) // AllocationCandidate 是一笔待校验的核销申请账单分摊候选。 type AllocationCandidate struct { // BillID 表示目标账单ID。 BillID uint // BillStatus 表示目标账单当前持久化状态。 BillStatus int // Amount 表示本次分摊金额(分)。 Amount int64 // Available 表示目标账单当前可核销余额(分),已扣除已通过分摊与其他审批中预占。 Available int64 } // ValidateAllocations 校验核销申请的账单分摊集合。 // 规则:付款金额为正、分摊数量在允许区间、账单不重复、账单已关闭时拒绝、 // 单笔分摊为正且不超过该账单可核销余额、分摊总额不超过本次付款金额。 func ValidateAllocations(paidAmount int64, candidates []AllocationCandidate) error { if paidAmount <= 0 { return errors.New(errors.CodeInvalidParam, "付款金额必须大于零") } if len(candidates) == 0 { return errors.New(errors.CodeInvalidParam, "核销申请至少需要一个账单分摊") } if len(candidates) > constants.EmployeeCollectionAllocationMaxCount { return errors.New(errors.CodeInvalidParam, "核销申请账单分摊数量超出限制") } seen := make(map[uint]struct{}, len(candidates)) var total int64 for _, candidate := range candidates { if candidate.BillID == 0 { return errors.New(errors.CodeInvalidParam, "账单分摊缺少目标账单") } if _, exists := seen[candidate.BillID]; exists { return errors.New(errors.CodeInvalidParam, "同一账单不能重复分摊") } seen[candidate.BillID] = struct{}{} if candidate.BillStatus == constants.EmployeeCollectionBillStatusClosed { return errors.New(errors.CodeEmployeeCollectionBillClosed) } if candidate.Amount <= 0 { return errors.New(errors.CodeEmployeeCollectionAllocationAmountInvalid) } if candidate.Amount > candidate.Available { return errors.New(errors.CodeEmployeeCollectionAllocationExceeded) } total += candidate.Amount } if total > paidAmount { return errors.New(errors.CodeEmployeeCollectionPaidAmountExceeded) } return nil }