Help us shape the future of Go! Take the 2026 Go Developer Survey

resources

package module
v0.0.0-...-a70fa55 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2026 License: AGPL-3.0 Imports: 14 Imported by: 0

README

StuHelper Resources

资料模块管理分类、课程关联、资料版本、审核发布、访问授权和目录下载包。文件字节交给 media,学术对象通过 catalog 校验,审核通过 moderation 处理。各模块使用独立数据库身份。

课件、往年题、教材和习题来自可修改的初始配置。分类使用稳定 ID,字段支持文本、整数和选项。往年题的年份只是配置中的整数型字段,可筛选和分组,业务逻辑不根据分类名称分支。

分类修订和发布版本保留快照。新版本待审时继续展示和下载旧版本。下载和打包检查当前资格、访问权及资料状态,普通下架保留已购交付,安全停用阻止全部下载。

检查与接入

make setup
make generate
bash scripts/check-with-postgres.sh

检查使用独立 PostgreSQL 集群,覆盖事务、权限、并发幂等、审核连续性和恢复。OpenAPI 生成 Go 与 TypeScript 客户端,HTTP 同时按该契约校验请求。

迁移身份执行 postgres.Migrate,运行身份通过 CheckRuntime 检查。服务通过 Dependencies 接入当前身份与资格、目录、文件和审核接口。账务是可选能力,未接入时不提供购买功能,单独授权仍可使用。

当前本仓测试使用可控外部接口。应用装配、页面、真实账号与生产验收在对应仓库分别记录。完整规则见模块规格,许可证为 AGPL-3.0-only。

Documentation

Overview

Package resources owns learning-resource publications and configurable taxonomy. Files, academic references, moderation and entitlements are supplied through ports.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInput           = errors.New("resources_invalid_input")
	ErrUnauthenticated = errors.New("resources_authentication_required")
	ErrDenied          = errors.New("resources_permission_denied")
	ErrNotFound        = errors.New("resources_not_found")
	ErrConflict        = errors.New("resources_conflict")
	ErrUnavailable     = errors.New("resources_unavailable")
	ErrCapacity        = errors.New("resources_capacity_exceeded")
)

Functions

func ValidateAttributes

func ValidateAttributes(c Category, values map[string]Value, filter bool) error

func ValidateCategory

func ValidateCategory(c CategoryWrite) error

ValidateCategory checks the configurable schema, never a category name or ID.

Types

type Associations

type Associations struct {
	SchoolID   string `json:"school_id"`
	CourseID   string `json:"course_id,omitempty"`
	TermID     string `json:"term_id,omitempty"`
	OfferingID string `json:"offering_id,omitempty"`
	TeacherID  string `json:"teacher_id,omitempty"`
}

type Authority

type Authority interface {
	Check(context.Context, Principal, string, string) error
}

Authority supplies current action eligibility and administration scope. Upload and download decisions include the configured campus/phone facts. Missing facts return unavailable; a permission grant cannot silently stand in for a fact.

type Capabilities

type Capabilities struct {
	UploadProfiles    []UploadProfile `json:"upload_profiles"`
	Terms             Terms           `json:"terms"`
	AllowedTypes      []string        `json:"allowed_types"`
	MaxFileSize       int64           `json:"max_file_size"`
	MaxPackageBytes   int64           `json:"max_package_bytes"`
	MaxPackageFiles   int             `json:"max_package_files"`
	PurchaseAvailable bool            `json:"purchase_available"`
}

type Catalog

type Catalog interface {
	Validate(context.Context, Associations) error
}

type Category

type Category struct {
	ID       string  `json:"id"`
	Revision int64   `json:"revision"`
	Names    Names   `json:"names"`
	Enabled  bool    `json:"enabled"`
	Order    int     `json:"order"`
	Fields   []Field `json:"fields"`
	GroupBy  string  `json:"group_by"`
}

type CategoryPage

type CategoryPage struct {
	Items      []Category `json:"items"`
	NextCursor string     `json:"next_cursor"`
}

type CategoryWrite

type CategoryWrite struct {
	Revision int64   `json:"revision"`
	Names    Names   `json:"names"`
	Enabled  bool    `json:"enabled"`
	Order    int     `json:"order"`
	Fields   []Field `json:"fields"`
	GroupBy  string  `json:"group_by"`
}

type Command

type Command struct {
	Revision int64 `json:"revision"`
}

type Config

