Files
multica/server/pkg/db/generated/project.sql.go
Naiyuan Qing 334c143ff0 feat(teams): per-user membership model with wholesale member configuration
Membership rows gain a fractional sort_order (migration 135) — the
user's personal sidebar order, Linear's TeamMembership.sortOrder shape.
System placement is atomic and mandatory: workspace creation, invitation
acceptance, and direct member-add all join the default team (hard
failure instead of the old silent skip, so a system-placed member can
never be team-less). Team creation makes the creator lead and can pull
in members.

The member set is configured wholesale via PUT /api/teams/{id}/members
(anyone can edit anyone — membership only drives the sidebar and
personal defaults, never access): kept rows are untouched, added members
land at the end of their own order, and an empty list is rejected since
zero members means archiving, which the client does explicitly after a
confirm. GET members and PATCH membership (sidebar drag sort) complete
the surface. User-facing wording says identifier; the API keeps 'key',
mirroring Linear's own Team.key.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 17:21:54 +08:00

464 lines
12 KiB
Go

// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: project.sql
package db
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const addProjectTeam = `-- name: AddProjectTeam :exec
INSERT INTO project_team (workspace_id, project_id, team_id)
VALUES ($1, $2, $3)
ON CONFLICT (project_id, team_id) DO NOTHING
`
type AddProjectTeamParams struct {
WorkspaceID pgtype.UUID `json:"workspace_id"`
ProjectID pgtype.UUID `json:"project_id"`
TeamID pgtype.UUID `json:"team_id"`
}
func (q *Queries) AddProjectTeam(ctx context.Context, arg AddProjectTeamParams) error {
_, err := q.db.Exec(ctx, addProjectTeam, arg.WorkspaceID, arg.ProjectID, arg.TeamID)
return err
}
const countIssuesByProject = `-- name: CountIssuesByProject :one
SELECT count(*) FROM issue
WHERE project_id = $1
`
func (q *Queries) CountIssuesByProject(ctx context.Context, projectID pgtype.UUID) (int64, error) {
row := q.db.QueryRow(ctx, countIssuesByProject, projectID)
var count int64
err := row.Scan(&count)
return count, err
}
const createProject = `-- name: CreateProject :one
INSERT INTO project (
workspace_id, title, description, icon, status,
lead_type, lead_id, priority
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8
) RETURNING id, workspace_id, title, description, icon, status, lead_type, lead_id, created_at, updated_at, priority
`
type CreateProjectParams struct {
WorkspaceID pgtype.UUID `json:"workspace_id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Icon pgtype.Text `json:"icon"`
Status string `json:"status"`
LeadType pgtype.Text `json:"lead_type"`
LeadID pgtype.UUID `json:"lead_id"`
Priority string `json:"priority"`
}
func (q *Queries) CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error) {
row := q.db.QueryRow(ctx, createProject,
arg.WorkspaceID,
arg.Title,
arg.Description,
arg.Icon,
arg.Status,
arg.LeadType,
arg.LeadID,
arg.Priority,
)
var i Project
err := row.Scan(
&i.ID,
&i.WorkspaceID,
&i.Title,
&i.Description,
&i.Icon,
&i.Status,
&i.LeadType,
&i.LeadID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Priority,
)
return i, err
}
const deleteProject = `-- name: DeleteProject :exec
DELETE FROM project WHERE id = $1 AND workspace_id = $2
`
type DeleteProjectParams struct {
ID pgtype.UUID `json:"id"`
WorkspaceID pgtype.UUID `json:"workspace_id"`
}
// Defense-in-depth: workspace_id is a SQL-layer tenant guard. See DeleteIssue.
func (q *Queries) DeleteProject(ctx context.Context, arg DeleteProjectParams) error {
_, err := q.db.Exec(ctx, deleteProject, arg.ID, arg.WorkspaceID)
return err
}
const getProject = `-- name: GetProject :one
SELECT id, workspace_id, title, description, icon, status, lead_type, lead_id, created_at, updated_at, priority FROM project
WHERE id = $1
`
func (q *Queries) GetProject(ctx context.Context, id pgtype.UUID) (Project, error) {
row := q.db.QueryRow(ctx, getProject, id)
var i Project
err := row.Scan(
&i.ID,
&i.WorkspaceID,
&i.Title,
&i.Description,
&i.Icon,
&i.Status,
&i.LeadType,
&i.LeadID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Priority,
)
return i, err
}
const getProjectInWorkspace = `-- name: GetProjectInWorkspace :one
SELECT id, workspace_id, title, description, icon, status, lead_type, lead_id, created_at, updated_at, priority FROM project
WHERE id = $1 AND workspace_id = $2
`
type GetProjectInWorkspaceParams struct {
ID pgtype.UUID `json:"id"`
WorkspaceID pgtype.UUID `json:"workspace_id"`
}
func (q *Queries) GetProjectInWorkspace(ctx context.Context, arg GetProjectInWorkspaceParams) (Project, error) {
row := q.db.QueryRow(ctx, getProjectInWorkspace, arg.ID, arg.WorkspaceID)
var i Project
err := row.Scan(
&i.ID,
&i.WorkspaceID,
&i.Title,
&i.Description,
&i.Icon,
&i.Status,
&i.LeadType,
&i.LeadID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Priority,
)
return i, err
}
const getProjectIssueStats = `-- name: GetProjectIssueStats :many
SELECT project_id,
count(*)::bigint AS total_count,
count(*) FILTER (WHERE status IN ('done', 'cancelled'))::bigint AS done_count
FROM issue
WHERE project_id = ANY($1::uuid[])
GROUP BY project_id
`
type GetProjectIssueStatsRow struct {
ProjectID pgtype.UUID `json:"project_id"`
TotalCount int64 `json:"total_count"`
DoneCount int64 `json:"done_count"`
}
func (q *Queries) GetProjectIssueStats(ctx context.Context, projectIds []pgtype.UUID) ([]GetProjectIssueStatsRow, error) {
rows, err := q.db.Query(ctx, getProjectIssueStats, projectIds)
if err != nil {
return nil, err
}
defer rows.Close()
items := []GetProjectIssueStatsRow{}
for rows.Next() {
var i GetProjectIssueStatsRow
if err := rows.Scan(&i.ProjectID, &i.TotalCount, &i.DoneCount); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listProjectTeams = `-- name: ListProjectTeams :many
SELECT wt.id, wt.workspace_id, wt.name, wt.key, wt.description, wt.icon, wt.issue_counter, wt.is_default, wt.archived_at, wt.archived_by, wt.created_by, wt.created_at, wt.updated_at FROM workspace_team wt
JOIN project_team pt ON pt.team_id = wt.id AND pt.workspace_id = wt.workspace_id
WHERE pt.workspace_id = $1
AND pt.project_id = $2
ORDER BY wt.is_default DESC, wt.name ASC, wt.created_at ASC
`
type ListProjectTeamsParams struct {
WorkspaceID pgtype.UUID `json:"workspace_id"`
ProjectID pgtype.UUID `json:"project_id"`
}
func (q *Queries) ListProjectTeams(ctx context.Context, arg ListProjectTeamsParams) ([]WorkspaceTeam, error) {
rows, err := q.db.Query(ctx, listProjectTeams, arg.WorkspaceID, arg.ProjectID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []WorkspaceTeam{}
for rows.Next() {
var i WorkspaceTeam
if err := rows.Scan(
&i.ID,
&i.WorkspaceID,
&i.Name,
&i.Key,
&i.Description,
&i.Icon,
&i.IssueCounter,
&i.IsDefault,
&i.ArchivedAt,
&i.ArchivedBy,
&i.CreatedBy,
&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 listProjectTeamsByProjects = `-- name: ListProjectTeamsByProjects :many
SELECT pt.project_id, wt.id, wt.workspace_id, wt.name, wt.key, wt.description,
wt.icon, wt.issue_counter, wt.is_default, wt.archived_at, wt.archived_by,
wt.created_by, wt.created_at, wt.updated_at
FROM project_team pt
JOIN workspace_team wt ON wt.id = pt.team_id AND wt.workspace_id = pt.workspace_id
WHERE pt.workspace_id = $1
AND pt.project_id = ANY($2::uuid[])
ORDER BY pt.project_id, wt.is_default DESC, wt.name ASC, wt.created_at ASC
`
type ListProjectTeamsByProjectsParams struct {
WorkspaceID pgtype.UUID `json:"workspace_id"`
ProjectIds []pgtype.UUID `json:"project_ids"`
}
type ListProjectTeamsByProjectsRow struct {
ProjectID pgtype.UUID `json:"project_id"`
ID pgtype.UUID `json:"id"`
WorkspaceID pgtype.UUID `json:"workspace_id"`
Name string `json:"name"`
Key string `json:"key"`
Description string `json:"description"`
Icon pgtype.Text `json:"icon"`
IssueCounter int32 `json:"issue_counter"`
IsDefault bool `json:"is_default"`
ArchivedAt pgtype.Timestamptz `json:"archived_at"`
ArchivedBy pgtype.UUID `json:"archived_by"`
CreatedBy pgtype.UUID `json:"created_by"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}
func (q *Queries) ListProjectTeamsByProjects(ctx context.Context, arg ListProjectTeamsByProjectsParams) ([]ListProjectTeamsByProjectsRow, error) {
rows, err := q.db.Query(ctx, listProjectTeamsByProjects, arg.WorkspaceID, arg.ProjectIds)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListProjectTeamsByProjectsRow{}
for rows.Next() {
var i ListProjectTeamsByProjectsRow
if err := rows.Scan(
&i.ProjectID,
&i.ID,
&i.WorkspaceID,
&i.Name,
&i.Key,
&i.Description,
&i.Icon,
&i.IssueCounter,
&i.IsDefault,
&i.ArchivedAt,
&i.ArchivedBy,
&i.CreatedBy,
&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 listProjects = `-- name: ListProjects :many
SELECT id, workspace_id, title, description, icon, status, lead_type, lead_id, created_at, updated_at, priority FROM project
WHERE project.workspace_id = $1
AND ($2::uuid IS NULL OR EXISTS (
SELECT 1 FROM project_team pt
WHERE pt.project_id = project.id
AND pt.workspace_id = project.workspace_id
AND pt.team_id = $2::uuid
))
AND ($3::text IS NULL OR project.status = $3)
AND ($4::text IS NULL OR project.priority = $4)
ORDER BY created_at DESC
`
type ListProjectsParams struct {
WorkspaceID pgtype.UUID `json:"workspace_id"`
TeamID pgtype.UUID `json:"team_id"`
Status pgtype.Text `json:"status"`
Priority pgtype.Text `json:"priority"`
}
func (q *Queries) ListProjects(ctx context.Context, arg ListProjectsParams) ([]Project, error) {
rows, err := q.db.Query(ctx, listProjects,
arg.WorkspaceID,
arg.TeamID,
arg.Status,
arg.Priority,
)
if err != nil {
return nil, err
}
defer rows.Close()
items := []Project{}
for rows.Next() {
var i Project
if err := rows.Scan(
&i.ID,
&i.WorkspaceID,
&i.Title,
&i.Description,
&i.Icon,
&i.Status,
&i.LeadType,
&i.LeadID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Priority,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const projectHasTeam = `-- name: ProjectHasTeam :one
SELECT EXISTS (
SELECT 1 FROM project_team
WHERE workspace_id = $1
AND project_id = $2
AND team_id = $3
)::boolean
`
type ProjectHasTeamParams struct {
WorkspaceID pgtype.UUID `json:"workspace_id"`
ProjectID pgtype.UUID `json:"project_id"`
TeamID pgtype.UUID `json:"team_id"`
}
func (q *Queries) ProjectHasTeam(ctx context.Context, arg ProjectHasTeamParams) (bool, error) {
row := q.db.QueryRow(ctx, projectHasTeam, arg.WorkspaceID, arg.ProjectID, arg.TeamID)
var column_1 bool
err := row.Scan(&column_1)
return column_1, err
}
const replaceProjectTeams = `-- name: ReplaceProjectTeams :exec
WITH deleted AS (
DELETE FROM project_team
WHERE workspace_id = $1
AND project_id = $2
AND NOT (team_id = ANY($3::uuid[]))
)
INSERT INTO project_team (workspace_id, project_id, team_id)
SELECT $1, $2, unnest($3::uuid[])
ON CONFLICT (project_id, team_id) DO NOTHING
`
type ReplaceProjectTeamsParams struct {
WorkspaceID pgtype.UUID `json:"workspace_id"`
ProjectID pgtype.UUID `json:"project_id"`
TeamIds []pgtype.UUID `json:"team_ids"`
}
func (q *Queries) ReplaceProjectTeams(ctx context.Context, arg ReplaceProjectTeamsParams) error {
_, err := q.db.Exec(ctx, replaceProjectTeams, arg.WorkspaceID, arg.ProjectID, arg.TeamIds)
return err
}
const updateProject = `-- name: UpdateProject :one
UPDATE project SET
title = COALESCE($2, title),
description = $3,
icon = $4,
status = COALESCE($5, status),
priority = COALESCE($6, priority),
lead_type = $7,
lead_id = $8,
updated_at = now()
WHERE id = $1
RETURNING id, workspace_id, title, description, icon, status, lead_type, lead_id, created_at, updated_at, priority
`
type UpdateProjectParams struct {
ID pgtype.UUID `json:"id"`
Title pgtype.Text `json:"title"`
Description pgtype.Text `json:"description"`
Icon pgtype.Text `json:"icon"`
Status pgtype.Text `json:"status"`
Priority pgtype.Text `json:"priority"`
LeadType pgtype.Text `json:"lead_type"`
LeadID pgtype.UUID `json:"lead_id"`
}
func (q *Queries) UpdateProject(ctx context.Context, arg UpdateProjectParams) (Project, error) {
row := q.db.QueryRow(ctx, updateProject,
arg.ID,
arg.Title,
arg.Description,
arg.Icon,
arg.Status,
arg.Priority,
arg.LeadType,
arg.LeadID,
)
var i Project
err := row.Scan(
&i.ID,
&i.WorkspaceID,
&i.Title,
&i.Description,
&i.Icon,
&i.Status,
&i.LeadType,
&i.LeadID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Priority,
)
return i, err
}