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 }