type Config struct {
	UploadProfiles                                                                          []UploadProfile
	Clock                                                                                   func() time.Time
	Terms                                                                                   Terms
	AllowedTypes                                                                            []string
	MaxFileSize, MaxPackageBytes                                                            int64
	MaxPackageFiles, MaxPackageParts, MaxDirectoryDepth, MaxRecords, MaxOperations, MaxJobs int
	OperationTimeout, PackageTTL, Lease                                                     time.Duration
	MaxAttempts                                                                             int
}
type Consent struct {
	Accepted bool   `json:"accepted"`
	Version  string `json:"version"`
	Digest   string `json:"digest"`
}

type Decision

type Decision struct {
	ID        string `json:"id"`
	Revision  int64  `json:"revision"`
	VersionID string `json:"version_id"`
	Action    string `json:"action"`
	Reason    string `json:"reason"`
}

type Dependencies

type Dependencies struct {
	Authority    Authority
	Catalog      Catalog
	Files        Files
	Moderation   Moderation
	Entitlements Entitlements
}

type Directory

type Directory struct {
	ID       string `json:"id"`
	Revision int64  `json:"revision"`
	SchoolID string `json:"school_id"`
	CourseID string `json:"course_id,omitempty"`
	ParentID string `json:"parent_id,omitempty"`
	Names    Names  `json:"names"`
	Enabled  bool   `json:"enabled"`
}

type DirectoryPage

type DirectoryPage struct {
	Items      []Directory `json:"items"`
	NextCursor string      `json:"next_cursor"`
}

type DirectoryWrite

type DirectoryWrite struct {
	Revision int64  `json:"revision"`
	SchoolID string `json:"school_id"`
	CourseID string `json:"course_id,omitempty"`
	ParentID string `json:"parent_id,omitempty"`
	Names    Names  `json:"names"`
	Enabled  bool   `json:"enabled"`
}

type DraftWrite

type DraftWrite struct {
	Revision int64    `json:"revision"`
	Metadata Metadata `json:"metadata"`
}

type Entitlement

type Entitlement struct {
	Purchased bool
	Member    bool
}

type Entitlements

type Entitlements interface {
	Lookup(context.Context, Principal, string) (Entitlement, error)
}

type Event

type Event struct {
	ID, Type, ResourceID, SchoolID, Actor string
	Revision                              int64
	Time                                  time.Time
}

type Field

type Field struct {
	Key        string   `json:"key"`
	Names      Names    `json:"names"`
	Kind       string   `json:"kind"`
	Required   bool     `json:"required"`
	Filterable bool     `json:"filterable"`
	Groupable  bool     `json:"groupable"`
	Min        *int64   `json:"min,omitempty"`
	Max        *int64   `json:"max,omitempty"`
	MaxLength  int      `json:"max_length,omitempty"`
	Options    []Option `json:"options,omitempty"`
}

type File

type File struct {
	VersionID   string `json:"version_id"`
	Filename    string `json:"filename"`
	ContentType string `json:"content_type"`
	Size        int64  `json:"size"`
	SHA256      string `json:"sha256"`
}

type FileEvidence

type FileEvidence struct {
	File                            File
	Owner                           Principal
	Module, State, Access, SchoolID string
}
type FileLink struct {
	Revision       int64  `json:"revision"`
	MediaVersionID string `json:"media_version_id"`
}

type Files

type Files interface {
	Inspect(context.Context, Principal, string) (FileEvidence, error)
	Publish(context.Context, Publication) (reference string, ready bool, err error)
	Release(context.Context, string, string) error
	Download(context.Context, Principal, string, io.Writer) error
	Archive(context.Context, Principal, []Selection, io.Writer) error
	ReviewDownload(context.Context, Principal, string, string, io.Writer) error
}

type Filter

type Filter struct {
	SchoolID, CourseID, CategoryID, DirectoryID, Query, After, OwnerKey string
	Attributes                                                          map[string]Value
	Limit                                                               int
	IncludeUnpublished                                                  bool
}

type Grant

type Grant struct {
	SubjectKey string     `json:"subject_key"`
	ID         string     `json:"id"`
	Revision   int64      `json:"revision"`
	ExpiresAt  *time.Time `json:"expires_at,omitempty"`
	Revoked    bool       `json:"revoked"`
}

type GrantPage

type GrantPage struct {
	Items      []Grant `json:"items"`
	NextCursor string  `json:"next_cursor"`
}

type GrantRecord

type GrantRecord struct {
	Grant                  Grant
	ResourceID, SubjectKey string
}

