feat(ECR-022): CrisisCare CrisisPolicy 只读并 Closed

新增 crisis_policies、admin.crisis.read、列表/试匹配 API 与 admin-h5「危机」页;禁 CrisisEvent 写/UGC/真支付。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-08-08 01:28:45 +08:00
co-authored by Cursor
parent 4b5e5bc63d
commit de224184a6
33 changed files with 746 additions and 6 deletions
+102
View File
@@ -0,0 +1,102 @@
package repository
import (
"context"
"errors"
"strings"
"time"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
)
// CrisisPolicyRow is CrisisCare CrisisPolicy catalog.
type CrisisPolicyRow struct {
ID uuid.UUID `json:"id"`
Code string `json:"code"`
Title string `json:"title"`
Severity string `json:"severity"`
Pattern string `json:"pattern"`
Action string `json:"action"`
HelplineText *string `json:"helpline_text,omitempty"`
Active bool `json:"active"`
System bool `json:"system"`
UpdatedAt time.Time `json:"updated_at"`
}
// CrisisMatch is one evaluate hit.
type CrisisMatch struct {
Code string `json:"code"`
Title string `json:"title"`
Severity string `json:"severity"`
Action string `json:"action"`
HelplineText *string `json:"helpline_text,omitempty"`
}
// ListCrisisPolicies returns policies active-first.
func (r *AdminRepo) ListCrisisPolicies(ctx context.Context) ([]CrisisPolicyRow, error) {
rows, err := r.Pool.Query(ctx, `
SELECT id, code, title, severity, pattern, action, helpline_text, active, system, updated_at
FROM crisis_policies
ORDER BY active DESC, severity DESC, code ASC`)
if err != nil {
return nil, err
}
defer rows.Close()
var out []CrisisPolicyRow
for rows.Next() {
var p CrisisPolicyRow
if err := rows.Scan(
&p.ID, &p.Code, &p.Title, &p.Severity, &p.Pattern, &p.Action, &p.HelplineText,
&p.Active, &p.System, &p.UpdatedAt,
); err != nil {
return nil, err
}
out = append(out, p)
}
return out, rows.Err()
}
// GetCrisisPolicy loads one policy.
func (r *AdminRepo) GetCrisisPolicy(ctx context.Context, id uuid.UUID) (*CrisisPolicyRow, error) {
var p CrisisPolicyRow
err := r.Pool.QueryRow(ctx, `
SELECT id, code, title, severity, pattern, action, helpline_text, active, system, updated_at
FROM crisis_policies WHERE id=$1`, id,
).Scan(
&p.ID, &p.Code, &p.Title, &p.Severity, &p.Pattern, &p.Action, &p.HelplineText,
&p.Active, &p.System, &p.UpdatedAt,
)
if errors.Is(err, pgx.ErrNoRows) {
return nil, err
}
if err != nil {
return nil, err
}
return &p, nil
}
// EvaluateCrisisPolicies runs substring match preview (ops only).
func (r *AdminRepo) EvaluateCrisisPolicies(ctx context.Context, text string) ([]CrisisMatch, error) {
policies, err := r.ListCrisisPolicies(ctx)
if err != nil {
return nil, err
}
lower := strings.ToLower(text)
var out []CrisisMatch
for _, p := range policies {
if !p.Active || p.Pattern == "" {
continue
}
if strings.Contains(lower, strings.ToLower(p.Pattern)) {
out = append(out, CrisisMatch{
Code: p.Code, Title: p.Title, Severity: p.Severity,
Action: p.Action, HelplineText: p.HelplineText,
})
}
}
if out == nil {
out = []CrisisMatch{}
}
return out, nil
}