search

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 10, 2026 License: AGPL-3.0 Imports: 12 Imported by: 0

README

search 提供学校代码、名称与别名检索,以及关键词搜索、热门课程与教师、我的投票查询。当前实现为独立 Go 库,由 backend 注入身份认证、来源公开接口和事件 relay。跨模块装配尚未完成。

课程与教师五维评分统计由 GET /api/v1/reviews/statistics 提供。客户端对 2–5 门课程分别读取 reviews 统计,组合展示课程对比。

OpenAPI 版本为 0.3.0,HTTP 路径与响应未变化,接口定义位于 contracts/search.openapi.json,Go 和 TypeScript SDK 位于 sdk/。详细数据约定、事件缺口及装配步骤见 模块说明。

热门活动由 catalog(浏览)、reviews(评价与互动)、favorites(收藏)分别提供日快照。活动事件使用 discovery.activity.v1,契约见 contracts/discovery-activity.schema.json(1.0.0),摘要见相邻 .sha256 文件。来源接口签名及字段规则见模块说明。

pnpm install --frozen-lockfile
pnpm run hooks
make generate
bash scripts/check-with-postgres.sh

数据库检查创建独立、仅 Unix socket 可访问的 PostgreSQL 集群,退出时清理。可用 STUHELPER_TEST_PG_BIN 指定 PostgreSQL 二进制目录。每项测试创建独立数据库,不连接业务数据库。

公开仓库为 StuHelper/stuhelper-search,默认分支为 develop,候选分支为 feat/backend-decisions,通过 草稿 PR #1 交付。

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInput           = errors.New("invalid_input")
	ErrUnauthenticated = errors.New("unauthenticated")
	ErrNotFound        = errors.New("not_found")
	ErrUnavailable     = errors.New("unavailable")
	ErrSourceDisabled  = errors.New("source_disabled")
	ErrConflict        = errors.New("event_conflict")
)

Functions

func ActivityID

func ActivityID(target Target, day string) string

ActivityID is independent of provider; the database key includes Source.

func Validate

func Validate(d Document) error

func ValidateActivity

func ValidateActivity(d ActivitySnapshot) error

Types

type ActivityEvent

type ActivityEvent struct {
	SpecVersion string           `json:"specversion"`
	ID          string           `json:"id"`
	Source      string           `json:"source"`
	Type        string           `json:"type"`
	Subject     string           `json:"subject"`
	Time        time.Time        `json:"time"`
	Data        ActivitySnapshot `json:"data"`
}

type ActivityFilter

type ActivityFilter struct {
	Kind, After string
	From, To    time.Time
	Limit       int
}

type ActivitySnapshot

type ActivitySnapshot struct {
	Source   string           `json:"source"`
	ID       string           `json:"id"`
	Target   Target           `json:"target"`
	Day      string           `json:"day"`
	Revision int64            `json:"revision"`
	Visible  bool             `json:"visible"`
	Metrics  map[string]int64 `json:"metrics"`
}

ActivitySnapshot is one provider's complete daily counters for one canonical object. Missing providers contribute zero. Metrics are never transferred between providers.

type Config

type Config struct {
	QueryTimeout       time.Duration
	HeatWindow         time.Duration
	HeatWeights        [4]float64
	MinimumHeatSamples int
	Now                func() time.Time
}

type Document

type Document struct {
	Code       string    `json:"code,omitempty"`
	Aliases    []string  `json:"aliases,omitempty"`
	Target     Target    `json:"target"`
	Revision   int64     `json:"revision"`
	Title      string    `json:"title"`
	SchoolID   string    `json:"school_id"`
	Visible    bool      `json:"visible"`
	CourseID   string    `json:"course_id,omitempty"`
	TeacherIDs []string  `json:"teacher_ids,omitempty"`
	At         time.Time `json:"at"`
}

Document is a source-owned public projection snapshot, never review body or identity data.

type Event

type Event struct {
	SpecVersion string    `json:"specversion"`
	ID          string    `json:"id"`
	Source      string    `json:"source"`
	Type        string    `json:"type"`
	Subject     string    `json:"subject"`
	Time        time.Time `json:"time"`
	Data        Document  `json:"data"`
}

type Filter

type Filter struct {
	Query, Kind, After string
	Limit              int
}

type Item

type Item struct {
	Target   Target `json:"target"`
	Title    string `json:"title"`
	SchoolID string `json:"school_id"`
}

type Page

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

type Repository

type Repository interface {
	Apply(context.Context, Event, string) error
	Candidates(context.Context, Filter) ([]Document, error)
	// Rebuild holds the projection write lock and atomically publishes all source snapshots.
	Rebuild(context.Context, func(func([]Document) error, func([]ActivitySnapshot) error) error) error
	ApplyActivity(context.Context, ActivityEvent, string) error
	Activities(context.Context, ActivityFilter) ([]ActivitySnapshot, error)
}

type Service

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

func New

func New(repo Repository, sources Sources, c Config) (*Service, error)

func (*Service) Consume

func (s *Service) Consume(ctx context.Context, e Event) error

func (*Service) ConsumeActivity

func (s *Service) ConsumeActivity(ctx context.Context, e ActivityEvent) error

func (*Service) MyVotes

func (s *Service) MyVotes(ctx context.Context, user, cursor string, limit int) (Votes, error)

func (*Service) Popular

func (s *Service) Popular(ctx context.Context, user, kind, cursor string, limit int) (Page, error)

func (*Service) Rebuild

func (s *Service) Rebuild(ctx context.Context) error

func (*Service) Search

func (s *Service) Search(ctx context.Context, user, q, kind, cursor string, limit int) (Page, error)

type Sources

type Sources interface {
	Current(context.Context, string, Target) (Document, bool, error)
	// DiscoverySnapshot returns at most limit records, including tombstones, using a consistent source snapshot.
	// A nonempty cursor must advance. Empty next marks the end. Disabled sources return ErrSourceDisabled.
	DiscoverySnapshot(context.Context, string, string, int) ([]Document, string, error)
	ActivitySnapshot(context.Context, string, time.Time, time.Time, string, int) ([]ActivitySnapshot, string, error)
	CurrentActivity(context.Context, string, string, string) (ActivitySnapshot, bool, error)
	MyVotes(context.Context, string, string, int) (Votes, error)
}

Sources must use module public APIs and the authenticated principal. Never read source tables. Current returns current readable metadata; false includes deletion, withdrawal and revoked access.

type Target

type Target struct {
	Source string `json:"source"`
	ID     string `json:"id"`
	Kind   string `json:"kind"`
}

type Vote

type Vote struct {
	Target Target `json:"target"`
	Choice string `json:"choice"`
}

type Votes

type Votes struct {
	Items      []Vote `json:"items"`
	NextCursor string `json:"next_cursor"`
}

Directories

Path Synopsis
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