Files
zsp-project/backend/internal/service/zsp_contract_service.go
2026-06-03 20:59:39 +08:00

272 lines
8.9 KiB
Go
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import (
"fmt"
"mime/multipart"
"os"
"path"
"strings"
"time"
"github.com/rovina/zsp-backend/internal/dto"
"github.com/rovina/zsp-backend/internal/model"
"github.com/rovina/zsp-backend/internal/repository"
)
type ZSPContractService interface {
CreateFolder(req *dto.ZSPFolderCreateRequest) error
GetFolders() ([]model.ZSPContractFolder, error)
GetFolderById(folderID string) (*model.ZSPContractFolder, error)
DeleteFolderById(folderID string) error
UpdateFolderById(folderID string, req *dto.ZSPFolderUpdateRequest) error
UploadContract(req *dto.ZSPContractCreateRequest, fileHeader *multipart.FileHeader) error
GetContractById(contractID string) (*model.ZSPContractFile, error)
GetContractWithDetails(contractID string) (*model.ZSPContractFile, error)
ListContracts(folderID string) ([]model.ZSPContractFile, error)
ListContractsWithDetails(folderID string) ([]model.ZSPContractFile, error)
DeleteContractById(contractID string) error
UpdateContract(contract *model.ZSPContractFile) error
UpdateContractById(contractID string, req *dto.ZSPContractUpdateRequest) error
// 合同详情相关
CreateContractDetail(req *dto.ZSPContractDetailCreateRequest) error
UpdateContractDetail(req *dto.ZSPContractDetailUpdateRequest) error
GetContractDetailById(detailID string) (*model.ZSPContractDetail, error)
ListContractDetails(contractFileID string) ([]model.ZSPContractDetail, error)
DeleteContractDetailById(detailID string) error
BatchCreateContractDetails(req *dto.ZSPContractDetailBatchCreateRequest) error
}
type zspContractService struct {
repo repository.ZSPContractRepository
}
func NewZSPContractService(repo repository.ZSPContractRepository) (ZSPContractService, error) {
return &zspContractService{repo: repo}, nil
}
func (s *zspContractService) CreateFolder(req *dto.ZSPFolderCreateRequest) error {
var folder model.ZSPContractFolder
folder.Name = req.Name
folder.Description = req.Description
folder.Count = 0
folder.CreateTime = time.Now()
return s.repo.CreateFolder(&folder)
}
func (s *zspContractService) GetFolders() ([]model.ZSPContractFolder, error) {
folders, err := s.repo.GetFolders()
if err != nil {
return nil, err
}
return folders, nil
}
func (s *zspContractService) GetFolderById(folderID string) (*model.ZSPContractFolder, error) {
return s.repo.GetFolderById(folderID)
}
func (s *zspContractService) DeleteFolderById(folderID string) error {
return s.repo.DeleteFolderById(folderID)
}
func (s *zspContractService) UpdateFolderById(folderID string, req *dto.ZSPFolderUpdateRequest) error {
folder, err := s.repo.GetFolderById(folderID)
if err != nil {
return err
}
folder.Name = req.Name
folder.Description = req.Description
return s.repo.UpdateFolder(folder)
}
func (s *zspContractService) UploadContract(req *dto.ZSPContractCreateRequest, fileHeader *multipart.FileHeader) error {
timeNow := time.Now()
var contract model.ZSPContractFile
contract.FolderID = req.FolderID
contract.Commodity = req.Commodity
contract.CompanyName = req.CompanyName
contract.ContractNumber = req.ContractNumber
contract.ContractType = req.ContractType
contract.FileName = fileHeader.Filename
// 提取扩展名便于前端展示(如 pdf、jpg
if ext := path.Ext(fileHeader.Filename); ext != "" {
contract.FileType = strings.TrimPrefix(strings.ToLower(ext), ".")
} else {
contract.FileType = fileHeader.Header.Get("Content-Type")
}
if req.SignDate == "" {
return fmt.Errorf("签订日期为必填项")
}
signDate, err := time.Parse("2006-01-02", req.SignDate)
if err != nil {
return fmt.Errorf("签订日期格式不正确,请使用 YYYY-MM-DD: %w", err)
}
contract.SignDate = signDate
contract.UploadTime = timeNow
// 确保目录存在后保存文件
saveDir := "./data/zsp-contract/files"
if err := os.MkdirAll(saveDir, os.ModePerm); err != nil {
return fmt.Errorf("create upload dir: %w", err)
}
storedName := fmt.Sprintf("%d_%s", timeNow.UnixNano(), fileHeader.Filename)
savePath := path.Join(saveDir, storedName)
if err := saveUploadedFile(fileHeader, savePath); err != nil {
return err
}
contract.FileURL = "files/" + storedName
contract.FileSize = fileHeader.Size
return s.repo.CreateContract(&contract)
}
func (s *zspContractService) GetContractById(contractID string) (*model.ZSPContractFile, error) {
return s.repo.GetContractById(contractID)
}
func (s *zspContractService) ListContracts(folderID string) ([]model.ZSPContractFile, error) {
return s.repo.ListContracts(folderID)
}
func (s *zspContractService) DeleteContractById(contractID string) error {
return s.repo.DeleteContractById(contractID)
}
func (s *zspContractService) UpdateContract(contract *model.ZSPContractFile) error {
return s.repo.UpdateContract(contract)
}
func (s *zspContractService) UpdateContractById(contractID string, req *dto.ZSPContractUpdateRequest) error {
contract, err := s.repo.GetContractById(contractID)
if err != nil {
return err
}
if req.ContractNumber != "" {
contract.ContractNumber = req.ContractNumber
}
if req.CompanyName != "" {
contract.CompanyName = req.CompanyName
}
if req.Commodity != "" {
contract.Commodity = req.Commodity
}
if req.ContractType != "" {
contract.ContractType = req.ContractType
}
if req.FolderID > 0 {
contract.FolderID = req.FolderID
}
if req.SignDate != "" {
signDate, err := time.Parse("2006-01-02", req.SignDate)
if err != nil {
return fmt.Errorf("签订日期格式不正确: %w", err)
}
contract.SignDate = signDate
}
return s.repo.UpdateContract(contract)
}
func (s *zspContractService) GetContractWithDetails(contractID string) (*model.ZSPContractFile, error) {
contract, err := s.repo.GetContractById(contractID)
if err != nil {
return nil, err
}
// 获取详情列表
details, err := s.repo.ListContractDetails(contractID)
if err != nil {
return nil, err
}
contract.Details = details
return contract, nil
}
func (s *zspContractService) ListContractsWithDetails(folderID string) ([]model.ZSPContractFile, error) {
return s.repo.ListContractsWithDetails(folderID)
}
// ============ 合同详情相关方法 ============
func (s *zspContractService) CreateContractDetail(req *dto.ZSPContractDetailCreateRequest) error {
var detail model.ZSPContractDetail
detail.ContractFileID = req.ContractFileID
detail.CommodityName = req.CommodityName
detail.CommodityCode = req.CommodityCode
detail.TotalQuantity = req.TotalQuantity
detail.Unit = req.Unit
detail.UnitPrice = req.UnitPrice
detail.DeliveredQty = req.DeliveredQty
detail.BillOfLading = req.BillOfLading
detail.Remark = req.Remark
// PendingQty 会在 BeforeCreate 钩子中自动计算
return s.repo.CreateContractDetail(&detail)
}
func (s *zspContractService) UpdateContractDetail(req *dto.ZSPContractDetailUpdateRequest) error {
detail, err := s.repo.GetContractDetailById(fmt.Sprintf("%d", req.ID))
if err != nil {
return err
}
if req.CommodityName != "" {
detail.CommodityName = req.CommodityName
}
if req.CommodityCode != "" {
detail.CommodityCode = req.CommodityCode
}
if req.TotalQuantity > 0 {
detail.TotalQuantity = req.TotalQuantity
}
if req.Unit != "" {
detail.Unit = req.Unit
}
if req.UnitPrice > 0 {
detail.UnitPrice = req.UnitPrice
}
// DeliveredQty 可以为 0所以需要特别处理
detail.DeliveredQty = req.DeliveredQty
if req.BillOfLading != "" {
detail.BillOfLading = req.BillOfLading
}
detail.Remark = req.Remark
// PendingQty 会在 BeforeUpdate 钩子中自动计算
return s.repo.UpdateContractDetail(detail)
}
func (s *zspContractService) GetContractDetailById(detailID string) (*model.ZSPContractDetail, error) {
return s.repo.GetContractDetailById(detailID)
}
func (s *zspContractService) ListContractDetails(contractFileID string) ([]model.ZSPContractDetail, error) {
return s.repo.ListContractDetails(contractFileID)
}
func (s *zspContractService) DeleteContractDetailById(detailID string) error {
return s.repo.DeleteContractDetailById(detailID)
}
func (s *zspContractService) BatchCreateContractDetails(req *dto.ZSPContractDetailBatchCreateRequest) error {
details := make([]model.ZSPContractDetail, 0, len(req.Details))
for _, detailReq := range req.Details {
detail := model.ZSPContractDetail{
ContractFileID: req.ContractFileID,
CommodityName: detailReq.CommodityName,
CommodityCode: detailReq.CommodityCode,
TotalQuantity: detailReq.TotalQuantity,
Unit: detailReq.Unit,
UnitPrice: detailReq.UnitPrice,
DeliveredQty: detailReq.DeliveredQty,
BillOfLading: detailReq.BillOfLading,
Remark: detailReq.Remark,
}
// PendingQty 会在 BeforeCreate 钩子中自动计算
details = append(details, detail)
}
return s.repo.BatchCreateContractDetails(details)
}