package service import ( "time" "github.com/rovina/zsp-backend/internal/dto" "github.com/rovina/zsp-backend/internal/model" "github.com/rovina/zsp-backend/internal/repository" "gorm.io/gorm" ) type ZSPFinancesService interface { CreateFreightCharge(req *dto.ZSPFreightChargesCreateRequest) error GetFreightCharge(id string) (*model.ZSPFreightCharges, error) ListFreightCharges() ([]model.ZSPFreightCharges, error) UpdateFreightCharge(id string, req *dto.ZSPFreightChargesUpdateRequest) error DeleteFreightCharge(id string) error CreateMiscCharge(req *dto.ZSPMiscChargesCreateRequest) error GetMiscCharge(id string) (*model.ZSPMiscCharges, error) ListMiscCharges() ([]model.ZSPMiscCharges, error) UpdateMiscCharge(id string, req *dto.ZSPMiscChargesUpdateRequest) error DeleteMiscCharge(id string) error CreateServiceFee(req *dto.ZSPServiceFeesCreateRequest) error GetServiceFee(id string) (*model.ZSPServiceFees, error) ListServiceFees() ([]model.ZSPServiceFees, error) UpdateServiceFee(id string, req *dto.ZSPServiceFeesUpdateRequest) error DeleteServiceFee(id string) error CreateCostBreakdown(req *dto.ZSPCostBreakdownCreateRequest) error GetCostBreakdown(id string) (*model.ZSPCostBreakdown, error) ListCostBreakdowns(businessID string) ([]model.ZSPCostBreakdown, error) UpdateCostBreakdown(id string, req *dto.ZSPCostBreakdownUpdateRequest) error DeleteCostBreakdown(id string) error CreateProfitRecord(req *dto.ZSPProfitRecordCreateRequest) error GetProfitRecord(id string) (*model.ZSPProfitRecord, error) ListProfitRecords(filters map[string]string) ([]model.ZSPProfitRecord, error) UpdateProfitRecord(id string, req *dto.ZSPProfitRecordUpdateRequest) error DeleteProfitRecord(id string) error } type zspFinancesService struct { repo repository.ZSPFinancesRepository } func NewZSPFinancesService(db *gorm.DB) ZSPFinancesService { return &zspFinancesService{repo: repository.NewZSPFinancesRepository(db)} } // ============ 货代费用 ============ func (s *zspFinancesService) CreateFreightCharge(req *dto.ZSPFreightChargesCreateRequest) error { currency := req.Currency if currency == "" { currency = "CNY" } charge := model.ZSPFreightCharges{ BillOfLading: req.BillOfLading, ContractNumber: req.ContractNumber, ExpenseItem: req.ExpenseItem, Amount: req.Amount, Currency: currency, PaymentDate: parseDate(req.PaymentDate), FreightCompanyName: req.FreightCompanyName, Remark: req.Remark, CreateTime: time.Now(), UpdateTime: time.Now(), } return s.repo.CreateFreightCharge(&charge) } func (s *zspFinancesService) GetFreightCharge(id string) (*model.ZSPFreightCharges, error) { return s.repo.GetFreightCharge(id) } func (s *zspFinancesService) ListFreightCharges() ([]model.ZSPFreightCharges, error) { return s.repo.ListFreightCharges() } func (s *zspFinancesService) UpdateFreightCharge(id string, req *dto.ZSPFreightChargesUpdateRequest) error { charge, err := s.repo.GetFreightCharge(id) if err != nil { return err } if req.BillOfLading != "" { charge.BillOfLading = req.BillOfLading } if req.ContractNumber != "" { charge.ContractNumber = req.ContractNumber } if req.ExpenseItem != "" { charge.ExpenseItem = req.ExpenseItem } if req.Amount != 0 { charge.Amount = req.Amount } if req.Currency != "" { charge.Currency = req.Currency } if req.PaymentDate != "" { charge.PaymentDate = parseDate(req.PaymentDate) } if req.FreightCompanyName != "" { charge.FreightCompanyName = req.FreightCompanyName } charge.Remark = req.Remark charge.UpdateTime = time.Now() return s.repo.UpdateFreightCharge(charge) } func (s *zspFinancesService) DeleteFreightCharge(id string) error { return s.repo.DeleteFreightCharge(id) } // ============ 杂费 ============ func (s *zspFinancesService) CreateMiscCharge(req *dto.ZSPMiscChargesCreateRequest) error { currency := req.Currency if currency == "" { currency = "CNY" } charge := model.ZSPMiscCharges{ BillOfLading: req.BillOfLading, ContractNumber: req.ContractNumber, ExpenseItem: req.ExpenseItem, Amount: req.Amount, Currency: currency, PaymentDate: parseDate(req.PaymentDate), CompanyName: req.CompanyName, Remark: req.Remark, CreateTime: time.Now(), UpdateTime: time.Now(), } return s.repo.CreateMiscCharge(&charge) } func (s *zspFinancesService) GetMiscCharge(id string) (*model.ZSPMiscCharges, error) { return s.repo.GetMiscCharge(id) } func (s *zspFinancesService) ListMiscCharges() ([]model.ZSPMiscCharges, error) { return s.repo.ListMiscCharges() } func (s *zspFinancesService) UpdateMiscCharge(id string, req *dto.ZSPMiscChargesUpdateRequest) error { charge, err := s.repo.GetMiscCharge(id) if err != nil { return err } if req.BillOfLading != "" { charge.BillOfLading = req.BillOfLading } if req.ContractNumber != "" { charge.ContractNumber = req.ContractNumber } if req.ExpenseItem != "" { charge.ExpenseItem = req.ExpenseItem } if req.Amount != 0 { charge.Amount = req.Amount } if req.Currency != "" { charge.Currency = req.Currency } if req.PaymentDate != "" { charge.PaymentDate = parseDate(req.PaymentDate) } if req.CompanyName != "" { charge.CompanyName = req.CompanyName } charge.Remark = req.Remark charge.UpdateTime = time.Now() return s.repo.UpdateMiscCharge(charge) } func (s *zspFinancesService) DeleteMiscCharge(id string) error { return s.repo.DeleteMiscCharge(id) } // ============ 服务费 ============ func (s *zspFinancesService) CreateServiceFee(req *dto.ZSPServiceFeesCreateRequest) error { currency := req.Currency if currency == "" { currency = "CNY" } fee := model.ZSPServiceFees{ BillOfLading: req.BillOfLading, ContractNumber: req.ContractNumber, Name: req.Name, Amount: req.Amount, Currency: currency, Date: parseDate(req.Date), Remark: req.Remark, CreateTime: time.Now(), UpdateTime: time.Now(), } return s.repo.CreateServiceFee(&fee) } func (s *zspFinancesService) GetServiceFee(id string) (*model.ZSPServiceFees, error) { return s.repo.GetServiceFee(id) } func (s *zspFinancesService) ListServiceFees() ([]model.ZSPServiceFees, error) { return s.repo.ListServiceFees() } func (s *zspFinancesService) UpdateServiceFee(id string, req *dto.ZSPServiceFeesUpdateRequest) error { fee, err := s.repo.GetServiceFee(id) if err != nil { return err } if req.BillOfLading != "" { fee.BillOfLading = req.BillOfLading } if req.ContractNumber != "" { fee.ContractNumber = req.ContractNumber } if req.Name != "" { fee.Name = req.Name } if req.Amount != 0 { fee.Amount = req.Amount } if req.Currency != "" { fee.Currency = req.Currency } if req.Date != "" { fee.Date = parseDate(req.Date) } fee.Remark = req.Remark fee.UpdateTime = time.Now() return s.repo.UpdateServiceFee(fee) } func (s *zspFinancesService) DeleteServiceFee(id string) error { return s.repo.DeleteServiceFee(id) } // ============ 成本构成 ============ func (s *zspFinancesService) CreateCostBreakdown(req *dto.ZSPCostBreakdownCreateRequest) error { breakdown := model.ZSPCostBreakdown{ BusinessID: req.BusinessID, BusinessType: req.BusinessType, TotalCost: 0, CreateTime: time.Now(), UpdateTime: time.Now(), } return s.repo.CreateCostBreakdown(&breakdown) } func (s *zspFinancesService) GetCostBreakdown(id string) (*model.ZSPCostBreakdown, error) { return s.repo.GetCostBreakdown(id) } func (s *zspFinancesService) ListCostBreakdowns(businessID string) ([]model.ZSPCostBreakdown, error) { return s.repo.ListCostBreakdowns(businessID) } func (s *zspFinancesService) UpdateCostBreakdown(id string, req *dto.ZSPCostBreakdownUpdateRequest) error { breakdown, err := s.repo.GetCostBreakdown(id) if err != nil { return err } breakdown.FreightChargeTotal = req.FreightChargeTotal breakdown.MiscChargeTotal = req.MiscChargeTotal breakdown.ServiceFeeTotal = req.ServiceFeeTotal breakdown.TotalCost = req.FreightChargeTotal + req.MiscChargeTotal + req.ServiceFeeTotal breakdown.UpdateTime = time.Now() return s.repo.UpdateCostBreakdown(breakdown) } func (s *zspFinancesService) DeleteCostBreakdown(id string) error { return s.repo.DeleteCostBreakdown(id) } // ============ 利润记录 ============ func (s *zspFinancesService) CreateProfitRecord(req *dto.ZSPProfitRecordCreateRequest) error { record := model.ZSPProfitRecord{ BusinessID: req.BusinessID, BusinessType: req.BusinessType, Revenue: req.Revenue, TotalCost: req.TotalCost, Profit: req.Revenue - req.TotalCost, ProfitRate: calculateProfitRate(req.Revenue, req.TotalCost), RecordDate: parseDate(req.RecordDate), Remark: req.Remark, CreateTime: time.Now(), UpdateTime: time.Now(), } return s.repo.CreateProfitRecord(&record) } func (s *zspFinancesService) GetProfitRecord(id string) (*model.ZSPProfitRecord, error) { return s.repo.GetProfitRecord(id) } func (s *zspFinancesService) ListProfitRecords(filters map[string]string) ([]model.ZSPProfitRecord, error) { return s.repo.ListProfitRecords(filters) } func (s *zspFinancesService) UpdateProfitRecord(id string, req *dto.ZSPProfitRecordUpdateRequest) error { record, err := s.repo.GetProfitRecord(id) if err != nil { return err } record.Revenue = req.Revenue record.TotalCost = req.TotalCost record.Profit = req.Revenue - req.TotalCost record.ProfitRate = calculateProfitRate(req.Revenue, req.TotalCost) record.Remark = req.Remark record.UpdateTime = time.Now() return s.repo.UpdateProfitRecord(id, record) } func (s *zspFinancesService) DeleteProfitRecord(id string) error { return s.repo.DeleteProfitRecord(id) } // ============ 辅助函数 ============ func parseDate(dateStr string) time.Time { t, err := time.Parse("2006-01-02", dateStr) if err != nil { return time.Time{} } return t } func calculateProfitRate(revenue, totalCost float64) float64 { if revenue == 0 { return 0 } return (revenue - totalCost) / revenue * 100 }