type GrantWrite

type GrantWrite struct {
	Revision   int64      `json:"revision"`
	SubjectKey string     `json:"subject_key"`
	ExpiresAt  *time.Time `json:"expires_at,omitempty"`
	Revoked    bool       `json:"revoked"`
}

type Inspection

type Inspection struct {
	ResourceID, VersionID, SchoolID, Owner, Title, Description, State string
	Revision                                                          int64
	Visible                                                           bool
}

Inspection is the minimal current content state for the moderation router. The account must have the current school-scoped moderation permission.

type Job

type Job struct {
	ID, Kind, ResourceID, VersionID, RequestID, ReferenceID, State, Owner, ErrorCode string
	Generation                                                                       int64
	Attempts                                                                         int
	LeaseUntil, AvailableAt                                                          time.Time
}

type Metadata

type Metadata struct {
	Title            string           `json:"title"`
	Description      string           `json:"description"`
	Tags             []string         `json:"tags"`
	CategoryID       string           `json:"category_id"`
	CategoryRevision int64            `json:"category_revision"`
	Attributes       map[string]Value `json:"attributes"`
	Associations     Associations     `json:"associations"`
	Access           string           `json:"access"`
	Origin           Origin           `json:"origin"`
	DirectoryID      string           `json:"directory_id,omitempty"`
}

type Names

type Names map[string]string

type Option

type Option struct {
	ID    string `json:"id"`
	Names Names  `json:"names"`
}

type Origin

type Origin struct {
	Kind    string   `json:"kind"`
	Source  string   `json:"source,omitempty"`
	Author  string   `json:"author,omitempty"`
	License string   `json:"license,omitempty"`
	Consent *Consent `json:"consent,omitempty"`
}

type Package

type Package struct {
	ID        string        `json:"id"`
	Parts     []PackagePart `json:"parts"`
	ExpiresAt time.Time     `json:"expires_at"`
}

type PackagePart

type PackagePart struct {
	Number int    `json:"number"`
	Files  []File `json:"files"`
	Bytes  int64  `json:"bytes"`
}

type PackageRecord

type PackageRecord struct {
	Package    Package
	Owner      Principal
	Selections [][]Selection
}

type PackageRequest

type PackageRequest struct {
	ResourceIDs []string `json:"resource_ids,omitempty"`
	DirectoryID string   `json:"directory_id,omitempty"`
}

type Page

type Page struct {
	Items      []Resource `json:"items"`
	NextCursor string     `json:"next_cursor"`
}

type Principal

type Principal struct {
	Issuer  string `json:"issuer"`
	Subject string `json:"subject"`
}

func (Principal) Key

func (p Principal) Key() string

func (Principal) Valid

func (p Principal) Valid() bool

type Publication

type Publication struct {
	ResourceID, VersionID, MediaVersionID, OperationID, Access string
	Owner                                                      Principal
}

type Record

type Record struct {
	ID          string
	Owner       Principal
	SchoolID    string
	Revision    int64
	State       string
	ActiveID    string
	ProposalID  string
	NextVersion int64
	CreatedAt   time.Time
}

Record and VersionRecord are private repository/port models, never HTTP DTOs.

type Replay

type Replay struct {
	Key, Digest string
	Result      json.RawMessage
}

type Repository

type Repository interface {
	Transaction(context.Context, func(Transaction) error) error
	Claim(context.Context, string, time.Time, time.Duration) (Job, error)
	PendingEvents(context.Context, int) ([]Event, error)
	AcknowledgeEvent(context.Context, string) error
}

type Request

type Request struct {
	PreviousTaskID string   `json:"previous_task_id,omitempty"`
	Kind           string   `json:"kind"`
	Reason         string   `json:"reason"`
	Contact        string   `json:"contact,omitempty"`
	Evidence       []string `json:"evidence,omitempty"`
}

type RequestPage

type RequestPage struct {
	Items      []RequestReceipt `json:"items"`
	NextCursor string           `json:"next_cursor"`
}

type RequestReceipt

type RequestReceipt struct {
	TaskID         string    `json:"task_id,omitempty"`
	ResourceID     string    `json:"resource_id,omitempty"`
	Kind           string    `json:"kind,omitempty"`
	DecisionReason string    `json:"decision_reason,omitempty"`
	CanAppeal      bool      `json:"can_appeal"`
	ID             string    `json:"id"`
	State          string    `json:"state"`
	CreatedAt      time.Time `json:"created_at"`
}

