package employeecollection import ( "strings" "time" "unicode/utf8" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" ) // ApplicationInput 是核销申请提交的领域输入;账单分摊由 ValidateAllocations 单独校验。 type ApplicationInput struct { // PaidAmount 表示人工确认的付款金额(分)。 PaidAmount int64 // PayerName 表示付款方名称。 PayerName string // PaidAt 表示付款时间。 PaidAt time.Time // ExternalTransactionNo 表示人工确认的外部交易流水号。 ExternalTransactionNo string // Remark 表示申请备注。 Remark string // ActingReason 表示代办原因,仅代办提交时必填。 ActingReason string // PaymentVoucherKeys 表示支付凭证对象存储键列表。 PaymentVoucherKeys []string } // NormalizedApplicationInput 是通过校验并去空格后的核销申请事实。 type NormalizedApplicationInput struct { PaidAmount int64 PayerName string PaidAt time.Time ExternalTransactionNo string Remark string ActingReason string PaymentVoucherKeys []string } // NormalizeApplicationInput 校验并规范化核销申请输入。 // acting 表示本次是否由超级管理员为他人代办:代办必须填写原因,本人办理不得填写原因。 func NormalizeApplicationInput(input ApplicationInput, acting bool) (NormalizedApplicationInput, error) { if input.PaidAmount <= 0 { return NormalizedApplicationInput{}, errors.New(errors.CodeInvalidParam, "付款金额必须大于零") } if input.PaidAt.IsZero() { return NormalizedApplicationInput{}, errors.New(errors.CodeInvalidParam, "付款时间必填") } payerName := strings.TrimSpace(input.PayerName) if payerName == "" { return NormalizedApplicationInput{}, errors.New(errors.CodeInvalidParam, "付款方名称必填") } if utf8.RuneCountInString(payerName) > constants.EmployeeCollectionPayerNameMaxLength { return NormalizedApplicationInput{}, errors.New(errors.CodeInvalidParam, "付款方名称长度超出限制") } externalTransactionNo := strings.TrimSpace(input.ExternalTransactionNo) if externalTransactionNo == "" { return NormalizedApplicationInput{}, errors.New(errors.CodeInvalidParam, "外部交易流水号必填") } if utf8.RuneCountInString(externalTransactionNo) > constants.EmployeeCollectionExternalTransactionNoMaxLength { return NormalizedApplicationInput{}, errors.New(errors.CodeInvalidParam, "外部交易流水号长度超出限制") } remark := strings.TrimSpace(input.Remark) if utf8.RuneCountInString(remark) > constants.EmployeeCollectionRemarkMaxLength { return NormalizedApplicationInput{}, errors.New(errors.CodeInvalidParam, "核销申请备注长度超出限制") } actingReason := strings.TrimSpace(input.ActingReason) if utf8.RuneCountInString(actingReason) > constants.EmployeeCollectionRemarkMaxLength { return NormalizedApplicationInput{}, errors.New(errors.CodeInvalidParam, "代办原因长度超出限制") } if acting && actingReason == "" { return NormalizedApplicationInput{}, errors.New(errors.CodeInvalidParam, "超级管理员代办核销申请必须填写代办原因") } if !acting && actingReason != "" { return NormalizedApplicationInput{}, errors.New(errors.CodeInvalidParam, "本人办理核销申请不能填写代办原因") } vouchers, err := NormalizePaymentVouchers(input.PaymentVoucherKeys) if err != nil { return NormalizedApplicationInput{}, err } return NormalizedApplicationInput{ PaidAmount: input.PaidAmount, PayerName: payerName, PaidAt: input.PaidAt.UTC(), ExternalTransactionNo: externalTransactionNo, Remark: remark, ActingReason: actingReason, PaymentVoucherKeys: vouchers, }, nil } // ValidateApplicationResubmit 校验申请当前状态允许修改并重提。 // 只有企业微信最终驳回的申请可以修改重提;已通过、审批中与异常终态一律拒绝。 func ValidateApplicationResubmit(status int) error { if status == constants.EmployeeCollectionApplicationStatusRejected { return nil } return errors.New(errors.CodeEmployeeCollectionApplicationStatusInvalid) } // MaskExternalTransactionNo 生成外部交易流水号的脱敏展示,用于审计与日志,不保留完整流水。 // 长度不超过 8 时整体掩码,否则保留首尾各 4 位。 func MaskExternalTransactionNo(value string) string { trimmed := strings.TrimSpace(value) if trimmed == "" { return "" } runes := []rune(trimmed) if len(runes) <= 8 { return "****" } return string(runes[:4]) + "****" + string(runes[len(runes)-4:]) }