refactor: 拆分 MasterDataService 的 Create/Update,分类与单位创建不再受脏 id 影响
This commit is contained in:
parent
1b0cdde780
commit
16ca9c02ba
|
|
@ -133,7 +133,7 @@ func (h *Handler) CreateMaterialCategory(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item.OrganizationID = orgID
|
item.OrganizationID = orgID
|
||||||
if err := h.masterDataSvc.SaveCategory(&item); err != nil {
|
if err := h.masterDataSvc.CreateCategory(&item); err != nil {
|
||||||
response.BadRequest(c, err.Error())
|
response.BadRequest(c, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -153,7 +153,7 @@ func (h *Handler) UpdateMaterialCategory(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item.ID, item.OrganizationID = id, orgID
|
item.ID, item.OrganizationID = id, orgID
|
||||||
if err := h.masterDataSvc.SaveCategory(&item); err != nil {
|
if err := h.masterDataSvc.UpdateCategory(&item); err != nil {
|
||||||
response.BadRequest(c, err.Error())
|
response.BadRequest(c, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +182,7 @@ func (h *Handler) CreateUnit(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item.OrganizationID = orgID
|
item.OrganizationID = orgID
|
||||||
if err := h.masterDataSvc.SaveUnit(&item); err != nil {
|
if err := h.masterDataSvc.CreateUnit(&item); err != nil {
|
||||||
response.BadRequest(c, err.Error())
|
response.BadRequest(c, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -202,7 +202,7 @@ func (h *Handler) UpdateUnit(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item.ID, item.OrganizationID = id, orgID
|
item.ID, item.OrganizationID = id, orgID
|
||||||
if err := h.masterDataSvc.SaveUnit(&item); err != nil {
|
if err := h.masterDataSvc.UpdateUnit(&item); err != nil {
|
||||||
response.BadRequest(c, err.Error())
|
response.BadRequest(c, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -251,7 +251,7 @@ func (h *Handler) CreateCodeRule(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item.OrganizationID = orgID
|
item.OrganizationID = orgID
|
||||||
if err := h.masterDataSvc.SaveCodeRule(&item); err != nil {
|
if err := h.masterDataSvc.CreateCodeRule(&item); err != nil {
|
||||||
response.BadRequest(c, err.Error())
|
response.BadRequest(c, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -271,7 +271,7 @@ func (h *Handler) UpdateCodeRule(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item.ID, item.OrganizationID = id, orgID
|
item.ID, item.OrganizationID = id, orgID
|
||||||
if err := h.masterDataSvc.SaveCodeRule(&item); err != nil {
|
if err := h.masterDataSvc.UpdateCodeRule(&item); err != nil {
|
||||||
response.BadRequest(c, err.Error())
|
response.BadRequest(c, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,28 @@ func NewMasterDataService() *MasterDataService {
|
||||||
func (s *MasterDataService) ListCategories(orgID int64) ([]model.MaterialCategory, error) {
|
func (s *MasterDataService) ListCategories(orgID int64) ([]model.MaterialCategory, error) {
|
||||||
return s.repo.ListCategories(orgID)
|
return s.repo.ListCategories(orgID)
|
||||||
}
|
}
|
||||||
func (s *MasterDataService) SaveCategory(item *model.MaterialCategory) error {
|
// CreateCategory 新建分类。请求体可能带着前端表单残留的 id,这里不读取 item.ID,
|
||||||
|
// repository 的 INSERT 也不写 id 列,所以脏 id 不会把新建变成覆盖。
|
||||||
|
func (s *MasterDataService) CreateCategory(item *model.MaterialCategory) error {
|
||||||
|
if err := prepareCategory(item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.repo.CreateCategory(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateCategory 更新分类。id 来自路由参数,必须显式存在。
|
||||||
|
func (s *MasterDataService) UpdateCategory(item *model.MaterialCategory) error {
|
||||||
|
if item.ID == 0 {
|
||||||
|
return errUpdateRequiresID
|
||||||
|
}
|
||||||
|
if err := prepareCategory(item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.repo.UpdateCategory(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareCategory 是新建与更新共用的字段清理、校验和默认值补全。
|
||||||
|
func prepareCategory(item *model.MaterialCategory) error {
|
||||||
item.Code = strings.TrimSpace(item.Code)
|
item.Code = strings.TrimSpace(item.Code)
|
||||||
item.Name = strings.TrimSpace(item.Name)
|
item.Name = strings.TrimSpace(item.Name)
|
||||||
item.Description = strings.TrimSpace(item.Description)
|
item.Description = strings.TrimSpace(item.Description)
|
||||||
|
|
@ -78,15 +99,32 @@ func (s *MasterDataService) SaveCategory(item *model.MaterialCategory) error {
|
||||||
if item.Status == "" {
|
if item.Status == "" {
|
||||||
item.Status = "active"
|
item.Status = "active"
|
||||||
}
|
}
|
||||||
if item.ID == 0 {
|
return nil
|
||||||
return s.repo.CreateCategory(item)
|
|
||||||
}
|
|
||||||
return s.repo.UpdateCategory(item)
|
|
||||||
}
|
}
|
||||||
func (s *MasterDataService) ListUnits(orgID int64) ([]model.Unit, error) {
|
func (s *MasterDataService) ListUnits(orgID int64) ([]model.Unit, error) {
|
||||||
return s.repo.ListUnits(orgID)
|
return s.repo.ListUnits(orgID)
|
||||||
}
|
}
|
||||||
func (s *MasterDataService) SaveUnit(item *model.Unit) error {
|
// CreateUnit 新建单位。同样不读取 item.ID,避免表单残留的脏 id 把新建变成覆盖。
|
||||||
|
func (s *MasterDataService) CreateUnit(item *model.Unit) error {
|
||||||
|
if err := prepareUnit(item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.repo.CreateUnit(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUnit 更新单位。id 来自路由参数,必须显式存在。
|
||||||
|
func (s *MasterDataService) UpdateUnit(item *model.Unit) error {
|
||||||
|
if item.ID == 0 {
|
||||||
|
return errUpdateRequiresID
|
||||||
|
}
|
||||||
|
if err := prepareUnit(item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.repo.UpdateUnit(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareUnit 是新建与更新共用的字段清理、校验和默认值补全。
|
||||||
|
func prepareUnit(item *model.Unit) error {
|
||||||
item.Code = strings.TrimSpace(item.Code)
|
item.Code = strings.TrimSpace(item.Code)
|
||||||
item.Name = strings.TrimSpace(item.Name)
|
item.Name = strings.TrimSpace(item.Name)
|
||||||
if item.Code == "" || item.Name == "" {
|
if item.Code == "" || item.Name == "" {
|
||||||
|
|
@ -98,10 +136,7 @@ func (s *MasterDataService) SaveUnit(item *model.Unit) error {
|
||||||
if item.PrecisionScale < 0 || item.PrecisionScale > 6 {
|
if item.PrecisionScale < 0 || item.PrecisionScale > 6 {
|
||||||
return errors.New("precision_scale must be between 0 and 6")
|
return errors.New("precision_scale must be between 0 and 6")
|
||||||
}
|
}
|
||||||
if item.ID == 0 {
|
return nil
|
||||||
return s.repo.CreateUnit(item)
|
|
||||||
}
|
|
||||||
return s.repo.UpdateUnit(item)
|
|
||||||
}
|
}
|
||||||
func (s *MasterDataService) ListCodeRules(orgID int64) ([]model.CodeRule, error) {
|
func (s *MasterDataService) ListCodeRules(orgID int64) ([]model.CodeRule, error) {
|
||||||
return s.repo.ListCodeRules(orgID)
|
return s.repo.ListCodeRules(orgID)
|
||||||
|
|
@ -119,7 +154,27 @@ func (s *MasterDataService) NextMaterialCode(orgID, categoryID int64) (*model.Ne
|
||||||
}
|
}
|
||||||
return &model.NextMaterialCode{Available: true, Code: rule.FormatCode(rule.NextSequence)}, nil
|
return &model.NextMaterialCode{Available: true, Code: rule.FormatCode(rule.NextSequence)}, nil
|
||||||
}
|
}
|
||||||
func (s *MasterDataService) SaveCodeRule(item *model.CodeRule) error {
|
// CreateCodeRule 新建编码规则。同样不读取 item.ID,避免表单残留的脏 id 把新建变成覆盖。
|
||||||
|
func (s *MasterDataService) CreateCodeRule(item *model.CodeRule) error {
|
||||||
|
if err := prepareCodeRule(item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.repo.CreateCodeRule(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateCodeRule 更新编码规则。id 来自路由参数,必须显式存在。
|
||||||
|
func (s *MasterDataService) UpdateCodeRule(item *model.CodeRule) error {
|
||||||
|
if item.ID == 0 {
|
||||||
|
return errUpdateRequiresID
|
||||||
|
}
|
||||||
|
if err := prepareCodeRule(item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.repo.UpdateCodeRule(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareCodeRule 是新建与更新共用的字段清理、校验和默认值补全。
|
||||||
|
func prepareCodeRule(item *model.CodeRule) error {
|
||||||
item.Prefix = strings.TrimSpace(item.Prefix)
|
item.Prefix = strings.TrimSpace(item.Prefix)
|
||||||
if item.CategoryID <= 0 || item.Prefix == "" {
|
if item.CategoryID <= 0 || item.Prefix == "" {
|
||||||
return errors.New("category_id and prefix are required")
|
return errors.New("category_id and prefix are required")
|
||||||
|
|
@ -133,10 +188,7 @@ func (s *MasterDataService) SaveCodeRule(item *model.CodeRule) error {
|
||||||
if item.Status == "" {
|
if item.Status == "" {
|
||||||
item.Status = "active"
|
item.Status = "active"
|
||||||
}
|
}
|
||||||
if item.ID == 0 {
|
return nil
|
||||||
return s.repo.CreateCodeRule(item)
|
|
||||||
}
|
|
||||||
return s.repo.UpdateCodeRule(item)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PartnerService struct{ repo *repository.PartnerRepository }
|
type PartnerService struct{ repo *repository.PartnerRepository }
|
||||||
|
|
|
||||||
|
|
@ -55,3 +55,46 @@ func TestPartnerCreateStillRequiresARole(t *testing.T) {
|
||||||
t.Fatal("既非供应商也非客户时应当被拒绝")
|
t.Fatal("既非供应商也非客户时应当被拒绝")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MasterDataSettings.vue 的 openCreate 同样是 Object.assign 复用表单,
|
||||||
|
// 分类新增也会带上前一次编辑残留的 id,必须走 INSERT。
|
||||||
|
func TestCategoryCreateInsertsEvenWhenItemCarriesStaleID(t *testing.T) {
|
||||||
|
db, mock, err := sqlmock.New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("sqlmock: %v", err)
|
||||||
|
}
|
||||||
|
database.DB = db
|
||||||
|
t.Cleanup(func() { database.DB = nil; db.Close() })
|
||||||
|
|
||||||
|
mock.ExpectExec("INSERT INTO material_categories").WillReturnResult(sqlmock.NewResult(9, 1))
|
||||||
|
|
||||||
|
item := &model.MaterialCategory{ID: 1, Code: "A99", Name: "新分类"}
|
||||||
|
if err := NewMasterDataService().CreateCategory(item); err != nil {
|
||||||
|
t.Fatalf("CreateCategory: %v", err)
|
||||||
|
}
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Fatalf("应当执行 INSERT,实际 SQL 不是插入: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnitUpdateRequiresID(t *testing.T) {
|
||||||
|
err := NewMasterDataService().UpdateUnit(&model.Unit{Code: "pcs", Name: "件"})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("缺少 id 的更新应当被拒绝")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCodeRuleUpdateRequiresID(t *testing.T) {
|
||||||
|
err := NewMasterDataService().UpdateCodeRule(&model.CodeRule{CategoryID: 1, Prefix: "A", SequenceLength: 4})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("缺少 id 的更新应当被拒绝")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单位的小数位校验不能因为拆方法而丢失。
|
||||||
|
func TestUnitCreateStillValidatesPrecisionScale(t *testing.T) {
|
||||||
|
err := NewMasterDataService().CreateUnit(&model.Unit{Code: "kg", Name: "千克", PrecisionScale: 9})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("precision_scale 超过 6 应当被拒绝")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user