material_manage_system/internal/repository/master_data.go
2026-10-09 10:38:02 +08:00

155 lines
6.9 KiB
Go
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 repository
import (
"database/sql"
"errors"
"strings"
"github.com/wmms/wmms/internal/model"
"github.com/wmms/wmms/pkg/database"
)
// ErrDuplicateCodeRule 表示该分类下已经有一条编码规则。code_rules 上有
// uk_code_rules_org_category(organization_id, category_id),新建选到已占用的
// 分类、或者改分类改到已占用的分类,都会撞上它。
var ErrDuplicateCodeRule = errors.New("该分类已有编码规则")
// translateDuplicateCodeRule 把唯一键冲突翻译成可直接展示的中文错误,
// 其余错误原样返回。前端只有拿到中文消息才会照原样弹出,否则显示"请求失败"。
func translateDuplicateCodeRule(err error) error {
if err != nil && strings.Contains(err.Error(), "Duplicate entry") {
return ErrDuplicateCodeRule
}
return err
}
type MasterDataRepository struct{}
func NewMasterDataRepository() *MasterDataRepository { return &MasterDataRepository{} }
func (r *MasterDataRepository) ListCategories(organizationID int64) ([]model.MaterialCategory, error) {
rows, err := database.DB.Query(`SELECT id, organization_id, code, name, description, parent_id, status FROM material_categories WHERE organization_id = ? ORDER BY code`, organizationID)
if err != nil {
return nil, err
}
defer rows.Close()
items := make([]model.MaterialCategory, 0)
for rows.Next() {
var item model.MaterialCategory
if err := rows.Scan(&item.ID, &item.OrganizationID, &item.Code, &item.Name, &item.Description, &item.ParentID, &item.Status); err != nil {
return nil, err
}
items = append(items, item)
}
return items, rows.Err()
}
// FindCategory 按 id 在当前公司内查分类,查不到返回 (nil, nil)。用于校验编码
// 规则引用的小类、以及分类父级是否属于同一公司。
func (r *MasterDataRepository) FindCategory(organizationID, categoryID int64) (*model.MaterialCategory, error) {
query := `SELECT id, organization_id, code, name, description, parent_id, status FROM material_categories WHERE id = ? AND organization_id = ?`
item := &model.MaterialCategory{}
err := database.DB.QueryRow(query, categoryID, organizationID).Scan(&item.ID, &item.OrganizationID, &item.Code, &item.Name, &item.Description, &item.ParentID, &item.Status)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, err
}
return item, nil
}
func (r *MasterDataRepository) CreateCategory(item *model.MaterialCategory) error {
result, err := database.DB.Exec(`INSERT INTO material_categories (organization_id, code, name, description, parent_id, status) VALUES (?, ?, ?, ?, ?, ?)`, item.OrganizationID, item.Code, item.Name, item.Description, item.ParentID, item.Status)
if err != nil {
return err
}
item.ID, err = result.LastInsertId()
return err
}
func (r *MasterDataRepository) UpdateCategory(item *model.MaterialCategory) error {
return execExpectingRow(`UPDATE material_categories SET name = ?, description = ?, parent_id = ?, status = ? WHERE id = ? AND organization_id = ?`, item.Name, item.Description, item.ParentID, item.Status, item.ID, item.OrganizationID)
}
func (r *MasterDataRepository) ListUnits(organizationID int64) ([]model.Unit, error) {
rows, err := database.DB.Query(`SELECT id, organization_id, code, name, precision_scale, status FROM units WHERE organization_id = ? ORDER BY code`, organizationID)
if err != nil {
return nil, err
}
defer rows.Close()
items := make([]model.Unit, 0)
for rows.Next() {
var item model.Unit
if err := rows.Scan(&item.ID, &item.OrganizationID, &item.Code, &item.Name, &item.PrecisionScale, &item.Status); err != nil {
return nil, err
}
items = append(items, item)
}
return items, rows.Err()
}
func (r *MasterDataRepository) CreateUnit(item *model.Unit) error {
result, err := database.DB.Exec(`INSERT INTO units (organization_id, code, name, precision_scale, status) VALUES (?, ?, ?, ?, ?)`, item.OrganizationID, item.Code, item.Name, item.PrecisionScale, item.Status)
if err != nil {
return err
}
item.ID, err = result.LastInsertId()
return err
}
func (r *MasterDataRepository) UpdateUnit(item *model.Unit) error {
return execExpectingRow(`UPDATE units SET name = ?, precision_scale = ?, status = ? WHERE id = ? AND organization_id = ?`, item.Name, item.PrecisionScale, item.Status, item.ID, item.OrganizationID)
}
func (r *MasterDataRepository) ListCodeRules(organizationID int64) ([]model.CodeRule, error) {
rows, err := database.DB.Query(`SELECT cr.id, cr.organization_id, cr.category_id, c.code, c.name, cr.prefix, cr.sequence_length, cr.next_sequence, cr.status
FROM code_rules cr JOIN material_categories c ON c.id = cr.category_id
WHERE cr.organization_id = ? ORDER BY c.code`, organizationID)
if err != nil {
return nil, err
}
defer rows.Close()
items := make([]model.CodeRule, 0)
for rows.Next() {
var item model.CodeRule
if err := rows.Scan(&item.ID, &item.OrganizationID, &item.CategoryID, &item.CategoryCode, &item.CategoryName, &item.Prefix, &item.SequenceLength, &item.NextSequence, &item.Status); err != nil {
return nil, err
}
items = append(items, item)
}
return items, rows.Err()
}
func (r *MasterDataRepository) CreateCodeRule(item *model.CodeRule) error {
result, err := database.DB.Exec(`INSERT INTO code_rules (organization_id, category_id, prefix, sequence_length, next_sequence, status) VALUES (?, ?, ?, ?, ?, ?)`, item.OrganizationID, item.CategoryID, item.Prefix, item.SequenceLength, item.NextSequence, item.Status)
if err != nil {
return translateDuplicateCodeRule(err)
}
item.ID, err = result.LastInsertId()
return err
}
// UpdateCodeRule 更新编码规则。category_id 必须写进 SET:前端编辑弹窗里的
// 分类下拉没有禁用(与同页的编码输入框不同),而规则又只能改不能删,
// 漏掉这一列会让"改分类"提示成功却完全不落库。
func (r *MasterDataRepository) UpdateCodeRule(item *model.CodeRule) error {
return translateDuplicateCodeRule(execExpectingRow(`UPDATE code_rules SET category_id = ?, prefix = ?, sequence_length = ?, next_sequence = ?, status = ? WHERE id = ? AND organization_id = ?`, item.CategoryID, item.Prefix, item.SequenceLength, item.NextSequence, item.Status, item.ID, item.OrganizationID))
}
// FindActiveCodeRule 取小类当前启用的编码规则,没有规则时返回 (nil, nil)。
// 只读,不加锁:占号发生在创建物料的事务里。
func (r *MasterDataRepository) FindActiveCodeRule(organizationID, categoryID int64) (*model.CodeRule, error) {
query := `SELECT id, organization_id, category_id, prefix, sequence_length, next_sequence, status FROM code_rules WHERE organization_id = ? AND category_id = ? AND status = 'active'`
rule := &model.CodeRule{}
err := database.DB.QueryRow(query, organizationID, categoryID).Scan(&rule.ID, &rule.OrganizationID,
&rule.CategoryID, &rule.Prefix, &rule.SequenceLength, &rule.NextSequence, &rule.Status)
if err == sql.ErrNoRows {
return nil, nil
}
if err != nil {
return nil, err
}
return rule, nil
}