feat(auth,prompt-rule): add password change with session revocation and scope prompt rules by brand
Add self-service password change that re-hashes the credential and bumps a per-user session version so all existing access/refresh tokens are rejected. Session version is tracked in Redis with an in-memory fallback and enforced in middleware and refresh. Scope prompt rules and rule groups to a brand: new brand_id column (migrated to a "未归属" fallback brand for existing rows), brand-filtered queries/indexes, and brand-aware admin-web tabs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,7 @@ type AuthRepository interface {
|
||||
GetUserByEmail(ctx context.Context, email string) (*UserRecord, error)
|
||||
GetUserByLoginIdentifier(ctx context.Context, identifier string) (*UserRecord, error)
|
||||
GetUserByID(ctx context.Context, id int64) (*UserRecord, error)
|
||||
UpdateUserPassword(ctx context.Context, id int64, passwordHash string) error
|
||||
GetTenantMembership(ctx context.Context, userID int64) (*MembershipRecord, error)
|
||||
GetTenantMembershipByTenantAndUser(ctx context.Context, tenantID, userID int64) (*MembershipRecord, error)
|
||||
}
|
||||
@@ -103,6 +104,13 @@ func (r *authRepository) GetUserByID(ctx context.Context, id int64) (*UserRecord
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *authRepository) UpdateUserPassword(ctx context.Context, id int64, passwordHash string) error {
|
||||
return r.q.UpdateUserPassword(ctx, generated.UpdateUserPasswordParams{
|
||||
ID: id,
|
||||
PasswordHash: passwordHash,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *authRepository) GetTenantMembership(ctx context.Context, userID int64) (*MembershipRecord, error) {
|
||||
row, err := r.q.GetTenantMembership(ctx, userID)
|
||||
if err != nil {
|
||||
|
||||
@@ -198,3 +198,20 @@ func (q *Queries) GetUserByLoginIdentifier(ctx context.Context, loginIdentifier
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateUserPassword = `-- name: UpdateUserPassword :exec
|
||||
UPDATE users
|
||||
SET password_hash = $1,
|
||||
updated_at = NOW()
|
||||
WHERE id = $2 AND deleted_at IS NULL
|
||||
`
|
||||
|
||||
type UpdateUserPasswordParams struct {
|
||||
PasswordHash string `json:"password_hash"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUserPassword(ctx context.Context, arg UpdateUserPasswordParams) error {
|
||||
_, err := q.db.Exec(ctx, updateUserPassword, arg.PasswordHash, arg.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -691,6 +691,7 @@ type PromptRule struct {
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
}
|
||||
|
||||
type PromptRuleGroup struct {
|
||||
@@ -701,6 +702,7 @@ type PromptRuleGroup struct {
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
}
|
||||
|
||||
type PromptRuleKnowledgeGroup struct {
|
||||
|
||||
@@ -15,14 +15,16 @@ const countPromptRules = `-- name: CountPromptRules :one
|
||||
SELECT COUNT(*)
|
||||
FROM prompt_rules
|
||||
WHERE tenant_id = $1
|
||||
AND brand_id = $2
|
||||
AND deleted_at IS NULL
|
||||
AND ($2::bigint IS NULL OR group_id = $2)
|
||||
AND ($3::text IS NULL OR status = $3)
|
||||
AND ($4::text IS NULL OR name ILIKE '%' || $4 || '%')
|
||||
AND ($3::bigint IS NULL OR group_id = $3)
|
||||
AND ($4::text IS NULL OR status = $4)
|
||||
AND ($5::text IS NULL OR name ILIKE '%' || $5 || '%')
|
||||
`
|
||||
|
||||
type CountPromptRulesParams struct {
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
GroupID pgtype.Int8 `json:"group_id"`
|
||||
Status pgtype.Text `json:"status"`
|
||||
Keyword pgtype.Text `json:"keyword"`
|
||||
@@ -31,6 +33,7 @@ type CountPromptRulesParams struct {
|
||||
func (q *Queries) CountPromptRules(ctx context.Context, arg CountPromptRulesParams) (int64, error) {
|
||||
row := q.db.QueryRow(ctx, countPromptRules,
|
||||
arg.TenantID,
|
||||
arg.BrandID,
|
||||
arg.GroupID,
|
||||
arg.Status,
|
||||
arg.Keyword,
|
||||
@@ -44,26 +47,33 @@ const countPromptRulesUngrouped = `-- name: CountPromptRulesUngrouped :one
|
||||
SELECT COUNT(*)
|
||||
FROM prompt_rules
|
||||
WHERE tenant_id = $1
|
||||
AND brand_id = $2
|
||||
AND deleted_at IS NULL
|
||||
AND group_id IS NULL
|
||||
`
|
||||
|
||||
func (q *Queries) CountPromptRulesUngrouped(ctx context.Context, tenantID int64) (int64, error) {
|
||||
row := q.db.QueryRow(ctx, countPromptRulesUngrouped, tenantID)
|
||||
type CountPromptRulesUngroupedParams struct {
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) CountPromptRulesUngrouped(ctx context.Context, arg CountPromptRulesUngroupedParams) (int64, error) {
|
||||
row := q.db.QueryRow(ctx, countPromptRulesUngrouped, arg.TenantID, arg.BrandID)
|
||||
var count int64
|
||||
err := row.Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
|
||||
const createPromptRule = `-- name: CreatePromptRule :one
|
||||
INSERT INTO prompt_rules (tenant_id, group_id, name, prompt_content, scene, default_tone, default_word_count)
|
||||
VALUES ($1, $2, $3, $4,
|
||||
$5, $6, $7)
|
||||
INSERT INTO prompt_rules (tenant_id, brand_id, group_id, name, prompt_content, scene, default_tone, default_word_count)
|
||||
VALUES ($1, $2, $3, $4, $5,
|
||||
$6, $7, $8)
|
||||
RETURNING id, created_at
|
||||
`
|
||||
|
||||
type CreatePromptRuleParams struct {
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
GroupID pgtype.Int8 `json:"group_id"`
|
||||
Name string `json:"name"`
|
||||
PromptContent string `json:"prompt_content"`
|
||||
@@ -80,6 +90,7 @@ type CreatePromptRuleRow struct {
|
||||
func (q *Queries) CreatePromptRule(ctx context.Context, arg CreatePromptRuleParams) (CreatePromptRuleRow, error) {
|
||||
row := q.db.QueryRow(ctx, createPromptRule,
|
||||
arg.TenantID,
|
||||
arg.BrandID,
|
||||
arg.GroupID,
|
||||
arg.Name,
|
||||
arg.PromptContent,
|
||||
@@ -93,13 +104,14 @@ func (q *Queries) CreatePromptRule(ctx context.Context, arg CreatePromptRulePara
|
||||
}
|
||||
|
||||
const createPromptRuleGroup = `-- name: CreatePromptRuleGroup :one
|
||||
INSERT INTO prompt_rule_groups (tenant_id, name, sort_order)
|
||||
VALUES ($1, $2, $3)
|
||||
INSERT INTO prompt_rule_groups (tenant_id, brand_id, name, sort_order)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id, created_at
|
||||
`
|
||||
|
||||
type CreatePromptRuleGroupParams struct {
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
Name string `json:"name"`
|
||||
SortOrder int32 `json:"sort_order"`
|
||||
}
|
||||
@@ -110,7 +122,12 @@ type CreatePromptRuleGroupRow struct {
|
||||
}
|
||||
|
||||
func (q *Queries) CreatePromptRuleGroup(ctx context.Context, arg CreatePromptRuleGroupParams) (CreatePromptRuleGroupRow, error) {
|
||||
row := q.db.QueryRow(ctx, createPromptRuleGroup, arg.TenantID, arg.Name, arg.SortOrder)
|
||||
row := q.db.QueryRow(ctx, createPromptRuleGroup,
|
||||
arg.TenantID,
|
||||
arg.BrandID,
|
||||
arg.Name,
|
||||
arg.SortOrder,
|
||||
)
|
||||
var i CreatePromptRuleGroupRow
|
||||
err := row.Scan(&i.ID, &i.CreatedAt)
|
||||
return i, err
|
||||
@@ -121,12 +138,13 @@ SELECT id, tenant_id, group_id, name, prompt_content,
|
||||
scene, default_tone, default_word_count, status,
|
||||
created_at, updated_at
|
||||
FROM prompt_rules
|
||||
WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL
|
||||
WHERE id = $1 AND tenant_id = $2 AND brand_id = $3 AND deleted_at IS NULL
|
||||
`
|
||||
|
||||
type GetPromptRuleByIDParams struct {
|
||||
ID int64 `json:"id"`
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
}
|
||||
|
||||
type GetPromptRuleByIDRow struct {
|
||||
@@ -144,7 +162,7 @@ type GetPromptRuleByIDRow struct {
|
||||
}
|
||||
|
||||
func (q *Queries) GetPromptRuleByID(ctx context.Context, arg GetPromptRuleByIDParams) (GetPromptRuleByIDRow, error) {
|
||||
row := q.db.QueryRow(ctx, getPromptRuleByID, arg.ID, arg.TenantID)
|
||||
row := q.db.QueryRow(ctx, getPromptRuleByID, arg.ID, arg.TenantID, arg.BrandID)
|
||||
var i GetPromptRuleByIDRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
@@ -165,18 +183,23 @@ func (q *Queries) GetPromptRuleByID(ctx context.Context, arg GetPromptRuleByIDPa
|
||||
const listAllPromptRulesSimple = `-- name: ListAllPromptRulesSimple :many
|
||||
SELECT id, name, status
|
||||
FROM prompt_rules
|
||||
WHERE tenant_id = $1 AND deleted_at IS NULL AND status = 'enabled'
|
||||
WHERE tenant_id = $1 AND brand_id = $2 AND deleted_at IS NULL AND status = 'enabled'
|
||||
ORDER BY name
|
||||
`
|
||||
|
||||
type ListAllPromptRulesSimpleParams struct {
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
}
|
||||
|
||||
type ListAllPromptRulesSimpleRow struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListAllPromptRulesSimple(ctx context.Context, tenantID int64) ([]ListAllPromptRulesSimpleRow, error) {
|
||||
rows, err := q.db.Query(ctx, listAllPromptRulesSimple, tenantID)
|
||||
func (q *Queries) ListAllPromptRulesSimple(ctx context.Context, arg ListAllPromptRulesSimpleParams) ([]ListAllPromptRulesSimpleRow, error) {
|
||||
rows, err := q.db.Query(ctx, listAllPromptRulesSimple, arg.TenantID, arg.BrandID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -199,10 +222,15 @@ const listPromptRuleGroups = `-- name: ListPromptRuleGroups :many
|
||||
|
||||
SELECT id, tenant_id, name, sort_order, created_at, updated_at
|
||||
FROM prompt_rule_groups
|
||||
WHERE tenant_id = $1 AND deleted_at IS NULL
|
||||
WHERE tenant_id = $1 AND brand_id = $2 AND deleted_at IS NULL
|
||||
ORDER BY sort_order, created_at
|
||||
`
|
||||
|
||||
type ListPromptRuleGroupsParams struct {
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
}
|
||||
|
||||
type ListPromptRuleGroupsRow struct {
|
||||
ID int64 `json:"id"`
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
@@ -215,8 +243,8 @@ type ListPromptRuleGroupsRow struct {
|
||||
// ============================================================
|
||||
// Prompt Rule Groups
|
||||
// ============================================================
|
||||
func (q *Queries) ListPromptRuleGroups(ctx context.Context, tenantID int64) ([]ListPromptRuleGroupsRow, error) {
|
||||
rows, err := q.db.Query(ctx, listPromptRuleGroups, tenantID)
|
||||
func (q *Queries) ListPromptRuleGroups(ctx context.Context, arg ListPromptRuleGroupsParams) ([]ListPromptRuleGroupsRow, error) {
|
||||
rows, err := q.db.Query(ctx, listPromptRuleGroups, arg.TenantID, arg.BrandID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -249,19 +277,21 @@ SELECT pr.id, pr.tenant_id, pr.group_id, pr.name, pr.prompt_content,
|
||||
pr.created_at, pr.updated_at,
|
||||
COUNT(a.id)::INT AS article_count
|
||||
FROM prompt_rules pr
|
||||
LEFT JOIN articles a ON a.prompt_rule_id = pr.id AND a.deleted_at IS NULL
|
||||
LEFT JOIN articles a ON a.prompt_rule_id = pr.id AND a.brand_id = pr.brand_id AND a.deleted_at IS NULL
|
||||
WHERE pr.tenant_id = $1
|
||||
AND pr.brand_id = $2
|
||||
AND pr.deleted_at IS NULL
|
||||
AND ($2::bigint IS NULL OR pr.group_id = $2)
|
||||
AND ($3::text IS NULL OR pr.status = $3)
|
||||
AND ($4::text IS NULL OR pr.name ILIKE '%' || $4 || '%')
|
||||
AND ($3::bigint IS NULL OR pr.group_id = $3)
|
||||
AND ($4::text IS NULL OR pr.status = $4)
|
||||
AND ($5::text IS NULL OR pr.name ILIKE '%' || $5 || '%')
|
||||
GROUP BY pr.id
|
||||
ORDER BY pr.created_at DESC
|
||||
LIMIT $6 OFFSET $5
|
||||
LIMIT $7 OFFSET $6
|
||||
`
|
||||
|
||||
type ListPromptRulesParams struct {
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
GroupID pgtype.Int8 `json:"group_id"`
|
||||
Status pgtype.Text `json:"status"`
|
||||
Keyword pgtype.Text `json:"keyword"`
|
||||
@@ -290,6 +320,7 @@ type ListPromptRulesRow struct {
|
||||
func (q *Queries) ListPromptRules(ctx context.Context, arg ListPromptRulesParams) ([]ListPromptRulesRow, error) {
|
||||
rows, err := q.db.Query(ctx, listPromptRules,
|
||||
arg.TenantID,
|
||||
arg.BrandID,
|
||||
arg.GroupID,
|
||||
arg.Status,
|
||||
arg.Keyword,
|
||||
@@ -333,17 +364,19 @@ SELECT pr.id, pr.tenant_id, pr.group_id, pr.name, pr.prompt_content,
|
||||
pr.created_at, pr.updated_at,
|
||||
COUNT(a.id)::INT AS article_count
|
||||
FROM prompt_rules pr
|
||||
LEFT JOIN articles a ON a.prompt_rule_id = pr.id AND a.deleted_at IS NULL
|
||||
LEFT JOIN articles a ON a.prompt_rule_id = pr.id AND a.brand_id = pr.brand_id AND a.deleted_at IS NULL
|
||||
WHERE pr.tenant_id = $1
|
||||
AND pr.brand_id = $2
|
||||
AND pr.deleted_at IS NULL
|
||||
AND pr.group_id IS NULL
|
||||
GROUP BY pr.id
|
||||
ORDER BY pr.created_at DESC
|
||||
LIMIT $3 OFFSET $2
|
||||
LIMIT $4 OFFSET $3
|
||||
`
|
||||
|
||||
type ListPromptRulesUngroupedParams struct {
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
PageOffset int32 `json:"page_offset"`
|
||||
PageSize int32 `json:"page_size"`
|
||||
}
|
||||
@@ -364,7 +397,12 @@ type ListPromptRulesUngroupedRow struct {
|
||||
}
|
||||
|
||||
func (q *Queries) ListPromptRulesUngrouped(ctx context.Context, arg ListPromptRulesUngroupedParams) ([]ListPromptRulesUngroupedRow, error) {
|
||||
rows, err := q.db.Query(ctx, listPromptRulesUngrouped, arg.TenantID, arg.PageOffset, arg.PageSize)
|
||||
rows, err := q.db.Query(ctx, listPromptRulesUngrouped,
|
||||
arg.TenantID,
|
||||
arg.BrandID,
|
||||
arg.PageOffset,
|
||||
arg.PageSize,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -398,31 +436,33 @@ func (q *Queries) ListPromptRulesUngrouped(ctx context.Context, arg ListPromptRu
|
||||
|
||||
const softDeletePromptRule = `-- name: SoftDeletePromptRule :exec
|
||||
UPDATE prompt_rules SET deleted_at = NOW(), updated_at = NOW()
|
||||
WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL
|
||||
WHERE id = $1 AND tenant_id = $2 AND brand_id = $3 AND deleted_at IS NULL
|
||||
`
|
||||
|
||||
type SoftDeletePromptRuleParams struct {
|
||||
ID int64 `json:"id"`
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) SoftDeletePromptRule(ctx context.Context, arg SoftDeletePromptRuleParams) error {
|
||||
_, err := q.db.Exec(ctx, softDeletePromptRule, arg.ID, arg.TenantID)
|
||||
_, err := q.db.Exec(ctx, softDeletePromptRule, arg.ID, arg.TenantID, arg.BrandID)
|
||||
return err
|
||||
}
|
||||
|
||||
const softDeletePromptRuleGroup = `-- name: SoftDeletePromptRuleGroup :exec
|
||||
UPDATE prompt_rule_groups SET deleted_at = NOW(), updated_at = NOW()
|
||||
WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL
|
||||
WHERE id = $1 AND tenant_id = $2 AND brand_id = $3 AND deleted_at IS NULL
|
||||
`
|
||||
|
||||
type SoftDeletePromptRuleGroupParams struct {
|
||||
ID int64 `json:"id"`
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) SoftDeletePromptRuleGroup(ctx context.Context, arg SoftDeletePromptRuleGroupParams) error {
|
||||
_, err := q.db.Exec(ctx, softDeletePromptRuleGroup, arg.ID, arg.TenantID)
|
||||
_, err := q.db.Exec(ctx, softDeletePromptRuleGroup, arg.ID, arg.TenantID, arg.BrandID)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -432,7 +472,7 @@ SET name = $1, group_id = $2,
|
||||
prompt_content = $3,
|
||||
scene = $4, default_tone = $5,
|
||||
default_word_count = $6, updated_at = NOW()
|
||||
WHERE id = $7 AND tenant_id = $8 AND deleted_at IS NULL
|
||||
WHERE id = $7 AND tenant_id = $8 AND brand_id = $9 AND deleted_at IS NULL
|
||||
`
|
||||
|
||||
type UpdatePromptRuleParams struct {
|
||||
@@ -444,6 +484,7 @@ type UpdatePromptRuleParams struct {
|
||||
DefaultWordCount pgtype.Int4 `json:"default_word_count"`
|
||||
ID int64 `json:"id"`
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdatePromptRule(ctx context.Context, arg UpdatePromptRuleParams) error {
|
||||
@@ -456,13 +497,14 @@ func (q *Queries) UpdatePromptRule(ctx context.Context, arg UpdatePromptRulePara
|
||||
arg.DefaultWordCount,
|
||||
arg.ID,
|
||||
arg.TenantID,
|
||||
arg.BrandID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const updatePromptRuleGroup = `-- name: UpdatePromptRuleGroup :exec
|
||||
UPDATE prompt_rule_groups SET name = $1, sort_order = $2, updated_at = NOW()
|
||||
WHERE id = $3 AND tenant_id = $4 AND deleted_at IS NULL
|
||||
WHERE id = $3 AND tenant_id = $4 AND brand_id = $5 AND deleted_at IS NULL
|
||||
`
|
||||
|
||||
type UpdatePromptRuleGroupParams struct {
|
||||
@@ -470,6 +512,7 @@ type UpdatePromptRuleGroupParams struct {
|
||||
SortOrder int32 `json:"sort_order"`
|
||||
ID int64 `json:"id"`
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdatePromptRuleGroup(ctx context.Context, arg UpdatePromptRuleGroupParams) error {
|
||||
@@ -478,22 +521,29 @@ func (q *Queries) UpdatePromptRuleGroup(ctx context.Context, arg UpdatePromptRul
|
||||
arg.SortOrder,
|
||||
arg.ID,
|
||||
arg.TenantID,
|
||||
arg.BrandID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const updatePromptRuleStatus = `-- name: UpdatePromptRuleStatus :exec
|
||||
UPDATE prompt_rules SET status = $1, updated_at = NOW()
|
||||
WHERE id = $2 AND tenant_id = $3 AND deleted_at IS NULL
|
||||
WHERE id = $2 AND tenant_id = $3 AND brand_id = $4 AND deleted_at IS NULL
|
||||
`
|
||||
|
||||
type UpdatePromptRuleStatusParams struct {
|
||||
Status string `json:"status"`
|
||||
ID int64 `json:"id"`
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
BrandID int64 `json:"brand_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdatePromptRuleStatus(ctx context.Context, arg UpdatePromptRuleStatusParams) error {
|
||||
_, err := q.db.Exec(ctx, updatePromptRuleStatus, arg.Status, arg.ID, arg.TenantID)
|
||||
_, err := q.db.Exec(ctx, updatePromptRuleStatus,
|
||||
arg.Status,
|
||||
arg.ID,
|
||||
arg.TenantID,
|
||||
arg.BrandID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ type Querier interface {
|
||||
CountImageAssets(ctx context.Context, arg CountImageAssetsParams) (int32, error)
|
||||
CountImageReferences(ctx context.Context, arg CountImageReferencesParams) (int32, error)
|
||||
CountPromptRules(ctx context.Context, arg CountPromptRulesParams) (int64, error)
|
||||
CountPromptRulesUngrouped(ctx context.Context, tenantID int64) (int64, error)
|
||||
CountPromptRulesUngrouped(ctx context.Context, arg CountPromptRulesUngroupedParams) (int64, error)
|
||||
CountPublishedArticlesByTenantAndBrand(ctx context.Context, arg CountPublishedArticlesByTenantAndBrandParams) (int64, error)
|
||||
CountScheduleTasks(ctx context.Context, arg CountScheduleTasksParams) (int64, error)
|
||||
CountUncategorizedImages(ctx context.Context, tenantID int64) (int32, error)
|
||||
@@ -111,7 +111,7 @@ type Querier interface {
|
||||
// Cross-tenant read: used by platform admin for subscription fan-out.
|
||||
// Excludes expired subscriptions so new prompts are not granted to them.
|
||||
ListActiveSubscribersForPackage(ctx context.Context, packageID int64) ([]KolSubscription, error)
|
||||
ListAllPromptRulesSimple(ctx context.Context, tenantID int64) ([]ListAllPromptRulesSimpleRow, error)
|
||||
ListAllPromptRulesSimple(ctx context.Context, arg ListAllPromptRulesSimpleParams) ([]ListAllPromptRulesSimpleRow, error)
|
||||
ListArticles(ctx context.Context, arg ListArticlesParams) ([]ListArticlesRow, error)
|
||||
ListBrands(ctx context.Context, tenantID int64) ([]ListBrandsRow, error)
|
||||
ListCompetitors(ctx context.Context, arg ListCompetitorsParams) ([]ListCompetitorsRow, error)
|
||||
@@ -132,7 +132,7 @@ type Querier interface {
|
||||
// ============================================================
|
||||
// Prompt Rule Groups
|
||||
// ============================================================
|
||||
ListPromptRuleGroups(ctx context.Context, tenantID int64) ([]ListPromptRuleGroupsRow, error)
|
||||
ListPromptRuleGroups(ctx context.Context, arg ListPromptRuleGroupsParams) ([]ListPromptRuleGroupsRow, error)
|
||||
// ============================================================
|
||||
// Prompt Rules
|
||||
// ============================================================
|
||||
@@ -208,6 +208,7 @@ type Querier interface {
|
||||
UpdateScheduleTask(ctx context.Context, arg UpdateScheduleTaskParams) error
|
||||
UpdateScheduleTaskStatus(ctx context.Context, arg UpdateScheduleTaskStatusParams) error
|
||||
UpdateTaskRecordStatus(ctx context.Context, arg UpdateTaskRecordStatusParams) error
|
||||
UpdateUserPassword(ctx context.Context, arg UpdateUserPasswordParams) error
|
||||
UpsertDesktopAccount(ctx context.Context, arg UpsertDesktopAccountParams) (UpsertDesktopAccountRow, error)
|
||||
UpsertImageReference(ctx context.Context, arg UpsertImageReferenceParams) error
|
||||
}
|
||||
|
||||
@@ -14,6 +14,12 @@ SELECT id, email, phone, password_hash, name, avatar_url, status, created_at, up
|
||||
FROM users
|
||||
WHERE id = sqlc.arg(id) AND deleted_at IS NULL;
|
||||
|
||||
-- name: UpdateUserPassword :exec
|
||||
UPDATE users
|
||||
SET password_hash = sqlc.arg(password_hash),
|
||||
updated_at = NOW()
|
||||
WHERE id = sqlc.arg(id) AND deleted_at IS NULL;
|
||||
|
||||
-- name: GetTenantMembership :one
|
||||
SELECT tm.id,
|
||||
tm.tenant_id,
|
||||
|
||||
@@ -5,21 +5,21 @@
|
||||
-- name: ListPromptRuleGroups :many
|
||||
SELECT id, tenant_id, name, sort_order, created_at, updated_at
|
||||
FROM prompt_rule_groups
|
||||
WHERE tenant_id = sqlc.arg(tenant_id) AND deleted_at IS NULL
|
||||
WHERE tenant_id = sqlc.arg(tenant_id) AND brand_id = sqlc.arg(brand_id) AND deleted_at IS NULL
|
||||
ORDER BY sort_order, created_at;
|
||||
|
||||
-- name: CreatePromptRuleGroup :one
|
||||
INSERT INTO prompt_rule_groups (tenant_id, name, sort_order)
|
||||
VALUES (sqlc.arg(tenant_id), sqlc.arg(name), sqlc.arg(sort_order))
|
||||
INSERT INTO prompt_rule_groups (tenant_id, brand_id, name, sort_order)
|
||||
VALUES (sqlc.arg(tenant_id), sqlc.arg(brand_id), sqlc.arg(name), sqlc.arg(sort_order))
|
||||
RETURNING id, created_at;
|
||||
|
||||
-- name: UpdatePromptRuleGroup :exec
|
||||
UPDATE prompt_rule_groups SET name = sqlc.arg(name), sort_order = sqlc.arg(sort_order), updated_at = NOW()
|
||||
WHERE id = sqlc.arg(id) AND tenant_id = sqlc.arg(tenant_id) AND deleted_at IS NULL;
|
||||
WHERE id = sqlc.arg(id) AND tenant_id = sqlc.arg(tenant_id) AND brand_id = sqlc.arg(brand_id) AND deleted_at IS NULL;
|
||||
|
||||
-- name: SoftDeletePromptRuleGroup :exec
|
||||
UPDATE prompt_rule_groups SET deleted_at = NOW(), updated_at = NOW()
|
||||
WHERE id = sqlc.arg(id) AND tenant_id = sqlc.arg(tenant_id) AND deleted_at IS NULL;
|
||||
WHERE id = sqlc.arg(id) AND tenant_id = sqlc.arg(tenant_id) AND brand_id = sqlc.arg(brand_id) AND deleted_at IS NULL;
|
||||
|
||||
-- ============================================================
|
||||
-- Prompt Rules
|
||||
@@ -31,8 +31,9 @@ SELECT pr.id, pr.tenant_id, pr.group_id, pr.name, pr.prompt_content,
|
||||
pr.created_at, pr.updated_at,
|
||||
COUNT(a.id)::INT AS article_count
|
||||
FROM prompt_rules pr
|
||||
LEFT JOIN articles a ON a.prompt_rule_id = pr.id AND a.deleted_at IS NULL
|
||||
LEFT JOIN articles a ON a.prompt_rule_id = pr.id AND a.brand_id = pr.brand_id AND a.deleted_at IS NULL
|
||||
WHERE pr.tenant_id = sqlc.arg(tenant_id)
|
||||
AND pr.brand_id = sqlc.arg(brand_id)
|
||||
AND pr.deleted_at IS NULL
|
||||
AND (sqlc.narg(group_id)::bigint IS NULL OR pr.group_id = sqlc.narg(group_id))
|
||||
AND (sqlc.narg(status)::text IS NULL OR pr.status = sqlc.narg(status))
|
||||
@@ -45,6 +46,7 @@ LIMIT sqlc.arg(page_size) OFFSET sqlc.arg(page_offset);
|
||||
SELECT COUNT(*)
|
||||
FROM prompt_rules
|
||||
WHERE tenant_id = sqlc.arg(tenant_id)
|
||||
AND brand_id = sqlc.arg(brand_id)
|
||||
AND deleted_at IS NULL
|
||||
AND (sqlc.narg(group_id)::bigint IS NULL OR group_id = sqlc.narg(group_id))
|
||||
AND (sqlc.narg(status)::text IS NULL OR status = sqlc.narg(status))
|
||||
@@ -56,8 +58,9 @@ SELECT pr.id, pr.tenant_id, pr.group_id, pr.name, pr.prompt_content,
|
||||
pr.created_at, pr.updated_at,
|
||||
COUNT(a.id)::INT AS article_count
|
||||
FROM prompt_rules pr
|
||||
LEFT JOIN articles a ON a.prompt_rule_id = pr.id AND a.deleted_at IS NULL
|
||||
LEFT JOIN articles a ON a.prompt_rule_id = pr.id AND a.brand_id = pr.brand_id AND a.deleted_at IS NULL
|
||||
WHERE pr.tenant_id = sqlc.arg(tenant_id)
|
||||
AND pr.brand_id = sqlc.arg(brand_id)
|
||||
AND pr.deleted_at IS NULL
|
||||
AND pr.group_id IS NULL
|
||||
GROUP BY pr.id
|
||||
@@ -68,6 +71,7 @@ LIMIT sqlc.arg(page_size) OFFSET sqlc.arg(page_offset);
|
||||
SELECT COUNT(*)
|
||||
FROM prompt_rules
|
||||
WHERE tenant_id = sqlc.arg(tenant_id)
|
||||
AND brand_id = sqlc.arg(brand_id)
|
||||
AND deleted_at IS NULL
|
||||
AND group_id IS NULL;
|
||||
|
||||
@@ -76,11 +80,11 @@ SELECT id, tenant_id, group_id, name, prompt_content,
|
||||
scene, default_tone, default_word_count, status,
|
||||
created_at, updated_at
|
||||
FROM prompt_rules
|
||||
WHERE id = sqlc.arg(id) AND tenant_id = sqlc.arg(tenant_id) AND deleted_at IS NULL;
|
||||
WHERE id = sqlc.arg(id) AND tenant_id = sqlc.arg(tenant_id) AND brand_id = sqlc.arg(brand_id) AND deleted_at IS NULL;
|
||||
|
||||
-- name: CreatePromptRule :one
|
||||
INSERT INTO prompt_rules (tenant_id, group_id, name, prompt_content, scene, default_tone, default_word_count)
|
||||
VALUES (sqlc.arg(tenant_id), sqlc.narg(group_id), sqlc.arg(name), sqlc.arg(prompt_content),
|
||||
INSERT INTO prompt_rules (tenant_id, brand_id, group_id, name, prompt_content, scene, default_tone, default_word_count)
|
||||
VALUES (sqlc.arg(tenant_id), sqlc.arg(brand_id), sqlc.narg(group_id), sqlc.arg(name), sqlc.arg(prompt_content),
|
||||
sqlc.narg(scene), sqlc.narg(default_tone), sqlc.narg(default_word_count))
|
||||
RETURNING id, created_at;
|
||||
|
||||
@@ -90,18 +94,18 @@ SET name = sqlc.arg(name), group_id = sqlc.narg(group_id),
|
||||
prompt_content = sqlc.arg(prompt_content),
|
||||
scene = sqlc.narg(scene), default_tone = sqlc.narg(default_tone),
|
||||
default_word_count = sqlc.narg(default_word_count), updated_at = NOW()
|
||||
WHERE id = sqlc.arg(id) AND tenant_id = sqlc.arg(tenant_id) AND deleted_at IS NULL;
|
||||
WHERE id = sqlc.arg(id) AND tenant_id = sqlc.arg(tenant_id) AND brand_id = sqlc.arg(brand_id) AND deleted_at IS NULL;
|
||||
|
||||
-- name: UpdatePromptRuleStatus :exec
|
||||
UPDATE prompt_rules SET status = sqlc.arg(status), updated_at = NOW()
|
||||
WHERE id = sqlc.arg(id) AND tenant_id = sqlc.arg(tenant_id) AND deleted_at IS NULL;
|
||||
WHERE id = sqlc.arg(id) AND tenant_id = sqlc.arg(tenant_id) AND brand_id = sqlc.arg(brand_id) AND deleted_at IS NULL;
|
||||
|
||||
-- name: SoftDeletePromptRule :exec
|
||||
UPDATE prompt_rules SET deleted_at = NOW(), updated_at = NOW()
|
||||
WHERE id = sqlc.arg(id) AND tenant_id = sqlc.arg(tenant_id) AND deleted_at IS NULL;
|
||||
WHERE id = sqlc.arg(id) AND tenant_id = sqlc.arg(tenant_id) AND brand_id = sqlc.arg(brand_id) AND deleted_at IS NULL;
|
||||
|
||||
-- name: ListAllPromptRulesSimple :many
|
||||
SELECT id, name, status
|
||||
FROM prompt_rules
|
||||
WHERE tenant_id = sqlc.arg(tenant_id) AND deleted_at IS NULL AND status = 'enabled'
|
||||
WHERE tenant_id = sqlc.arg(tenant_id) AND brand_id = sqlc.arg(brand_id) AND deleted_at IS NULL AND status = 'enabled'
|
||||
ORDER BY name;
|
||||
|
||||
Reference in New Issue
Block a user