将Singleton数据库类与单独的模型和服务包一起使用

My question is similar to How to create singleton DB class in GoLang but I'm having trouble getting it to work with separate models and services packages.

project/lib/database/mysql.go:

package database

import (
    "fmt"

    _ "github.com/go-sql-driver/mysql"
    "github.com/jinzhu/gorm"
)

type Manager struct {
    *gorm.DB
}

var Mgr *Manager

func init() {
    dsn := MysqlConnectionString("parseTime=true")
    tablePrefix := "demo"
    var err error

    gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
        return fmt.Sprintf("%v_%v", tablePrefix, defaultTableName)
    }

    db, err := gorm.Open("mysql", dsn)
    if err != nil {
        panic(err)
    }

    Mgr = &Manager{db}
}

project/lib/models/retailer_keys.go

package models

import (
    "fmt"
    "project/lib/database"
    "time"
)

type RetailerKeysInterface interface {
    RetailerKeys() ([]*RetailerKey, error)
}

type DB struct {
    database.Manager
}

type RetailerKey struct {
    Id         int        `json:"id"`
    RetailerId int        `json:"retailer_id"`
    Key        string     `json:"key"`
    Enabled    *bool      `json:"enabled"`
    CreatedAt  *time.Time `json:"created_at"`
    UpdatedAt  *time.Time `json:"updated_at"`
}

func (db *DB) RetailerKeys() ([]*RetailerKey, error) {
    var keys []*RetailerKey
    if err := db.Find(&keys).Error; err != nil {
        return nil, err
    }
    return keys, nil
}

project/lib/services/retailer_keys.go

import (
    "fmt"
    "strings"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/apigateway"
    "gopkg.in/volatiletech/null.v6"

    "project/lib/models"
    "project/lib/services/api_keys"
)

func GetKeys() ([]*models.RetailerKey, error) {
    var q models.RetailerKeysInterface

    keys, err := q.RetailerKeys()
    if err != nil {
        return nil, err
    }
    return keys, nil
}

func CreateKey(id int) (models.RetailerKey, error) {
    ...
}

Then be able to use it in my main package like:

package main

import (
    "context"
    "encoding/json"
    // "reflect"
    "fmt"

    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"

    _ "project/lib/config"
    "project/lib/services"
)

func Handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    statusCode := 200

    keys, err := services.GetKeys()
    if err != nil {
        statusCode = 400
    }
    body, _ := json.Marshal(keys)

    return events.APIGatewayProxyResponse{
        Body:       string(body),
        StatusCode: statusCode,
    }, nil
}

...

I'd like to be able to embed the relevant subset of the Manager type in my models.

EDIT: Edited the question/code based on feedback in comments.

This gives me an error: runtime error: invalid memory address or nil pointer dereference.

I was definitely misunderstanding interfaces in go. After going through A Tour of Go it started to become clearer how it all fits together.

This is that I ended up doing for anyone that is going through the same thing. I'll leave the original question up so you can see the differences.

project/lib/database/mysql.go:

package database

import (
    "fmt"
    "log"

    _ "github.com/go-sql-driver/mysql" // Needed for gorm
    "github.com/jinzhu/gorm"
)

var Manager *gorm.DB

func init() {
    var err error

    dsn := MysqlConnectionString("parseTime=true")
    tablePrefix := "qcommerce"

    gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
        return fmt.Sprintf("%v_%v", tablePrefix, defaultTableName)
    }

    Manager, err = gorm.Open("mysql", dsn)
    if err != nil {
        log.Fatal(err)
    }

    if err := Manager.DB().Ping(); err != nil {
        log.Fatal(err)
    }
}

project/lib/models/retailer_keys.go

package models

import (
    "project/lib/database"
    "time"
)

type QRetailerKeys interface {
    Insert() error
    Find() error
}

type RetailerKey struct {
    ID                  int        `json:"id"`
    RetailerID          int        `json:"retailer_id"`
    Retailer            Retailer   `json:"retailer"`
    Key                 string     `json:"key"`
    Enabled             bool       `json:"enabled" gorm:"DEFAULT:true"`
    CreatedAt           *time.Time `json:"created_at"`
    UpdatedAt           *time.Time `json:"updated_at"`
}

// RetailerKeys returns a slice of all keys in table
func RetailerKeys() ([]*RetailerKey, error) {
    var keys []*RetailerKey
    if err := database.Manager.Find(&keys).Error; err != nil {
        return nil, err
    }
    return keys, nil
}

func (r *RetailerKey) Find() error {
    ...
}

// Create a new key
func (r *RetailerKey) Create() error {
    return database.Manager.Create(&r).Error
}

project/lib/services/retailer_keys.go

package services

import (
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    // "github.com/aws/aws-sdk-go/service/apigateway"

    "partners.drinks.com/lib/models"
    "partners.drinks.com/lib/services/api_keys"
)

func sessionBuilder() *session.Session {
    config := &aws.Config{
        Region: aws.String("us-west-2"),
    }
    session := session.Must(session.NewSession(config))
    return session
}

func GetKeys() ([]*models.RetailerKey, error) {
    keys, err := models.RetailerKeys()
    if err != nil {
        return nil, err
    }
    return keys, nil
}

func CreateKey(id int) (models.RetailerKey, error) {
    apikeys := &api_keys.ApiKeyBuilder{}
    base64Key := apikeys.GenUUID().GenKey().Base64

    var key = models.RetailerKey{
        RetailerID: id,
        Key: base64Key,
        Enabled: func(b bool)
    }

    if err := key.Create(); err != nil {
        return models.RetailerKey{}, err
    }
    ...

    return key, nil
}

I use it like:

package main

import (
    "context"
    "encoding/json"

    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"

    _ "partners.drinks.com/lib/config"
    "partners.drinks.com/lib/services"
)


func Handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    statusCode := 200

    keys, err := services.GetKeys()
    if err != nil {
        statusCode = 400
    }
    body, _ := json.Marshal(keys)

    return events.APIGatewayProxyResponse{
        Body:       string(body),
        StatusCode: statusCode,
    }, nil
}

...

Thanks to @mkopriva for the linked resources in the comments.