first commit
This commit is contained in:
271
backend/internal/service/zsp_contract_service.go
Executable file
271
backend/internal/service/zsp_contract_service.go
Executable file
@@ -0,0 +1,271 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user