package repository import ( "database/sql" "errors" "fmt" "strings" "github.com/wmms/wmms/internal/model" "github.com/wmms/wmms/pkg/database" ) type PartnerRepository struct{} func NewPartnerRepository() *PartnerRepository { return &PartnerRepository{} } func (r *PartnerRepository) List(organizationID int64, role string, limit, offset int, category string) ([]model.BusinessPartner, int64, error) { // 角色过滤写成一份,由计数和取数共用:两处各写一遍,改一处漏一处就会让 // 总数和列表对不上(总数算的是全部,列表却只有供应商)。 filter := `FROM organization_partners op JOIN business_partners bp ON bp.id = op.business_partner_id WHERE op.organization_id = ?` args := []interface{}{organizationID} if role == "supplier" { filter += ` AND op.is_supplier = TRUE` } if role == "customer" { filter += ` AND op.is_customer = TRUE` } if category != "" { // 字段名仅由已知角色选择,筛选值始终使用参数绑定。 field := "op.supplier_categories" if role == "customer" { field = "op.customer_categories" } else if role != "supplier" { return nil, 0, errors.New("业务分类筛选需要指定客户或供应商") } if category == "unclassified" { filter += ` AND ` + field + ` = ''` } else if category == "material" || category == "finished_product" { filter += ` AND FIND_IN_SET(?, ` + field + `) > 0` args = append(args, category) } else { return nil, 0, errors.New("invalid partner category") } } var total int64 if err := database.DB.QueryRow(`SELECT COUNT(*) `+filter, args...).Scan(&total); err != nil { return nil, 0, err } query := `SELECT op.id, op.organization_id, bp.id, op.partner_code, bp.legal_name, COALESCE(bp.unified_credit_code, ''), COALESCE(bp.contact_name, ''), COALESCE(bp.contact_phone, ''), COALESCE(bp.address, ''), op.is_supplier, op.is_customer, COALESCE(op.supplier_status, ''), COALESCE(op.customer_status, ''), COALESCE(op.settlement_currency, ''), COALESCE(op.payment_terms, ''), op.supplier_categories, op.customer_categories ` + filter + ` ORDER BY op.partner_code LIMIT ? OFFSET ?` // 计数查询已经用完 args,这里追加分页参数不会再影响到它 rows, err := database.DB.Query(query, append(args, limit, offset)...) if err != nil { return nil, 0, err } defer rows.Close() items := make([]model.BusinessPartner, 0) for rows.Next() { var item model.BusinessPartner var supplierCategories, customerCategories string if err := rows.Scan(&item.ID, &item.OrganizationID, &item.BusinessPartnerID, &item.PartnerCode, &item.LegalName, &item.UnifiedCreditCode, &item.ContactName, &item.ContactPhone, &item.Address, &item.IsSupplier, &item.IsCustomer, &item.SupplierStatus, &item.CustomerStatus, &item.SettlementCurrency, &item.PaymentTerms, &supplierCategories, &customerCategories); err != nil { return nil, 0, err } item.SupplierCategories = splitPartnerCategories(supplierCategories) item.CustomerCategories = splitPartnerCategories(customerCategories) items = append(items, item) } return items, total, rows.Err() } func (r *PartnerRepository) Create(item *model.BusinessPartner) (err error) { tx, err := database.DB.Begin() if err != nil { return err } defer func() { if err != nil { _ = tx.Rollback() } }() result, err := tx.Exec(`INSERT INTO business_partners (legal_name, unified_credit_code, contact_name, contact_phone, address) VALUES (?, NULLIF(?, ''), NULLIF(?, ''), NULLIF(?, ''), NULLIF(?, ''))`, item.LegalName, item.UnifiedCreditCode, item.ContactName, item.ContactPhone, item.Address) if err != nil { return err } item.BusinessPartnerID, err = result.LastInsertId() if err != nil { return err } result, err = tx.Exec(`INSERT INTO organization_partners (organization_id, business_partner_id, partner_code, is_supplier, is_customer, supplier_status, customer_status, settlement_currency, payment_terms, supplier_categories, customer_categories) VALUES (?, ?, ?, ?, ?, NULLIF(?, ''), NULLIF(?, ''), NULLIF(?, ''), NULLIF(?, ''), ?, ?)`, item.OrganizationID, item.BusinessPartnerID, item.PartnerCode, item.IsSupplier, item.IsCustomer, item.SupplierStatus, item.CustomerStatus, item.SettlementCurrency, item.PaymentTerms, strings.Join(item.SupplierCategories, ","), strings.Join(item.CustomerCategories, ",")) if err != nil { return err } item.ID, err = result.LastInsertId() if err != nil { return err } return tx.Commit() } func (r *PartnerRepository) Update(item *model.BusinessPartner) (err error) { tx, err := database.DB.Begin() if err != nil { return err } defer func() { if err != nil { _ = tx.Rollback() } }() // 这一句兼任存在性检查:查不到就说明伙伴不存在或不属于当前组织, // 不能让它以 sql.ErrNoRows 一路冒到接口变成 500。 var businessPartnerID int64 if err = tx.QueryRow(`SELECT business_partner_id FROM organization_partners WHERE id = ? AND organization_id = ? FOR UPDATE`, item.ID, item.OrganizationID).Scan(&businessPartnerID); err != nil { if errors.Is(err, sql.ErrNoRows) { return ErrRecordNotFound } return err } if _, err = tx.Exec(`UPDATE business_partners SET legal_name = ?, unified_credit_code = NULLIF(?, ''), contact_name = NULLIF(?, ''), contact_phone = NULLIF(?, ''), address = NULLIF(?, '') WHERE id = ?`, item.LegalName, item.UnifiedCreditCode, item.ContactName, item.ContactPhone, item.Address, businessPartnerID); err != nil { return err } if _, err = tx.Exec(`UPDATE organization_partners SET is_supplier = ?, is_customer = ?, supplier_status = NULLIF(?, ''), customer_status = NULLIF(?, ''), settlement_currency = NULLIF(?, ''), payment_terms = NULLIF(?, ''), supplier_categories = COALESCE(?, supplier_categories), customer_categories = COALESCE(?, customer_categories) WHERE id = ? AND organization_id = ?`, item.IsSupplier, item.IsCustomer, item.SupplierStatus, item.CustomerStatus, item.SettlementCurrency, item.PaymentTerms, optionalPartnerCategories(item.SupplierCategories), optionalPartnerCategories(item.CustomerCategories), item.ID, item.OrganizationID); err != nil { return err } if err = tx.Commit(); err != nil { return fmt.Errorf("commit partner update: %w", err) } return nil } func splitPartnerCategories(value string) []string { if value == "" { return []string{} } return strings.Split(value, ",") } func optionalPartnerCategories(categories []string) interface{} { if categories == nil { return nil } return strings.Join(categories, ",") }