Module project, commit, submission
All checks were successful
Build and Release / release (push) Successful in 1m15s

This commit is contained in:
2026-04-26 16:31:03 +07:00
parent ac90236022
commit 6918a100fc
60 changed files with 5957 additions and 1020 deletions

View File

@@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
"history-api/internal/gen/sqlc"
@@ -24,6 +25,13 @@ type ProjectRepository interface {
Create(ctx context.Context, params sqlc.CreateProjectParams) (*models.ProjectEntity, error)
Update(ctx context.Context, params sqlc.UpdateProjectParams) (*models.ProjectEntity, error)
Delete(ctx context.Context, id pgtype.UUID) error
AddMember(ctx context.Context, params sqlc.AddProjectMemberParams) error
UpdateMemberRole(ctx context.Context, params sqlc.UpdateProjectMemberRoleParams) error
RemoveMember(ctx context.Context, params sqlc.RemoveProjectMemberParams) error
CheckPermission(ctx context.Context, params sqlc.CheckProjectPermissionParams) (int16, error)
ChangeOwner(ctx context.Context, params sqlc.ChangeProjectOwnerParams) error
UpdateLatestCommit(ctx context.Context, params sqlc.UpdateLatestCommitParams) error
WithTx(tx pgx.Tx) ProjectRepository
}
type projectRepository struct {
@@ -38,13 +46,18 @@ func NewProjectRepository(db sqlc.DBTX, c cache.Cache) ProjectRepository {
}
}
func (r *projectRepository) WithTx(tx pgx.Tx) ProjectRepository {
return &projectRepository{
q: r.q.WithTx(tx),
c: r.c,
}
}
func (r *projectRepository) generateQueryKey(prefix string, params any) string {
b, _ := json.Marshal(params)
hash := fmt.Sprintf("%x", md5.Sum(b))
return fmt.Sprintf("%s:%s", prefix, hash)
}
func (r *projectRepository) getByIDsWithFallback(ctx context.Context, ids []string) ([]*models.ProjectEntity, error) {
if len(ids) == 0 {
return []*models.ProjectEntity{}, nil
@@ -75,21 +88,21 @@ func (r *projectRepository) getByIDsWithFallback(ctx context.Context, ids []stri
if err == nil {
for _, row := range dbRows {
item := models.ProjectEntity{
ID: convert.UUIDToString(row.ID),
Title: row.Title,
Description: convert.TextToString(row.Description),
LatestRevisionID: convert.UUIDToStringPtr(row.LatestRevisionID),
VersionCount: row.VersionCount,
ProjectStatus: constants.ParseProjectStatusType(row.ProjectStatus),
LockedBy: convert.UUIDToStringPtr(row.LockedBy),
IsDeleted: row.IsDeleted,
UserID: convert.UUIDToString(row.UserID),
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
CommitIds: convert.ListUUIDToString(row.CommitIds),
SubmissionIds: convert.ListUUIDToString(row.SubmissionIds),
ID: convert.UUIDToString(row.ID),
Title: row.Title,
Description: convert.TextToString(row.Description),
LatestCommitID: convert.UUIDToStringPtr(row.LatestCommitID),
ProjectStatus: constants.ParseProjectStatusType(row.ProjectStatus),
LockedBy: convert.UUIDToStringPtr(row.LockedBy),
IsDeleted: row.IsDeleted,
UserID: convert.UUIDToString(row.UserID),
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
SubmissionIds: convert.ListUUIDToString(row.SubmissionIds),
}
_ = item.ParseUser(row.User)
_ = item.ParseCommits(row.Commits)
_ = item.ParseMembers(row.Members)
dbMap[item.ID] = &item
}
}
@@ -135,21 +148,21 @@ func (r *projectRepository) GetByID(ctx context.Context, id pgtype.UUID) (*model
}
project = models.ProjectEntity{
ID: convert.UUIDToString(row.ID),
Title: row.Title,
Description: convert.TextToString(row.Description),
LatestRevisionID: convert.UUIDToStringPtr(row.LatestRevisionID),
VersionCount: row.VersionCount,
ProjectStatus: constants.ParseProjectStatusType(row.ProjectStatus),
LockedBy: convert.UUIDToStringPtr(row.LockedBy),
IsDeleted: row.IsDeleted,
UserID: convert.UUIDToString(row.UserID),
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
CommitIds: convert.ListUUIDToString(row.CommitIds),
SubmissionIds: convert.ListUUIDToString(row.SubmissionIds),
ID: convert.UUIDToString(row.ID),
Title: row.Title,
Description: convert.TextToString(row.Description),
LatestCommitID: convert.UUIDToStringPtr(row.LatestCommitID),
ProjectStatus: constants.ParseProjectStatusType(row.ProjectStatus),
LockedBy: convert.UUIDToStringPtr(row.LockedBy),
IsDeleted: row.IsDeleted,
UserID: convert.UUIDToString(row.UserID),
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
SubmissionIds: convert.ListUUIDToString(row.SubmissionIds),
}
_ = project.ParseUser(row.User)
_ = project.ParseCommits(row.Commits)
_ = project.ParseMembers(row.Members)
_ = r.c.Set(ctx, cacheId, project, constants.NormalCacheDuration)
@@ -167,28 +180,28 @@ func (r *projectRepository) GetByUserID(ctx context.Context, params sqlc.GetProj
if err != nil {
return nil, err
}
var projects []*models.ProjectEntity
var ids []string
projectToCache := make(map[string]any)
for _, row := range rows {
project := &models.ProjectEntity{
ID: convert.UUIDToString(row.ID),
Title: row.Title,
Description: convert.TextToString(row.Description),
LatestRevisionID: convert.UUIDToStringPtr(row.LatestRevisionID),
VersionCount: row.VersionCount,
ProjectStatus: constants.ParseProjectStatusType(row.ProjectStatus),
LockedBy: convert.UUIDToStringPtr(row.LockedBy),
IsDeleted: row.IsDeleted,
UserID: convert.UUIDToString(row.UserID),
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
CommitIds: convert.ListUUIDToString(row.CommitIds),
SubmissionIds: convert.ListUUIDToString(row.SubmissionIds),
ID: convert.UUIDToString(row.ID),
Title: row.Title,
Description: convert.TextToString(row.Description),
LatestCommitID: convert.UUIDToStringPtr(row.LatestCommitID),
ProjectStatus: constants.ParseProjectStatusType(row.ProjectStatus),
LockedBy: convert.UUIDToStringPtr(row.LockedBy),
IsDeleted: row.IsDeleted,
UserID: convert.UUIDToString(row.UserID),
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
SubmissionIds: convert.ListUUIDToString(row.SubmissionIds),
}
_ = project.ParseUser(row.User)
_ = project.ParseCommits(row.Commits)
_ = project.ParseMembers(row.Members)
ids = append(ids, project.ID)
projects = append(projects, project)
@@ -222,21 +235,21 @@ func (r *projectRepository) Search(ctx context.Context, params sqlc.SearchProjec
for _, row := range rows {
project := &models.ProjectEntity{
ID: convert.UUIDToString(row.ID),
Title: row.Title,
Description: convert.TextToString(row.Description),
LatestRevisionID: convert.UUIDToStringPtr(row.LatestRevisionID),
VersionCount: row.VersionCount,
ProjectStatus: constants.ParseProjectStatusType(row.ProjectStatus),
LockedBy: convert.UUIDToStringPtr(row.LockedBy),
IsDeleted: row.IsDeleted,
UserID: convert.UUIDToString(row.UserID),
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
CommitIds: convert.ListUUIDToString(row.CommitIds),
SubmissionIds: convert.ListUUIDToString(row.SubmissionIds),
ID: convert.UUIDToString(row.ID),
Title: row.Title,
Description: convert.TextToString(row.Description),
LatestCommitID: convert.UUIDToStringPtr(row.LatestCommitID),
ProjectStatus: constants.ParseProjectStatusType(row.ProjectStatus),
LockedBy: convert.UUIDToStringPtr(row.LockedBy),
IsDeleted: row.IsDeleted,
UserID: convert.UUIDToString(row.UserID),
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
SubmissionIds: convert.ListUUIDToString(row.SubmissionIds),
}
_ = project.ParseUser(row.User)
_ = project.ParseCommits(row.Commits)
_ = project.ParseMembers(row.Members)
ids = append(ids, project.ID)
projects = append(projects, project)
@@ -276,23 +289,21 @@ func (r *projectRepository) Create(ctx context.Context, params sqlc.CreateProjec
}
project := models.ProjectEntity{
ID: convert.UUIDToString(row.ID),
Title: row.Title,
Description: convert.TextToString(row.Description),
LatestRevisionID: convert.UUIDToStringPtr(row.LatestRevisionID),
VersionCount: row.VersionCount,
ProjectStatus: constants.ParseProjectStatusType(row.ProjectStatus),
LockedBy: convert.UUIDToStringPtr(row.LockedBy),
IsDeleted: row.IsDeleted,
UserID: convert.UUIDToString(row.UserID),
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
CommitIds: convert.ListUUIDToString(row.CommitIds),
SubmissionIds: convert.ListUUIDToString(row.SubmissionIds),
ID: convert.UUIDToString(row.ID),
Title: row.Title,
Description: convert.TextToString(row.Description),
LatestCommitID: convert.UUIDToStringPtr(row.LatestCommitID),
ProjectStatus: constants.ParseProjectStatusType(row.ProjectStatus),
LockedBy: convert.UUIDToStringPtr(row.LockedBy),
IsDeleted: row.IsDeleted,
UserID: convert.UUIDToString(row.UserID),
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
SubmissionIds: convert.ListUUIDToString(row.SubmissionIds),
}
_ = project.ParseUser(row.User)
_ = r.c.Set(ctx, fmt.Sprintf("project:id:%s", project.ID), project, constants.NormalCacheDuration)
_ = project.ParseCommits(row.Commits)
_ = project.ParseMembers(row.Members)
go func() {
bgCtx := context.Background()
@@ -309,23 +320,23 @@ func (r *projectRepository) Update(ctx context.Context, params sqlc.UpdateProjec
return nil, err
}
project := models.ProjectEntity{
ID: convert.UUIDToString(row.ID),
Title: row.Title,
Description: convert.TextToString(row.Description),
LatestRevisionID: convert.UUIDToStringPtr(row.LatestRevisionID),
VersionCount: row.VersionCount,
ProjectStatus: constants.ParseProjectStatusType(row.ProjectStatus),
LockedBy: convert.UUIDToStringPtr(row.LockedBy),
IsDeleted: row.IsDeleted,
UserID: convert.UUIDToString(row.UserID),
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
CommitIds: convert.ListUUIDToString(row.CommitIds),
SubmissionIds: convert.ListUUIDToString(row.SubmissionIds),
ID: convert.UUIDToString(row.ID),
Title: row.Title,
Description: convert.TextToString(row.Description),
LatestCommitID: convert.UUIDToStringPtr(row.LatestCommitID),
ProjectStatus: constants.ParseProjectStatusType(row.ProjectStatus),
LockedBy: convert.UUIDToStringPtr(row.LockedBy),
IsDeleted: row.IsDeleted,
UserID: convert.UUIDToString(row.UserID),
CreatedAt: convert.TimeToPtr(row.CreatedAt),
UpdatedAt: convert.TimeToPtr(row.UpdatedAt),
SubmissionIds: convert.ListUUIDToString(row.SubmissionIds),
}
_ = project.ParseUser(row.User)
_ = project.ParseCommits(row.Commits)
_ = project.ParseMembers(row.Members)
_ = r.c.Set(ctx, fmt.Sprintf("project:id:%s", project.ID), project, constants.NormalCacheDuration)
_ = r.c.Del(ctx, fmt.Sprintf("project:id:%s", project.ID))
return &project, nil
}
@@ -337,9 +348,75 @@ func (r *projectRepository) Delete(ctx context.Context, id pgtype.UUID) error {
_ = r.c.Del(ctx, fmt.Sprintf("project:id:%s", convert.UUIDToString(id)))
go func() {
bgCtx := context.Background()
_ = r.c.DelByPattern(bgCtx, "project:search*")
_ = r.c.DelByPattern(bgCtx, "project:user*")
_ = r.c.DelByPattern(bgCtx, "project:count*")
}()
return nil
}
func (r *projectRepository) AddMember(ctx context.Context, params sqlc.AddProjectMemberParams) error {
_, err := r.q.AddProjectMember(ctx, params)
if err != nil {
return err
}
_ = r.c.Del(ctx, fmt.Sprintf("project:id:%s", convert.UUIDToString(params.ProjectID)))
_ = r.c.Del(ctx, fmt.Sprintf("project:perm:%s:%s", convert.UUIDToString(params.ProjectID), convert.UUIDToString(params.UserID)))
return nil
}
func (r *projectRepository) UpdateMemberRole(ctx context.Context, params sqlc.UpdateProjectMemberRoleParams) error {
_, err := r.q.UpdateProjectMemberRole(ctx, params)
if err != nil {
return err
}
_ = r.c.Del(ctx, fmt.Sprintf("project:id:%s", convert.UUIDToString(params.ProjectID)))
_ = r.c.Del(ctx, fmt.Sprintf("project:perm:%s:%s", convert.UUIDToString(params.ProjectID), convert.UUIDToString(params.UserID)))
return nil
}
func (r *projectRepository) RemoveMember(ctx context.Context, params sqlc.RemoveProjectMemberParams) error {
err := r.q.RemoveProjectMember(ctx, params)
if err != nil {
return err
}
_ = r.c.Del(ctx, fmt.Sprintf("project:id:%s", convert.UUIDToString(params.ProjectID)))
_ = r.c.Del(ctx, fmt.Sprintf("project:perm:%s:%s", convert.UUIDToString(params.ProjectID), convert.UUIDToString(params.UserID)))
return nil
}
func (r *projectRepository) CheckPermission(ctx context.Context, params sqlc.CheckProjectPermissionParams) (int16, error) {
cacheKey := fmt.Sprintf("project:perm:%s:%s", convert.UUIDToString(params.ProjectID), convert.UUIDToString(params.UserID))
var role int16
if err := r.c.Get(ctx, cacheKey, &role); err == nil {
return role, nil
}
role, err := r.q.CheckProjectPermission(ctx, params)
if err != nil {
return 0, err
}
_ = r.c.Set(ctx, cacheKey, role, constants.NormalCacheDuration)
return role, nil
}
func (r *projectRepository) ChangeOwner(ctx context.Context, params sqlc.ChangeProjectOwnerParams) error {
err := r.q.ChangeProjectOwner(ctx, params)
if err != nil {
return err
}
projectID := convert.UUIDToString(params.ID)
_ = r.c.Del(ctx, fmt.Sprintf("project:id:%s", projectID))
go func() {
_ = r.c.DelByPattern(context.Background(), fmt.Sprintf("project:perm:%s:*", projectID))
}()
return nil
}
func (r *projectRepository) UpdateLatestCommit(ctx context.Context, params sqlc.UpdateLatestCommitParams) error {
err := r.q.UpdateLatestCommit(ctx, params)
if err != nil {
return err
}
_ = r.c.Del(ctx, fmt.Sprintf("project:id:%s", convert.UUIDToString(params.ID)))
return nil
}