feat(project): domain types and sqlc queries for project/requirement/issue

This commit is contained in:
2026-05-01 09:39:16 +08:00
parent c5ed9e8622
commit f60d30a63d
10 changed files with 1223 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
package projectsqlc
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
)
type DBTX interface {
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
QueryRow(context.Context, string, ...interface{}) pgx.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
return &Queries{
db: tx,
}
}
+292
View File
@@ -0,0 +1,292 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: issues.sql
package projectsqlc
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const closeIssue = `-- name: CloseIssue :exec
UPDATE issues SET status = 'closed', closed_at = now(), updated_at = now()
WHERE id = $1 AND status = 'open'
`
func (q *Queries) CloseIssue(ctx context.Context, id pgtype.UUID) error {
_, err := q.db.Exec(ctx, closeIssue, id)
return err
}
const createIssue = `-- name: CreateIssue :one
INSERT INTO issues (id, project_id, requirement_id, number, title, description, assignee_id, created_by)
VALUES (
$1, $2, $3,
(SELECT COALESCE(MAX(number), 0) + 1 FROM issues WHERE project_id = $2),
$4, $5, $6, $7
)
RETURNING id, project_id, requirement_id, number, title, description, status,
assignee_id, created_by, closed_at, created_at, updated_at
`
type CreateIssueParams struct {
ID pgtype.UUID `json:"id"`
ProjectID pgtype.UUID `json:"project_id"`
RequirementID pgtype.UUID `json:"requirement_id"`
Title string `json:"title"`
Description string `json:"description"`
AssigneeID pgtype.UUID `json:"assignee_id"`
CreatedBy pgtype.UUID `json:"created_by"`
}
func (q *Queries) CreateIssue(ctx context.Context, arg CreateIssueParams) (Issue, error) {
row := q.db.QueryRow(ctx, createIssue,
arg.ID,
arg.ProjectID,
arg.RequirementID,
arg.Title,
arg.Description,
arg.AssigneeID,
arg.CreatedBy,
)
var i Issue
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.RequirementID,
&i.Number,
&i.Title,
&i.Description,
&i.Status,
&i.AssigneeID,
&i.CreatedBy,
&i.ClosedAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getIssueByNumber = `-- name: GetIssueByNumber :one
SELECT id, project_id, requirement_id, number, title, description, status,
assignee_id, created_by, closed_at, created_at, updated_at
FROM issues
WHERE project_id = $1 AND number = $2
`
type GetIssueByNumberParams struct {
ProjectID pgtype.UUID `json:"project_id"`
Number int32 `json:"number"`
}
func (q *Queries) GetIssueByNumber(ctx context.Context, arg GetIssueByNumberParams) (Issue, error) {
row := q.db.QueryRow(ctx, getIssueByNumber, arg.ProjectID, arg.Number)
var i Issue
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.RequirementID,
&i.Number,
&i.Title,
&i.Description,
&i.Status,
&i.AssigneeID,
&i.CreatedBy,
&i.ClosedAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const listIssues = `-- name: ListIssues :many
SELECT id, project_id, requirement_id, number, title, description, status,
assignee_id, created_by, closed_at, created_at, updated_at
FROM issues
WHERE project_id = $1
AND ($2::text IS NULL OR status = $2)
AND (
$3::text IS NULL
OR ($3 = 'none' AND requirement_id IS NULL)
OR ($3 = 'id' AND requirement_id = $4)
)
AND ($5::uuid IS NULL OR assignee_id = $5)
ORDER BY number DESC
`
type ListIssuesParams struct {
ProjectID pgtype.UUID `json:"project_id"`
Status *string `json:"status"`
RequirementFilter *string `json:"requirement_filter"`
RequirementID pgtype.UUID `json:"requirement_id"`
AssigneeID pgtype.UUID `json:"assignee_id"`
}
func (q *Queries) ListIssues(ctx context.Context, arg ListIssuesParams) ([]Issue, error) {
rows, err := q.db.Query(ctx, listIssues,
arg.ProjectID,
arg.Status,
arg.RequirementFilter,
arg.RequirementID,
arg.AssigneeID,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Issue
for rows.Next() {
var i Issue
if err := rows.Scan(
&i.ID,
&i.ProjectID,
&i.RequirementID,
&i.Number,
&i.Title,
&i.Description,
&i.Status,
&i.AssigneeID,
&i.CreatedBy,
&i.ClosedAt,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listIssuesByRequirement = `-- name: ListIssuesByRequirement :many
SELECT id, project_id, requirement_id, number, title, description, status,
assignee_id, created_by, closed_at, created_at, updated_at
FROM issues
WHERE requirement_id = $1
ORDER BY number DESC
`
func (q *Queries) ListIssuesByRequirement(ctx context.Context, requirementID pgtype.UUID) ([]Issue, error) {
rows, err := q.db.Query(ctx, listIssuesByRequirement, requirementID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Issue
for rows.Next() {
var i Issue
if err := rows.Scan(
&i.ID,
&i.ProjectID,
&i.RequirementID,
&i.Number,
&i.Title,
&i.Description,
&i.Status,
&i.AssigneeID,
&i.CreatedBy,
&i.ClosedAt,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const reopenIssue = `-- name: ReopenIssue :exec
UPDATE issues SET status = 'open', closed_at = NULL, updated_at = now()
WHERE id = $1 AND status = 'closed'
`
func (q *Queries) ReopenIssue(ctx context.Context, id pgtype.UUID) error {
_, err := q.db.Exec(ctx, reopenIssue, id)
return err
}
const updateIssueAssignee = `-- name: UpdateIssueAssignee :one
UPDATE issues
SET assignee_id = $2, updated_at = now()
WHERE id = $1
RETURNING id, project_id, requirement_id, number, title, description, status,
assignee_id, created_by, closed_at, created_at, updated_at
`
type UpdateIssueAssigneeParams struct {
ID pgtype.UUID `json:"id"`
AssigneeID pgtype.UUID `json:"assignee_id"`
}
func (q *Queries) UpdateIssueAssignee(ctx context.Context, arg UpdateIssueAssigneeParams) (Issue, error) {
row := q.db.QueryRow(ctx, updateIssueAssignee, arg.ID, arg.AssigneeID)
var i Issue
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.RequirementID,
&i.Number,
&i.Title,
&i.Description,
&i.Status,
&i.AssigneeID,
&i.CreatedBy,
&i.ClosedAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const updateIssueCore = `-- name: UpdateIssueCore :one
UPDATE issues
SET title = $2,
description = $3,
requirement_id = $4,
updated_at = now()
WHERE id = $1
RETURNING id, project_id, requirement_id, number, title, description, status,
assignee_id, created_by, closed_at, created_at, updated_at
`
type UpdateIssueCoreParams struct {
ID pgtype.UUID `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
RequirementID pgtype.UUID `json:"requirement_id"`
}
func (q *Queries) UpdateIssueCore(ctx context.Context, arg UpdateIssueCoreParams) (Issue, error) {
row := q.db.QueryRow(ctx, updateIssueCore,
arg.ID,
arg.Title,
arg.Description,
arg.RequirementID,
)
var i Issue
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.RequirementID,
&i.Number,
&i.Title,
&i.Description,
&i.Status,
&i.AssigneeID,
&i.CreatedBy,
&i.ClosedAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
+95
View File
@@ -0,0 +1,95 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
package projectsqlc
import (
"net/netip"
"github.com/jackc/pgx/v5/pgtype"
)
type AuditLog struct {
ID int64 `json:"id"`
UserID pgtype.UUID `json:"user_id"`
Action string `json:"action"`
TargetType *string `json:"target_type"`
TargetID *string `json:"target_id"`
Ip *netip.Addr `json:"ip"`
Metadata []byte `json:"metadata"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type Issue struct {
ID pgtype.UUID `json:"id"`
ProjectID pgtype.UUID `json:"project_id"`
RequirementID pgtype.UUID `json:"requirement_id"`
Number int32 `json:"number"`
Title string `json:"title"`
Description string `json:"description"`
Status string `json:"status"`
AssigneeID pgtype.UUID `json:"assignee_id"`
CreatedBy pgtype.UUID `json:"created_by"`
ClosedAt pgtype.Timestamptz `json:"closed_at"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type Notification struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
Topic string `json:"topic"`
Severity string `json:"severity"`
Title string `json:"title"`
Body string `json:"body"`
Link *string `json:"link"`
Metadata []byte `json:"metadata"`
ReadAt pgtype.Timestamptz `json:"read_at"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
type Project struct {
ID pgtype.UUID `json:"id"`
Slug string `json:"slug"`
Name string `json:"name"`
Description string `json:"description"`
Visibility string `json:"visibility"`
OwnerID pgtype.UUID `json:"owner_id"`
ArchivedAt pgtype.Timestamptz `json:"archived_at"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type Requirement struct {
ID pgtype.UUID `json:"id"`
ProjectID pgtype.UUID `json:"project_id"`
Number int32 `json:"number"`
Title string `json:"title"`
Description string `json:"description"`
Phase string `json:"phase"`
Status string `json:"status"`
OwnerID pgtype.UUID `json:"owner_id"`
ClosedAt pgtype.Timestamptz `json:"closed_at"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type User struct {
ID pgtype.UUID `json:"id"`
Email string `json:"email"`
PasswordHash string `json:"password_hash"`
DisplayName string `json:"display_name"`
IsAdmin bool `json:"is_admin"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
type UserSession struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
TokenHash []byte `json:"token_hash"`
ExpiresAt pgtype.Timestamptz `json:"expires_at"`
LastSeenAt pgtype.Timestamptz `json:"last_seen_at"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
+191
View File
@@ -0,0 +1,191 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: projects.sql
package projectsqlc
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const archiveProject = `-- name: ArchiveProject :exec
UPDATE projects SET archived_at = now(), updated_at = now()
WHERE id = $1 AND archived_at IS NULL
`
func (q *Queries) ArchiveProject(ctx context.Context, id pgtype.UUID) error {
_, err := q.db.Exec(ctx, archiveProject, id)
return err
}
const createProject = `-- name: CreateProject :one
INSERT INTO projects (id, slug, name, description, visibility, owner_id)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING id, slug, name, description, visibility, owner_id, archived_at, created_at, updated_at
`
type CreateProjectParams struct {
ID pgtype.UUID `json:"id"`
Slug string `json:"slug"`
Name string `json:"name"`
Description string `json:"description"`
Visibility string `json:"visibility"`
OwnerID pgtype.UUID `json:"owner_id"`
}
func (q *Queries) CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error) {
row := q.db.QueryRow(ctx, createProject,
arg.ID,
arg.Slug,
arg.Name,
arg.Description,
arg.Visibility,
arg.OwnerID,
)
var i Project
err := row.Scan(
&i.ID,
&i.Slug,
&i.Name,
&i.Description,
&i.Visibility,
&i.OwnerID,
&i.ArchivedAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getProjectByID = `-- name: GetProjectByID :one
SELECT id, slug, name, description, visibility, owner_id, archived_at, created_at, updated_at
FROM projects WHERE id = $1
`
func (q *Queries) GetProjectByID(ctx context.Context, id pgtype.UUID) (Project, error) {
row := q.db.QueryRow(ctx, getProjectByID, id)
var i Project
err := row.Scan(
&i.ID,
&i.Slug,
&i.Name,
&i.Description,
&i.Visibility,
&i.OwnerID,
&i.ArchivedAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getProjectBySlug = `-- name: GetProjectBySlug :one
SELECT id, slug, name, description, visibility, owner_id, archived_at, created_at, updated_at
FROM projects WHERE slug = $1
`
func (q *Queries) GetProjectBySlug(ctx context.Context, slug string) (Project, error) {
row := q.db.QueryRow(ctx, getProjectBySlug, slug)
var i Project
err := row.Scan(
&i.ID,
&i.Slug,
&i.Name,
&i.Description,
&i.Visibility,
&i.OwnerID,
&i.ArchivedAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const listProjects = `-- name: ListProjects :many
SELECT id, slug, name, description, visibility, owner_id, archived_at, created_at, updated_at
FROM projects
WHERE ($1::bool OR archived_at IS NULL)
ORDER BY created_at DESC
`
func (q *Queries) ListProjects(ctx context.Context, includeArchived bool) ([]Project, error) {
rows, err := q.db.Query(ctx, listProjects, includeArchived)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Project
for rows.Next() {
var i Project
if err := rows.Scan(
&i.ID,
&i.Slug,
&i.Name,
&i.Description,
&i.Visibility,
&i.OwnerID,
&i.ArchivedAt,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const unarchiveProject = `-- name: UnarchiveProject :exec
UPDATE projects SET archived_at = NULL, updated_at = now()
WHERE id = $1 AND archived_at IS NOT NULL
`
func (q *Queries) UnarchiveProject(ctx context.Context, id pgtype.UUID) error {
_, err := q.db.Exec(ctx, unarchiveProject, id)
return err
}
const updateProject = `-- name: UpdateProject :one
UPDATE projects
SET name = $2,
description = $3,
visibility = $4,
updated_at = now()
WHERE id = $1
RETURNING id, slug, name, description, visibility, owner_id, archived_at, created_at, updated_at
`
type UpdateProjectParams struct {
ID pgtype.UUID `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Visibility string `json:"visibility"`
}
func (q *Queries) UpdateProject(ctx context.Context, arg UpdateProjectParams) (Project, error) {
row := q.db.QueryRow(ctx, updateProject,
arg.ID,
arg.Name,
arg.Description,
arg.Visibility,
)
var i Project
err := row.Scan(
&i.ID,
&i.Slug,
&i.Name,
&i.Description,
&i.Visibility,
&i.OwnerID,
&i.ArchivedAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
+256
View File
@@ -0,0 +1,256 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: requirements.sql
package projectsqlc
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const closeRequirement = `-- name: CloseRequirement :exec
UPDATE requirements
SET status = 'closed', closed_at = now(), updated_at = now()
WHERE id = $1 AND status = 'open'
`
func (q *Queries) CloseRequirement(ctx context.Context, id pgtype.UUID) error {
_, err := q.db.Exec(ctx, closeRequirement, id)
return err
}
const createRequirement = `-- name: CreateRequirement :one
INSERT INTO requirements (id, project_id, number, title, description, owner_id)
VALUES (
$1, $2,
(SELECT COALESCE(MAX(number), 0) + 1 FROM requirements WHERE project_id = $2),
$3, $4, $5
)
RETURNING id, project_id, number, title, description, phase, status, owner_id,
closed_at, created_at, updated_at
`
type CreateRequirementParams struct {
ID pgtype.UUID `json:"id"`
ProjectID pgtype.UUID `json:"project_id"`
Title string `json:"title"`
Description string `json:"description"`
OwnerID pgtype.UUID `json:"owner_id"`
}
func (q *Queries) CreateRequirement(ctx context.Context, arg CreateRequirementParams) (Requirement, error) {
row := q.db.QueryRow(ctx, createRequirement,
arg.ID,
arg.ProjectID,
arg.Title,
arg.Description,
arg.OwnerID,
)
var i Requirement
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.Number,
&i.Title,
&i.Description,
&i.Phase,
&i.Status,
&i.OwnerID,
&i.ClosedAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getRequirementByID = `-- name: GetRequirementByID :one
SELECT id, project_id, number, title, description, phase, status, owner_id,
closed_at, created_at, updated_at
FROM requirements WHERE id = $1
`
func (q *Queries) GetRequirementByID(ctx context.Context, id pgtype.UUID) (Requirement, error) {
row := q.db.QueryRow(ctx, getRequirementByID, id)
var i Requirement
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.Number,
&i.Title,
&i.Description,
&i.Phase,
&i.Status,
&i.OwnerID,
&i.ClosedAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getRequirementByNumber = `-- name: GetRequirementByNumber :one
SELECT id, project_id, number, title, description, phase, status, owner_id,
closed_at, created_at, updated_at
FROM requirements
WHERE project_id = $1 AND number = $2
`
type GetRequirementByNumberParams struct {
ProjectID pgtype.UUID `json:"project_id"`
Number int32 `json:"number"`
}
func (q *Queries) GetRequirementByNumber(ctx context.Context, arg GetRequirementByNumberParams) (Requirement, error) {
row := q.db.QueryRow(ctx, getRequirementByNumber, arg.ProjectID, arg.Number)
var i Requirement
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.Number,
&i.Title,
&i.Description,
&i.Phase,
&i.Status,
&i.OwnerID,
&i.ClosedAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const listRequirements = `-- name: ListRequirements :many
SELECT id, project_id, number, title, description, phase, status, owner_id,
closed_at, created_at, updated_at
FROM requirements
WHERE project_id = $1
AND ($2::text IS NULL OR phase = $2)
AND ($3::text IS NULL OR status = $3)
ORDER BY number DESC
`
type ListRequirementsParams struct {
ProjectID pgtype.UUID `json:"project_id"`
Phase *string `json:"phase"`
Status *string `json:"status"`
}
func (q *Queries) ListRequirements(ctx context.Context, arg ListRequirementsParams) ([]Requirement, error) {
rows, err := q.db.Query(ctx, listRequirements, arg.ProjectID, arg.Phase, arg.Status)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Requirement
for rows.Next() {
var i Requirement
if err := rows.Scan(
&i.ID,
&i.ProjectID,
&i.Number,
&i.Title,
&i.Description,
&i.Phase,
&i.Status,
&i.OwnerID,
&i.ClosedAt,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const reopenRequirement = `-- name: ReopenRequirement :exec
UPDATE requirements
SET status = 'open', closed_at = NULL, updated_at = now()
WHERE id = $1 AND status = 'closed'
`
func (q *Queries) ReopenRequirement(ctx context.Context, id pgtype.UUID) error {
_, err := q.db.Exec(ctx, reopenRequirement, id)
return err
}
const updateRequirement = `-- name: UpdateRequirement :one
UPDATE requirements
SET title = $2,
description = $3,
owner_id = $4,
updated_at = now()
WHERE id = $1
RETURNING id, project_id, number, title, description, phase, status, owner_id,
closed_at, created_at, updated_at
`
type UpdateRequirementParams struct {
ID pgtype.UUID `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
OwnerID pgtype.UUID `json:"owner_id"`
}
func (q *Queries) UpdateRequirement(ctx context.Context, arg UpdateRequirementParams) (Requirement, error) {
row := q.db.QueryRow(ctx, updateRequirement,
arg.ID,
arg.Title,
arg.Description,
arg.OwnerID,
)
var i Requirement
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.Number,
&i.Title,
&i.Description,
&i.Phase,
&i.Status,
&i.OwnerID,
&i.ClosedAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const updateRequirementPhase = `-- name: UpdateRequirementPhase :one
UPDATE requirements
SET phase = $2, updated_at = now()
WHERE id = $1
RETURNING id, project_id, number, title, description, phase, status, owner_id,
closed_at, created_at, updated_at
`
type UpdateRequirementPhaseParams struct {
ID pgtype.UUID `json:"id"`
Phase string `json:"phase"`
}
func (q *Queries) UpdateRequirementPhase(ctx context.Context, arg UpdateRequirementPhaseParams) (Requirement, error) {
row := q.db.QueryRow(ctx, updateRequirementPhase, arg.ID, arg.Phase)
var i Requirement
err := row.Scan(
&i.ID,
&i.ProjectID,
&i.Number,
&i.Title,
&i.Description,
&i.Phase,
&i.Status,
&i.OwnerID,
&i.ClosedAt,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}