type RequestRecord

type RequestRecord struct {
	Receipt    RequestReceipt
	ResourceID string
	VersionID  string
	Reporter   Principal
	Request    Request
	ExternalID string
}

type RequestStatus

type RequestStatus struct {
	State, Reason string
	CanAppeal     bool
}

type Resource

type Resource struct {
	ID              string   `json:"id"`
	Revision        int64    `json:"revision"`
	State           string   `json:"state"`
	Current         *Version `json:"current,omitempty"`
	Proposal        *Version `json:"proposal,omitempty"`
	DownloadAllowed bool     `json:"download_allowed"`
	AccessReason    string   `json:"access_reason"`
	Owned           bool     `json:"owned"`
}

type ReviewSubmission

type ReviewSubmission struct {
	ResourceID, VersionID, SchoolID, Title, Description string
	Revision                                            int64
}

type Selection

type Selection struct {
	ResourceID, VersionID, ReferenceID string
	File                               File
}

type Service

type Service struct {
	// contains filtered or unexported fields
}

func New

func New(repo Repository, deps Dependencies, cfg Config) (*Service, error)

func (*Service) ApplyContentDecision

func (s *Service) ApplyContentDecision(ctx context.Context, p Principal, in Decision) (Resource, error)

ApplyContentDecision is the moderation delivery boundary. A single durable replay key covers both initial review and later state changes, so a retry after an ambiguous response does not reinterpret a now-published version.

func (*Service) ApplyDecision

func (s *Service) ApplyDecision(ctx context.Context, p Principal, id, key string, in Decision) (Resource, error)

func (*Service) AttachFile

func (s *Service) AttachFile(ctx context.Context, p Principal, id, key string, in FileLink) (Resource, error)

func (*Service) AuthorizeFile

func (s *Service) AuthorizeFile(ctx context.Context, p Principal, versionID, mediaVersionID, action string) error

AuthorizeFile is used by the media adapter before every file action. Reference ownership comes from the persisted resources version; clients cannot supply it.

func (*Service) Capabilities

func (s *Service) Capabilities() Capabilities

func (*Service) Categories

func (s *Service) Categories(ctx context.Context, limit int, after string, includeDisabled bool) (CategoryPage, error)

func (*Service) CreateDraft

func (s *Service) CreateDraft(ctx context.Context, p Principal, key string, in DraftWrite) (Resource, error)

func (*Service) CreatePackage

func (s *Service) CreatePackage(ctx context.Context, p Principal, key string, in PackageRequest) (Package, error)

func (*Service) CreateRequest

func (s *Service) CreateRequest(ctx context.Context, p Principal, id, key string, in Request) (RequestReceipt, error)

func (*Service) Directories

func (s *Service) Directories(ctx context.Context, p Principal, school, course, parent string, n int, after string) (DirectoryPage, error)

func (*Service) Download

func (s *Service) Download(ctx context.Context, p Principal, id string, w io.Writer) (File, error)

func (*Service) DownloadPackage

func (s *Service) DownloadPackage(ctx context.Context, p Principal, id string, part int, w io.Writer) error

func (*Service) DownloadReviewFile

func (s *Service) DownloadReviewFile(ctx context.Context, p Principal, versionID string, w io.Writer) error

func (*Service) Get

func (s *Service) Get(ctx context.Context, p Principal, id string) (Resource, error)

func (*Service) GetPackage

func (s *Service) GetPackage(ctx context.Context, p Principal, id string) (PackageRecord, error)

func (*Service) Grants

func (s *Service) Grants(ctx context.Context, p Principal, id string, n int, after string) (GrantPage, error)

func (*Service) InspectReview

func (s *Service) InspectReview(ctx context.Context, p Principal, versionID string) (Inspection, error)

func (*Service) List

func (s *Service) List(ctx context.Context, p Principal, f Filter, mine, admin bool) (Page, error)

func (*Service) ProcessOne

func (s *Service) ProcessOne(ctx context.Context, worker string) (bool, error)

ProcessOne claims one recoverable operation. The host owns concurrency and shutdown; a cancelled attempt remains leased until it can safely be reclaimed.

func (*Service) PutCategory

func (s *Service) PutCategory(ctx context.Context, p Principal, id, key string, input CategoryWrite) (Category, error)

func (*Service) PutDirectory

func (s *Service) PutDirectory(ctx context.Context, p Principal, id, key string, in DirectoryWrite) (Directory, error)

