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
+195
View File
@@ -0,0 +1,195 @@
// Package project 承载 Project / Requirement / Issue 三个 PM 聚合根的领域类型与
// Service 接口契约。本文件只做类型与签名声明,业务实现在 *_service.go。
//
// 三个聚合根共享同一个 Go 包是有意为之:它们在 schema 上紧耦合(Issue 引用
// Requirement,Requirement 属于 Project),且对外只暴露一组 chi route,拆包
// 反而要互相 import。
package project
import (
"context"
"time"
"github.com/google/uuid"
)
// Phase 是 Requirement 的五阶段枚举。phase 不强制单向流动,可任意跳跃/回退。
// 字符串常量保持与 DB CHECK 约束一致。
type Phase string
const (
PhasePlanning Phase = "planning"
PhaseAuditing Phase = "auditing"
PhaseImplementing Phase = "implementing"
PhaseReviewing Phase = "reviewing"
PhaseDone Phase = "done"
)
// AllPhases 用于校验 phase 字符串合法性,顺序也对应前端看板/流转条的列序。
var AllPhases = []Phase{PhasePlanning, PhaseAuditing, PhaseImplementing, PhaseReviewing, PhaseDone}
// IsValid 报告 p 是否在枚举集合内。
func (p Phase) IsValid() bool {
for _, x := range AllPhases {
if p == x {
return true
}
}
return false
}
// Status 是 Requirement / Issue 通用的开/关状态。
type Status string
const (
StatusOpen Status = "open"
StatusClosed Status = "closed"
)
// Visibility 控制 Project 的可见域:private 仅 owner+admin 可读,
// internal 所有登录用户可读;写权限两者都仅限 owner+admin。
type Visibility string
const (
VisibilityPrivate Visibility = "private"
VisibilityInternal Visibility = "internal"
)
// IsValid 报告 v 是否在枚举集合内。
func (v Visibility) IsValid() bool {
return v == VisibilityPrivate || v == VisibilityInternal
}
// Project 是顶层聚合根。ArchivedAt 为 nil 表示未归档。
type Project struct {
ID uuid.UUID
Slug string
Name string
Description string
Visibility Visibility
OwnerID uuid.UUID
ArchivedAt *time.Time
CreatedAt time.Time
UpdatedAt time.Time
}
// Requirement 表示一个项目内的需求。Number 在 (project_id) 内自增。
type Requirement struct {
ID uuid.UUID
ProjectID uuid.UUID
Number int
Title string
Description string
Phase Phase
Status Status
OwnerID uuid.UUID
ClosedAt *time.Time
CreatedAt time.Time
UpdatedAt time.Time
}
// Issue 可挂载到某个 Requirement,也可游离(RequirementID == nil)。
// AssigneeID 为 nil 表示未指派。CreatedBy 永远存在(DELETE RESTRICT)。
type Issue struct {
ID uuid.UUID
ProjectID uuid.UUID
RequirementID *uuid.UUID
Number int
Title string
Description string
Status Status
AssigneeID *uuid.UUID
CreatedBy uuid.UUID
ClosedAt *time.Time
CreatedAt time.Time
UpdatedAt time.Time
}
// Caller 表示发起请求的用户上下文。Service 层据此判定鉴权。
type Caller struct {
UserID uuid.UUID
IsAdmin bool
}
// ProjectService 暴露 Project 聚合根的应用服务方法。
type ProjectService interface {
Create(ctx context.Context, c Caller, in CreateProjectInput) (*Project, error)
Get(ctx context.Context, c Caller, slug string) (*Project, error)
List(ctx context.Context, c Caller, includeArchived bool) ([]*Project, error)
Update(ctx context.Context, c Caller, slug string, in UpdateProjectInput) (*Project, error)
Archive(ctx context.Context, c Caller, slug string) error
Unarchive(ctx context.Context, c Caller, slug string) error
}
// RequirementService 暴露 Requirement 聚合根的应用服务方法。
type RequirementService interface {
Create(ctx context.Context, c Caller, projectSlug string, in CreateRequirementInput) (*Requirement, error)
Get(ctx context.Context, c Caller, projectSlug string, number int) (*Requirement, error)
List(ctx context.Context, c Caller, projectSlug string, filter RequirementFilter) ([]*Requirement, error)
Update(ctx context.Context, c Caller, projectSlug string, number int, in UpdateRequirementInput) (*Requirement, error)
ChangePhase(ctx context.Context, c Caller, projectSlug string, number int, to Phase) (*Requirement, error)
Close(ctx context.Context, c Caller, projectSlug string, number int) error
Reopen(ctx context.Context, c Caller, projectSlug string, number int) error
}
// IssueService 暴露 Issue 聚合根的应用服务方法。
type IssueService interface {
Create(ctx context.Context, c Caller, projectSlug string, in CreateIssueInput) (*Issue, error)
Get(ctx context.Context, c Caller, projectSlug string, number int) (*Issue, error)
List(ctx context.Context, c Caller, projectSlug string, filter IssueFilter) ([]*Issue, error)
Update(ctx context.Context, c Caller, projectSlug string, number int, in UpdateIssueInput) (*Issue, error)
Assign(ctx context.Context, c Caller, projectSlug string, number int, assignee *uuid.UUID) (*Issue, error)
Close(ctx context.Context, c Caller, projectSlug string, number int) error
Reopen(ctx context.Context, c Caller, projectSlug string, number int) error
}
// ===== 输入 DTO =====
type CreateProjectInput struct {
Slug string
Name string
Description string
Visibility Visibility
}
type UpdateProjectInput struct {
Name *string
Description *string
Visibility *Visibility
}
type CreateRequirementInput struct {
Title string
Description string
OwnerID *uuid.UUID
}
type UpdateRequirementInput struct {
Title *string
Description *string
OwnerID *uuid.UUID
}
type RequirementFilter struct {
Phase Phase
Status Status
}
type CreateIssueInput struct {
Title string
Description string
RequirementNumber *int
AssigneeID *uuid.UUID
}
type UpdateIssueInput struct {
Title *string
Description *string
RequirementNumber **int
}
type IssueFilter struct {
Status Status
Requirement *int
AssigneeID *uuid.UUID
}
+61
View File
@@ -0,0 +1,61 @@
-- 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;
-- 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;
-- 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 (sqlc.narg('status')::text IS NULL OR status = sqlc.narg('status'))
AND (
sqlc.narg('requirement_filter')::text IS NULL
OR (sqlc.narg('requirement_filter') = 'none' AND requirement_id IS NULL)
OR (sqlc.narg('requirement_filter') = 'id' AND requirement_id = sqlc.narg('requirement_id'))
)
AND (sqlc.narg('assignee_id')::uuid IS NULL OR assignee_id = sqlc.narg('assignee_id'))
ORDER BY number DESC;
-- 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;
-- 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;
-- 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;
-- name: CloseIssue :exec
UPDATE issues SET status = 'closed', closed_at = now(), updated_at = now()
WHERE id = $1 AND status = 'open';
-- name: ReopenIssue :exec
UPDATE issues SET status = 'open', closed_at = NULL, updated_at = now()
WHERE id = $1 AND status = 'closed';
+35
View File
@@ -0,0 +1,35 @@
-- 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;
-- name: GetProjectBySlug :one
SELECT id, slug, name, description, visibility, owner_id, archived_at, created_at, updated_at
FROM projects WHERE slug = $1;
-- name: GetProjectByID :one
SELECT id, slug, name, description, visibility, owner_id, archived_at, created_at, updated_at
FROM projects WHERE id = $1;
-- name: ListProjects :many
SELECT id, slug, name, description, visibility, owner_id, archived_at, created_at, updated_at
FROM projects
WHERE (sqlc.arg('include_archived')::bool OR archived_at IS NULL)
ORDER BY created_at DESC;
-- 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;
-- name: ArchiveProject :exec
UPDATE projects SET archived_at = now(), updated_at = now()
WHERE id = $1 AND archived_at IS NULL;
-- name: UnarchiveProject :exec
UPDATE projects SET archived_at = NULL, updated_at = now()
WHERE id = $1 AND archived_at IS NOT NULL;
+56
View File
@@ -0,0 +1,56 @@
-- 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;
-- 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;
-- 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;
-- 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 (sqlc.narg('phase')::text IS NULL OR phase = sqlc.narg('phase'))
AND (sqlc.narg('status')::text IS NULL OR status = sqlc.narg('status'))
ORDER BY number DESC;
-- 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;
-- 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;
-- name: CloseRequirement :exec
UPDATE requirements
SET status = 'closed', closed_at = now(), updated_at = now()
WHERE id = $1 AND status = 'open';
-- name: ReopenRequirement :exec
UPDATE requirements
SET status = 'open', closed_at = NULL, updated_at = now()
WHERE id = $1 AND status = 'closed';
+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
}