272 lines
8.9 KiB
Go
Executable File
272 lines
8.9 KiB
Go
Executable File
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)
|
||
}
|