123 lines
3.3 KiB
Go
123 lines
3.3 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
|
|
"git.yuntuo.hk/k2safe/go-omni/examples/reference/model"
|
|
"github.com/go-sql-driver/mysql"
|
|
)
|
|
|
|
// MySQLUsers is the persistent implementation of service.UserRepository.
|
|
// The schema is documented in schema.sql; migrations stay outside the framework.
|
|
type MySQLUsers struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
func NewMySQLUsers(db *sql.DB) *MySQLUsers {
|
|
return &MySQLUsers{db: db}
|
|
}
|
|
|
|
func (r *MySQLUsers) Create(ctx context.Context, input model.UserInput) (model.User, error) {
|
|
result, err := r.db.ExecContext(ctx, `
|
|
INSERT INTO users (name, email, created_at, updated_at)
|
|
VALUES (?, ?, UTC_TIMESTAMP(6), UTC_TIMESTAMP(6))`, input.Name, input.Email)
|
|
if err != nil {
|
|
return model.User{}, mapMySQLError(err)
|
|
}
|
|
id, err := result.LastInsertId()
|
|
if err != nil {
|
|
return model.User{}, err
|
|
}
|
|
return r.Get(ctx, id)
|
|
}
|
|
|
|
func (r *MySQLUsers) Get(ctx context.Context, id int64) (model.User, error) {
|
|
var user model.User
|
|
err := r.db.QueryRowContext(ctx, `
|
|
SELECT id, name, email, created_at, updated_at
|
|
FROM users WHERE id = ?`, id).Scan(
|
|
&user.ID, &user.Name, &user.Email, &user.CreatedAt, &user.UpdatedAt,
|
|
)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return model.User{}, model.ErrUserNotFound
|
|
}
|
|
if err != nil {
|
|
return model.User{}, err
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
func (r *MySQLUsers) List(ctx context.Context, page, pageSize int) (model.UserPage, error) {
|
|
var total int
|
|
if err := r.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM users`).Scan(&total); err != nil {
|
|
return model.UserPage{}, err
|
|
}
|
|
result := model.UserPage{Items: []model.User{}, Total: total, Page: page, PageSize: pageSize}
|
|
if total == 0 || page-1 > (total-1)/pageSize {
|
|
return result, nil
|
|
}
|
|
offset := (page - 1) * pageSize
|
|
rows, err := r.db.QueryContext(ctx, `
|
|
SELECT id, name, email, created_at, updated_at
|
|
FROM users ORDER BY id LIMIT ? OFFSET ?`, pageSize, offset)
|
|
if err != nil {
|
|
return model.UserPage{}, err
|
|
}
|
|
defer rows.Close()
|
|
for rows.Next() {
|
|
var user model.User
|
|
if err := rows.Scan(&user.ID, &user.Name, &user.Email, &user.CreatedAt, &user.UpdatedAt); err != nil {
|
|
return model.UserPage{}, err
|
|
}
|
|
result.Items = append(result.Items, user)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return model.UserPage{}, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (r *MySQLUsers) Update(ctx context.Context, id int64, input model.UserInput) (model.User, error) {
|
|
result, err := r.db.ExecContext(ctx, `
|
|
UPDATE users SET name = ?, email = ?, updated_at = UTC_TIMESTAMP(6)
|
|
WHERE id = ?`, input.Name, input.Email, id)
|
|
if err != nil {
|
|
return model.User{}, mapMySQLError(err)
|
|
}
|
|
changed, err := result.RowsAffected()
|
|
if err != nil {
|
|
return model.User{}, err
|
|
}
|
|
if changed == 0 {
|
|
// MySQL may report zero affected rows when the replacement is identical;
|
|
// distinguish that from an ID that does not exist.
|
|
return r.Get(ctx, id)
|
|
}
|
|
return r.Get(ctx, id)
|
|
}
|
|
|
|
func (r *MySQLUsers) Delete(ctx context.Context, id int64) error {
|
|
result, err := r.db.ExecContext(ctx, `DELETE FROM users WHERE id = ?`, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
deleted, err := result.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if deleted == 0 {
|
|
return model.ErrUserNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func mapMySQLError(err error) error {
|
|
var mysqlErr *mysql.MySQLError
|
|
if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 {
|
|
return model.ErrEmailTaken
|
|
}
|
|
return err
|
|
}
|