func (*Service) PutGrant

func (s *Service) PutGrant(ctx context.Context, p Principal, id, grantID, key string, in GrantWrite) (Grant, error)

func (*Service) RequestStatus

func (s *Service) RequestStatus(ctx context.Context, p Principal, id string) (RequestReceipt, error)

RequestStatus returns only the submitting user's receipt and safe decision text. Private reporter evidence remains in the moderation task and is never copied.

func (*Service) Requests

func (s *Service) Requests(ctx context.Context, p Principal, n int, after string) (RequestPage, error)

Requests is a bounded owner-only receipt listing. Detail refresh performs the current moderation lookup without issuing one external call per list row.

func (*Service) RetryPending

func (s *Service) RetryPending(ctx context.Context, p Principal, id, key string, in Command) (Resource, error)

RetryPending resumes exhausted operations without changing their idempotency keys. Providers must return the earlier result after an ambiguous timeout.

func (*Service) RetryRequest

func (s *Service) RetryRequest(ctx context.Context, p Principal, id, key string) (RequestReceipt, error)

func (*Service) ReviewResource

func (s *Service) ReviewResource(ctx context.Context, p Principal, versionID string) (Resource, error)

func (*Service) SetState

func (s *Service) SetState(ctx context.Context, p Principal, id, key string, in StateChange) (Resource, error)

func (*Service) Submit

func (s *Service) Submit(ctx context.Context, p Principal, id, key string, in Command) (Resource, error)

func (*Service) UpdateDraft

func (s *Service) UpdateDraft(ctx context.Context, p Principal, id, key string, in DraftWrite) (Resource, error)

type StateChange

type StateChange struct {
	Revision int64  `json:"revision"`
	State    string `json:"state"`
	Reason   string `json:"reason"`
}

type Terms

type Terms struct {
	Version string `json:"version"`
	Digest  string `json:"digest"`
	URL     string `json:"url"`
}

type Transaction

type Transaction interface {
	Lock(string) error
	Category(string, int64) (Category, error)
	SaveCategory(Category) error
	Categories(int, string, bool) ([]Category, error)
	Record(string) (Record, error)
	SaveRecord(Record) error
	Version(string) (VersionRecord, error)
	SaveVersion(VersionRecord) error
	List(Filter) ([]Record, error)
	Counts() (records, operations, jobs int, err error)
	Replay(string) (Replay, error)
	SaveReplay(Replay) error
	Grant(string, string) (GrantRecord, error)
	GrantByID(string) (GrantRecord, error)
	Grants(string, int, string) ([]GrantRecord, error)
	SaveGrant(GrantRecord) error
	Directory(string) (Directory, error)
	SaveDirectory(Directory) error
	Directories(string, string, string, int, string) ([]Directory, error)
	Package(string) (PackageRecord, error)
	SavePackage(PackageRecord) error
	Request(string) (RequestRecord, error)
	Requests(Principal, int, string) ([]RequestRecord, error)
	SaveRequest(RequestRecord) error
	Job(string) (Job, error)
	SaveJob(Job) error
	Event(Event) error
}

type UploadProfile

type UploadProfile struct {
	SchoolID string `json:"school_id"`
	Access   string `json:"access"`
	Purpose  string `json:"purpose"`
}

type Value

type Value struct {
	Text   *string `json:"text,omitempty"`
	Number *int64  `json:"number,omitempty"`
	Option *string `json:"option,omitempty"`
}

type Version

type Version struct {
	ID        string    `json:"id"`
	Number    int64     `json:"number"`
	Metadata  Metadata  `json:"metadata"`
	Category  Category  `json:"category"`
	File      *File     `json:"file,omitempty"`
	State     string    `json:"state"`
	CreatedAt time.Time `json:"created_at"`
}

type VersionRecord

type VersionRecord struct {
	Version          Version
	ResourceID       string
	FileOwner        Principal
	ReferenceID      string
	ModerationTaskID string
	DecisionID       string
	DecisionRevision int64
}

Directories

Path Synopsis
Package contracts supplies the same OpenAPI document used to generate clients.
Package contracts supplies the same OpenAPI document used to generate clients.
Package httpapi translates bounded resource requests and stable errors.
Package httpapi translates bounded resource requests and stable errors.
Package postgres owns the resources schema and data access identity.
Package postgres owns the resources schema and data access identity.
Package sdk provides primitives to interact with the openapi HTTP API.
Package sdk provides primitives to interact with the openapi HTTP API.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL