package repository import ( "database/sql" "errors" "fmt" "strings" "cmautobuy/admin/model" "github.com/go-sql-driver/mysql" ) var ( ErrShopNameExists = errors.New("店铺名称已经存在") ErrShopAliasExists = errors.New("渠道店铺名称已经关联") ErrShopHasReferences = errors.New("店铺仍有关联数据") ) // ListShops 返回管理页的一店一行汇总。 func ListShops(q Execer) ([]model.Shop, error) { rows, err := q.Query(`SELECT s.shop_id,s.display_name,s.normalized_name,s.enabled, COUNT(DISTINCT sp.goods_id),s.created_by_user_id,s.created_at,s.updated_at FROM shops s LEFT JOIN shopee_products sp ON sp.shop_id=s.shop_id AND sp.deleted_at IS NULL GROUP BY s.shop_id ORDER BY s.enabled DESC,s.normalized_name`) if err != nil { return nil, fmt.Errorf("查询店铺列表失败: %w", err) } defer rows.Close() var result []model.Shop for rows.Next() { var item model.Shop var enabled int if err := rows.Scan(&item.ShopID, &item.DisplayName, &item.NormalizedName, &enabled, &item.ShopeeProductCount, &item.CreatedByUserID, &item.CreatedAt, &item.UpdatedAt); err != nil { return nil, err } item.Enabled = enabled == 1 result = append(result, item) } return result, rows.Err() } func GetShop(q Execer, shopID string) (model.Shop, bool, error) { var item model.Shop var enabled int err := q.QueryRow(`SELECT shop_id,display_name,normalized_name,enabled,created_by_user_id,created_at,updated_at FROM shops WHERE shop_id=?`, shopID).Scan(&item.ShopID, &item.DisplayName, &item.NormalizedName, &enabled, &item.CreatedByUserID, &item.CreatedAt, &item.UpdatedAt) if errors.Is(err, sql.ErrNoRows) { return model.Shop{}, false, nil } if err != nil { return model.Shop{}, false, fmt.Errorf("查询店铺失败: %w", err) } item.Enabled = enabled == 1 return item, true, nil } // ListEnabledSybShopMappings 返回“店铺精确名称 → 店铺 ID”。 func ListEnabledSybShopMappings(q Execer) (map[string]string, error) { rows, err := q.Query(`SELECT normalized_name,shop_id FROM shops WHERE enabled=1 ORDER BY normalized_name`) if err != nil { return nil, fmt.Errorf("查询启用的 SYB 店铺配置失败: %w", err) } defer rows.Close() result := map[string]string{} for rows.Next() { var name, shopID string if err := rows.Scan(&name, &shopID); err != nil { return nil, err } result[name] = shopID } return result, rows.Err() } func FindShopIDByName(q Execer, rawName string) (string, error) { name := strings.TrimSpace(rawName) if name == "" { return "", nil } var shopID string err := q.QueryRow(`SELECT shop_id FROM shops WHERE normalized_name=?`, name).Scan(&shopID) if errors.Is(err, sql.ErrNoRows) { return "", nil } if err != nil { return "", fmt.Errorf("按店铺名称查询店铺失败: %w", err) } return shopID, nil } func InsertShop(q Execer, item model.Shop) error { _, err := q.Exec(`INSERT INTO shops(shop_id,display_name,normalized_name,enabled,created_by_user_id,created_at,updated_at) VALUES(?,?,?,?,?,?,?)`, item.ShopID, item.DisplayName, item.NormalizedName, item.Enabled, item.CreatedByUserID, item.CreatedAt, item.UpdatedAt) if err != nil { var mysqlErr *mysql.MySQLError if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 { return ErrShopNameExists } return fmt.Errorf("新增店铺失败: %w", err) } return nil } func InsertShopAlias(q Execer, item model.ShopChannelAlias) error { _, err := q.Exec(`INSERT INTO shop_channel_aliases(alias_id,shop_id,channel,alias_name,normalized_alias,enabled,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?)`, item.AliasID, item.ShopID, item.Channel, item.AliasName, item.NormalizedAlias, item.Enabled, item.CreatedAt, item.UpdatedAt) if err != nil { var mysqlErr *mysql.MySQLError if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 { return ErrShopAliasExists } return fmt.Errorf("新增渠道店铺名称失败: %w", err) } return nil } func UpdateShopName(q Execer, shopID, displayName, normalizedName, updatedAt string) (bool, error) { result, err := q.Exec(`UPDATE shops SET display_name=?,normalized_name=?,updated_at=? WHERE shop_id=?`, displayName, normalizedName, updatedAt, shopID) if err != nil { var mysqlErr *mysql.MySQLError if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 { return false, ErrShopNameExists } return false, fmt.Errorf("更新店铺名称失败: %w", err) } n, err := result.RowsAffected() return n == 1, err } func SetShopEnabled(q Execer, shopID string, enabled bool, updatedAt string) (bool, error) { result, err := q.Exec(`UPDATE shops SET enabled=?,updated_at=? WHERE shop_id=?`, enabled, updatedAt, shopID) if err != nil { return false, fmt.Errorf("更新店铺状态失败: %w", err) } n, err := result.RowsAffected() return n == 1, err } func SetShopCompatibilityAliasesEnabled(q Execer, shopID string, enabled bool, updatedAt string) error { _, err := q.Exec(`UPDATE shop_channel_aliases SET enabled=?,updated_at=? WHERE shop_id=?`, enabled, updatedAt, shopID) if err != nil { return fmt.Errorf("同步店铺兼容数据状态失败: %w", err) } return nil } func ReplaceShopAlias(q Execer, aliasID, shopID, channel, aliasName, updatedAt string, enabled bool) error { if _, err := q.Exec(`DELETE FROM shop_channel_aliases WHERE shop_id=? AND channel=?`, shopID, channel); err != nil { return fmt.Errorf("移除旧渠道店铺名称失败: %w", err) } if strings.TrimSpace(aliasName) == "" { return nil } return InsertShopAlias(q, model.ShopChannelAlias{AliasID: aliasID, ShopID: shopID, Channel: channel, AliasName: aliasName, NormalizedAlias: strings.TrimSpace(aliasName), Enabled: enabled, CreatedAt: updatedAt, UpdatedAt: updatedAt}) } // RebuildShopAssociations 按唯一店铺名称精确重建一个店铺的派生关联。 func RebuildShopAssociations(q Execer, shopID string) error { if _, err := q.Exec(`UPDATE syb_orders SET shop_id=NULL WHERE shop_id=?`, shopID); err != nil { return err } if _, err := q.Exec(`UPDATE shopee_products SET shop_id=NULL WHERE shop_id=?`, shopID); err != nil { return err } if _, err := q.Exec(`UPDATE syb_orders so JOIN shops s ON s.shop_id=? AND s.normalized_name=TRIM(so.shop_name) COLLATE utf8mb4_bin SET so.shop_id=s.shop_id`, shopID); err != nil { return err } if _, err := q.Exec(`UPDATE shopee_products sp JOIN shops s ON s.shop_id=? AND s.normalized_name=TRIM(sp.shopee_shop_name) COLLATE utf8mb4_bin SET sp.shop_id=s.shop_id`, shopID); err != nil { return err } return nil } func DeleteDisabledShop(q Execer, shopID string) (bool, error) { var references int if err := q.QueryRow(`SELECT (SELECT COUNT(*) FROM syb_orders WHERE shop_id=?)+ (SELECT COUNT(*) FROM shopee_products WHERE shop_id=?)`, shopID, shopID).Scan(&references); err != nil { return false, err } if references > 0 { return false, ErrShopHasReferences } result, err := q.Exec(`DELETE FROM shops WHERE shop_id=? AND enabled=0`, shopID) if err != nil { return false, fmt.Errorf("删除停用店铺失败: %w", err) } n, err := result.RowsAffected() return n == 1, err } func CountUnlinkedShopeeShops(q Execer) (int, error) { var count int err := q.QueryRow(`SELECT COUNT(*) FROM shopee_products WHERE deleted_at IS NULL AND shop_id IS NULL`).Scan(&count) return count, err }