mirror of
https://gh-proxy.org/https://github.com/klinsmaya/multica.git
synced 2026-11-08 01:16:45 +08:00
* feat(channels): add new and clear conversation controls * fix(dingtalk): resolve bot identity before dispatch * feat(chat): expose channel-created chats consistently * refactor(channels): snapshot task delivery routes
588 lines
22 KiB
Go
588 lines
22 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.31.1
|
|
// source: dingtalk.sql
|
|
|
|
package db
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
const countInactiveDingTalkGroupPresencesByWorkspace = `-- name: CountInactiveDingTalkGroupPresencesByWorkspace :many
|
|
SELECT presence.installation_id, installation.agent_id, count(*)::bigint AS group_count
|
|
FROM dingtalk_group_presence presence
|
|
JOIN channel_installation installation ON installation.id = presence.installation_id
|
|
WHERE installation.workspace_id = $1
|
|
AND installation.channel_type = 'dingtalk'
|
|
AND installation.status = 'active'
|
|
AND (presence.last_active_at < $2 OR presence.last_active_at IS NULL)
|
|
AND (NOT $3::boolean OR installation.agent_id = $4)
|
|
GROUP BY presence.installation_id, installation.agent_id
|
|
`
|
|
|
|
type CountInactiveDingTalkGroupPresencesByWorkspaceParams struct {
|
|
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
|
ActiveSince pgtype.Timestamptz `json:"active_since"`
|
|
FilterByAgent bool `json:"filter_by_agent"`
|
|
AgentID pgtype.UUID `json:"agent_id"`
|
|
}
|
|
|
|
type CountInactiveDingTalkGroupPresencesByWorkspaceRow struct {
|
|
InstallationID pgtype.UUID `json:"installation_id"`
|
|
AgentID pgtype.UUID `json:"agent_id"`
|
|
GroupCount int64 `json:"group_count"`
|
|
}
|
|
|
|
func (q *Queries) CountInactiveDingTalkGroupPresencesByWorkspace(ctx context.Context, arg CountInactiveDingTalkGroupPresencesByWorkspaceParams) ([]CountInactiveDingTalkGroupPresencesByWorkspaceRow, error) {
|
|
rows, err := q.db.Query(ctx, countInactiveDingTalkGroupPresencesByWorkspace,
|
|
arg.WorkspaceID,
|
|
arg.ActiveSince,
|
|
arg.FilterByAgent,
|
|
arg.AgentID,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []CountInactiveDingTalkGroupPresencesByWorkspaceRow{}
|
|
for rows.Next() {
|
|
var i CountInactiveDingTalkGroupPresencesByWorkspaceRow
|
|
if err := rows.Scan(&i.InstallationID, &i.AgentID, &i.GroupCount); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const deleteDingTalkInstallationForReplacement = `-- name: DeleteDingTalkInstallationForReplacement :one
|
|
WITH retired AS (
|
|
DELETE FROM channel_installation ci
|
|
WHERE ci.id = $1
|
|
AND ci.workspace_id = $2
|
|
AND ci.agent_id = $3
|
|
AND ci.channel_type = 'dingtalk'
|
|
RETURNING ci.id
|
|
),
|
|
cleared_group_presence AS (
|
|
DELETE FROM dingtalk_group_presence
|
|
WHERE installation_id IN (SELECT id FROM retired)
|
|
),
|
|
cleared_bot_identity AS (
|
|
DELETE FROM dingtalk_bot_identity
|
|
WHERE installation_id IN (SELECT id FROM retired)
|
|
),
|
|
cleared_group_routes AS (
|
|
DELETE FROM dingtalk_group_route
|
|
WHERE installation_id IN (SELECT id FROM retired)
|
|
),
|
|
cleared_task_deliveries AS (
|
|
DELETE FROM channel_task_delivery
|
|
WHERE installation_id IN (SELECT id FROM retired)
|
|
),
|
|
cleared_outbound_messages AS (
|
|
DELETE FROM channel_outbound_message
|
|
WHERE installation_id IN (SELECT id FROM retired)
|
|
),
|
|
cleared_chat_sessions AS (
|
|
DELETE FROM channel_chat_session_binding
|
|
WHERE installation_id IN (SELECT id FROM retired)
|
|
RETURNING chat_session_id
|
|
),
|
|
cleared_outbound_cards AS (
|
|
DELETE FROM channel_outbound_card_message
|
|
WHERE chat_session_id IN (SELECT chat_session_id FROM cleared_chat_sessions)
|
|
),
|
|
cleared_binding_tokens AS (
|
|
DELETE FROM channel_binding_token
|
|
WHERE installation_id IN (SELECT id FROM retired)
|
|
),
|
|
cleared_user_bindings AS (
|
|
DELETE FROM channel_user_binding
|
|
WHERE installation_id IN (SELECT id FROM retired)
|
|
),
|
|
cleared_inbound_dedup AS (
|
|
DELETE FROM channel_inbound_message_dedup
|
|
WHERE installation_id IN (SELECT id FROM retired)
|
|
),
|
|
detached_audit AS (
|
|
UPDATE channel_inbound_audit SET installation_id = NULL
|
|
WHERE installation_id IN (SELECT id FROM retired)
|
|
),
|
|
detached_media_intents AS (
|
|
UPDATE channel_media_pending_object SET installation_id = NULL
|
|
WHERE installation_id IN (SELECT id FROM retired)
|
|
)
|
|
SELECT retired.id FROM retired
|
|
`
|
|
|
|
type DeleteDingTalkInstallationForReplacementParams struct {
|
|
InstallationID pgtype.UUID `json:"installation_id"`
|
|
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
|
AgentID pgtype.UUID `json:"agent_id"`
|
|
}
|
|
|
|
// Retires an installation when the same agent is connected with a DIFFERENT
|
|
// AppKey. A senderStaffId is scoped to one DingTalk organization, so none of the
|
|
// old installation's identity, token, session, dedup, or outbound state may
|
|
// cross into the new robot. The caller inserts a fresh installation in the same
|
|
// transaction, giving the replacement a new installation_id that also fences
|
|
// late writes and replies from the old connection.
|
|
//
|
|
// Chat sessions themselves remain as history, but their channel bindings and
|
|
// outbound cards are removed. Audit and media-intent rows remain useful for
|
|
// diagnostics / reconciliation, so their installation references are detached.
|
|
func (q *Queries) DeleteDingTalkInstallationForReplacement(ctx context.Context, arg DeleteDingTalkInstallationForReplacementParams) (pgtype.UUID, error) {
|
|
row := q.db.QueryRow(ctx, deleteDingTalkInstallationForReplacement, arg.InstallationID, arg.WorkspaceID, arg.AgentID)
|
|
var id pgtype.UUID
|
|
err := row.Scan(&id)
|
|
return id, err
|
|
}
|
|
|
|
const forgetDingTalkGroupPresence = `-- name: ForgetDingTalkGroupPresence :one
|
|
DELETE FROM dingtalk_group_presence presence
|
|
USING channel_installation installation
|
|
WHERE presence.installation_id = installation.id
|
|
AND presence.workspace_id = $1
|
|
AND presence.installation_id = $2
|
|
AND presence.conversation_id = $3
|
|
AND installation.workspace_id = $1
|
|
AND installation.channel_type = 'dingtalk'
|
|
RETURNING presence.installation_id
|
|
`
|
|
|
|
type ForgetDingTalkGroupPresenceParams struct {
|
|
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
|
InstallationID pgtype.UUID `json:"installation_id"`
|
|
ConversationID string `json:"conversation_id"`
|
|
}
|
|
|
|
func (q *Queries) ForgetDingTalkGroupPresence(ctx context.Context, arg ForgetDingTalkGroupPresenceParams) (pgtype.UUID, error) {
|
|
row := q.db.QueryRow(ctx, forgetDingTalkGroupPresence, arg.WorkspaceID, arg.InstallationID, arg.ConversationID)
|
|
var installation_id pgtype.UUID
|
|
err := row.Scan(&installation_id)
|
|
return installation_id, err
|
|
}
|
|
|
|
const getDingTalkInstallationOwnerForUpdate = `-- name: GetDingTalkInstallationOwnerForUpdate :one
|
|
SELECT id, COALESCE(config ->> 'app_id', '')::text AS app_id
|
|
FROM channel_installation
|
|
WHERE workspace_id = $1
|
|
AND agent_id = $2
|
|
AND channel_type = 'dingtalk'
|
|
FOR UPDATE
|
|
`
|
|
|
|
type GetDingTalkInstallationOwnerForUpdateParams struct {
|
|
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
|
AgentID pgtype.UUID `json:"agent_id"`
|
|
}
|
|
|
|
type GetDingTalkInstallationOwnerForUpdateRow struct {
|
|
ID pgtype.UUID `json:"id"`
|
|
AppID string `json:"app_id"`
|
|
}
|
|
|
|
// Reads the current robot identity after LockDingTalkInstallationOwner has
|
|
// serialized the logical owner slot. app_id is non-null for every DingTalk
|
|
// installation; COALESCE treats malformed legacy config as a different robot,
|
|
// which safely replaces it instead of preserving unknown identity state.
|
|
func (q *Queries) GetDingTalkInstallationOwnerForUpdate(ctx context.Context, arg GetDingTalkInstallationOwnerForUpdateParams) (GetDingTalkInstallationOwnerForUpdateRow, error) {
|
|
row := q.db.QueryRow(ctx, getDingTalkInstallationOwnerForUpdate, arg.WorkspaceID, arg.AgentID)
|
|
var i GetDingTalkInstallationOwnerForUpdateRow
|
|
err := row.Scan(&i.ID, &i.AppID)
|
|
return i, err
|
|
}
|
|
|
|
const listDingTalkBotIdentitiesByWorkspace = `-- name: ListDingTalkBotIdentitiesByWorkspace :many
|
|
SELECT
|
|
installation.id AS installation_id,
|
|
installation.agent_id,
|
|
identity.bot_name,
|
|
identity.bot_identity_issue
|
|
FROM channel_installation installation
|
|
JOIN dingtalk_bot_identity identity ON identity.installation_id = installation.id
|
|
WHERE installation.workspace_id = $1
|
|
AND installation.channel_type = 'dingtalk'
|
|
AND installation.status = 'active'
|
|
AND (NOT $2::boolean OR installation.agent_id = $3)
|
|
`
|
|
|
|
type ListDingTalkBotIdentitiesByWorkspaceParams struct {
|
|
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
|
FilterByAgent bool `json:"filter_by_agent"`
|
|
AgentID pgtype.UUID `json:"agent_id"`
|
|
}
|
|
|
|
type ListDingTalkBotIdentitiesByWorkspaceRow struct {
|
|
InstallationID pgtype.UUID `json:"installation_id"`
|
|
AgentID pgtype.UUID `json:"agent_id"`
|
|
BotName string `json:"bot_name"`
|
|
BotIdentityIssue string `json:"bot_identity_issue"`
|
|
}
|
|
|
|
// Identity is installation-owned, so forgetting or collapsing groups cannot
|
|
// change the connection label.
|
|
func (q *Queries) ListDingTalkBotIdentitiesByWorkspace(ctx context.Context, arg ListDingTalkBotIdentitiesByWorkspaceParams) ([]ListDingTalkBotIdentitiesByWorkspaceRow, error) {
|
|
rows, err := q.db.Query(ctx, listDingTalkBotIdentitiesByWorkspace, arg.WorkspaceID, arg.FilterByAgent, arg.AgentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []ListDingTalkBotIdentitiesByWorkspaceRow{}
|
|
for rows.Next() {
|
|
var i ListDingTalkBotIdentitiesByWorkspaceRow
|
|
if err := rows.Scan(
|
|
&i.InstallationID,
|
|
&i.AgentID,
|
|
&i.BotName,
|
|
&i.BotIdentityIssue,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listDingTalkGroupPresencesByWorkspace = `-- name: ListDingTalkGroupPresencesByWorkspace :many
|
|
SELECT
|
|
presence.conversation_id,
|
|
presence.conversation_title,
|
|
presence.installation_id,
|
|
installation.agent_id,
|
|
COALESCE(identity.bot_name, presence.bot_name)::text AS bot_name,
|
|
COALESCE(identity.bot_identity_issue, presence.bot_identity_issue)::text AS bot_identity_issue,
|
|
presence.last_active_at,
|
|
presence.mention_count
|
|
FROM dingtalk_group_presence presence
|
|
JOIN channel_installation installation ON installation.id = presence.installation_id
|
|
LEFT JOIN dingtalk_bot_identity identity ON identity.installation_id = presence.installation_id
|
|
WHERE installation.workspace_id = $1
|
|
AND installation.channel_type = 'dingtalk'
|
|
AND installation.status = 'active'
|
|
AND (NOT $2::boolean OR installation.agent_id = $3)
|
|
AND (NOT $4::boolean OR installation.id = $5)
|
|
AND (
|
|
(NOT $6::boolean AND presence.last_active_at >= $7)
|
|
OR ($6::boolean AND (presence.last_active_at < $7 OR presence.last_active_at IS NULL))
|
|
)
|
|
ORDER BY presence.last_active_at DESC NULLS LAST, presence.conversation_title ASC,
|
|
presence.conversation_id ASC, installation.installed_at ASC, installation.id ASC
|
|
LIMIT NULLIF($9::integer, 0)
|
|
OFFSET $8::integer
|
|
`
|
|
|
|
type ListDingTalkGroupPresencesByWorkspaceParams struct {
|
|
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
|
FilterByAgent bool `json:"filter_by_agent"`
|
|
AgentID pgtype.UUID `json:"agent_id"`
|
|
FilterByInstallation bool `json:"filter_by_installation"`
|
|
FilterInstallationID pgtype.UUID `json:"filter_installation_id"`
|
|
IncludeInactive bool `json:"include_inactive"`
|
|
ActiveSince pgtype.Timestamptz `json:"active_since"`
|
|
PageOffset int32 `json:"page_offset"`
|
|
PageLimit int32 `json:"page_limit"`
|
|
}
|
|
|
|
type ListDingTalkGroupPresencesByWorkspaceRow struct {
|
|
ConversationID string `json:"conversation_id"`
|
|
ConversationTitle string `json:"conversation_title"`
|
|
InstallationID pgtype.UUID `json:"installation_id"`
|
|
AgentID pgtype.UUID `json:"agent_id"`
|
|
BotName string `json:"bot_name"`
|
|
BotIdentityIssue string `json:"bot_identity_issue"`
|
|
LastActiveAt pgtype.Timestamptz `json:"last_active_at"`
|
|
MentionCount int64 `json:"mention_count"`
|
|
}
|
|
|
|
// Active rows are the default inventory. Inactive rows are loaded only when a
|
|
// client expands one installation's historical section.
|
|
func (q *Queries) ListDingTalkGroupPresencesByWorkspace(ctx context.Context, arg ListDingTalkGroupPresencesByWorkspaceParams) ([]ListDingTalkGroupPresencesByWorkspaceRow, error) {
|
|
rows, err := q.db.Query(ctx, listDingTalkGroupPresencesByWorkspace,
|
|
arg.WorkspaceID,
|
|
arg.FilterByAgent,
|
|
arg.AgentID,
|
|
arg.FilterByInstallation,
|
|
arg.FilterInstallationID,
|
|
arg.IncludeInactive,
|
|
arg.ActiveSince,
|
|
arg.PageOffset,
|
|
arg.PageLimit,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []ListDingTalkGroupPresencesByWorkspaceRow{}
|
|
for rows.Next() {
|
|
var i ListDingTalkGroupPresencesByWorkspaceRow
|
|
if err := rows.Scan(
|
|
&i.ConversationID,
|
|
&i.ConversationTitle,
|
|
&i.InstallationID,
|
|
&i.AgentID,
|
|
&i.BotName,
|
|
&i.BotIdentityIssue,
|
|
&i.LastActiveAt,
|
|
&i.MentionCount,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listDingTalkUserBindingsForMember = `-- name: ListDingTalkUserBindingsForMember :many
|
|
|
|
SELECT installation_id, channel_user_id
|
|
FROM channel_user_binding
|
|
WHERE workspace_id = $1
|
|
AND multica_user_id = $2
|
|
AND channel_type = 'dingtalk'
|
|
ORDER BY bound_at DESC, id ASC
|
|
`
|
|
|
|
type ListDingTalkUserBindingsForMemberParams struct {
|
|
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
|
MulticaUserID pgtype.UUID `json:"multica_user_id"`
|
|
}
|
|
|
|
type ListDingTalkUserBindingsForMemberRow struct {
|
|
InstallationID pgtype.UUID `json:"installation_id"`
|
|
ChannelUserID string `json:"channel_user_id"`
|
|
}
|
|
|
|
// DingTalk-specific installation identity operations. The underlying channel_*
|
|
// tables are shared, but these replacement semantics belong to DingTalk's BYO
|
|
// AppKey model and deliberately stay out of the shared channel query surface.
|
|
// Returns only the requesting Multica member's DingTalk identities. The
|
|
// installation list is member-visible, so returning every member's staff id
|
|
// here would expose staff ID values more broadly than necessary.
|
|
func (q *Queries) ListDingTalkUserBindingsForMember(ctx context.Context, arg ListDingTalkUserBindingsForMemberParams) ([]ListDingTalkUserBindingsForMemberRow, error) {
|
|
rows, err := q.db.Query(ctx, listDingTalkUserBindingsForMember, arg.WorkspaceID, arg.MulticaUserID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []ListDingTalkUserBindingsForMemberRow{}
|
|
for rows.Next() {
|
|
var i ListDingTalkUserBindingsForMemberRow
|
|
if err := rows.Scan(&i.InstallationID, &i.ChannelUserID); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const lockDingTalkInstallationOwner = `-- name: LockDingTalkInstallationOwner :exec
|
|
SELECT pg_advisory_xact_lock(
|
|
hashtextextended(
|
|
($1::uuid)::text || ':' ||
|
|
($2::uuid)::text || ':dingtalk',
|
|
0
|
|
)
|
|
)
|
|
`
|
|
|
|
type LockDingTalkInstallationOwnerParams struct {
|
|
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
|
AgentID pgtype.UUID `json:"agent_id"`
|
|
}
|
|
|
|
// Serializes install / replacement decisions for one logical
|
|
// (workspace, agent, channel) slot. A different-AppKey replacement deletes the
|
|
// old row and inserts a fresh installation id; the advisory lock closes the
|
|
// gap where two concurrent replacements could otherwise miss each other's new
|
|
// row and update it in place, carrying identity state across robot boundaries.
|
|
func (q *Queries) LockDingTalkInstallationOwner(ctx context.Context, arg LockDingTalkInstallationOwnerParams) error {
|
|
_, err := q.db.Exec(ctx, lockDingTalkInstallationOwner, arg.WorkspaceID, arg.AgentID)
|
|
return err
|
|
}
|
|
|
|
const recordDingTalkGroupActivity = `-- name: RecordDingTalkGroupActivity :one
|
|
UPDATE dingtalk_group_presence
|
|
SET last_active_at = now(), mention_count = mention_count + 1, updated_at = now()
|
|
WHERE installation_id = $1
|
|
AND conversation_id = $2
|
|
RETURNING installation_id
|
|
`
|
|
|
|
type RecordDingTalkGroupActivityParams struct {
|
|
InstallationID pgtype.UUID `json:"installation_id"`
|
|
ConversationID string `json:"conversation_id"`
|
|
}
|
|
|
|
// Advances activity only after the deduplicated inbound message is durable.
|
|
// Presence observation runs first, so a forgotten group is recreated before
|
|
// this update executes.
|
|
func (q *Queries) RecordDingTalkGroupActivity(ctx context.Context, arg RecordDingTalkGroupActivityParams) (pgtype.UUID, error) {
|
|
row := q.db.QueryRow(ctx, recordDingTalkGroupActivity, arg.InstallationID, arg.ConversationID)
|
|
var installation_id pgtype.UUID
|
|
err := row.Scan(&installation_id)
|
|
return installation_id, err
|
|
}
|
|
|
|
const recordDingTalkGroupPresence = `-- name: RecordDingTalkGroupPresence :one
|
|
WITH target AS MATERIALIZED (
|
|
SELECT channel_installation.id, channel_installation.workspace_id
|
|
FROM channel_installation
|
|
WHERE channel_installation.id = $1
|
|
AND channel_installation.workspace_id = $2
|
|
AND channel_installation.channel_type = 'dingtalk'
|
|
AND channel_installation.status = 'active'
|
|
FOR KEY SHARE
|
|
), current_identity AS (
|
|
SELECT identity.installation_id, identity.bot_name, identity.bot_identity_issue
|
|
FROM target
|
|
JOIN dingtalk_bot_identity identity ON identity.installation_id = target.id
|
|
), observed AS (
|
|
INSERT INTO dingtalk_group_presence (
|
|
workspace_id, installation_id, conversation_id, conversation_title,
|
|
bot_name, bot_identity_issue
|
|
)
|
|
SELECT
|
|
target.workspace_id,
|
|
target.id,
|
|
$3::text,
|
|
$4::text,
|
|
current_identity.bot_name,
|
|
current_identity.bot_identity_issue
|
|
FROM target
|
|
JOIN current_identity ON current_identity.installation_id = target.id
|
|
ON CONFLICT (installation_id, conversation_id) DO UPDATE SET
|
|
conversation_title = CASE
|
|
WHEN EXCLUDED.conversation_title <> '' THEN EXCLUDED.conversation_title
|
|
ELSE dingtalk_group_presence.conversation_title
|
|
END,
|
|
bot_name = EXCLUDED.bot_name,
|
|
bot_identity_issue = EXCLUDED.bot_identity_issue,
|
|
updated_at = now()
|
|
WHERE (
|
|
dingtalk_group_presence.conversation_title IS DISTINCT FROM EXCLUDED.conversation_title
|
|
AND EXCLUDED.conversation_title <> ''
|
|
)
|
|
OR dingtalk_group_presence.bot_name IS DISTINCT FROM EXCLUDED.bot_name
|
|
OR dingtalk_group_presence.bot_identity_issue IS DISTINCT FROM EXCLUDED.bot_identity_issue
|
|
RETURNING installation_id
|
|
), synced_identity AS (
|
|
UPDATE dingtalk_group_presence presence
|
|
SET
|
|
bot_name = current_identity.bot_name,
|
|
bot_identity_issue = current_identity.bot_identity_issue,
|
|
updated_at = now()
|
|
FROM current_identity
|
|
WHERE presence.installation_id = current_identity.installation_id
|
|
AND presence.conversation_id <> $3::text
|
|
AND (
|
|
presence.bot_name IS DISTINCT FROM current_identity.bot_name
|
|
OR presence.bot_identity_issue IS DISTINCT FROM current_identity.bot_identity_issue
|
|
)
|
|
)
|
|
SELECT target.id FROM target
|
|
`
|
|
|
|
type RecordDingTalkGroupPresenceParams struct {
|
|
InstallationID pgtype.UUID `json:"installation_id"`
|
|
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
|
ConversationID string `json:"conversation_id"`
|
|
ConversationTitle string `json:"conversation_title"`
|
|
}
|
|
|
|
// Records an observed bot/group pair without carrying routing state. The
|
|
// installation identity is copied only for compatibility with the preceding
|
|
// draft; readers use dingtalk_bot_identity as the authority.
|
|
func (q *Queries) RecordDingTalkGroupPresence(ctx context.Context, arg RecordDingTalkGroupPresenceParams) (pgtype.UUID, error) {
|
|
row := q.db.QueryRow(ctx, recordDingTalkGroupPresence,
|
|
arg.InstallationID,
|
|
arg.WorkspaceID,
|
|
arg.ConversationID,
|
|
arg.ConversationTitle,
|
|
)
|
|
var id pgtype.UUID
|
|
err := row.Scan(&id)
|
|
return id, err
|
|
}
|
|
|
|
const upsertDingTalkBotIdentity = `-- name: UpsertDingTalkBotIdentity :one
|
|
WITH target AS MATERIALIZED (
|
|
SELECT channel_installation.id, channel_installation.workspace_id
|
|
FROM channel_installation
|
|
WHERE channel_installation.id = $1
|
|
AND channel_installation.workspace_id = $2
|
|
AND channel_installation.channel_type = 'dingtalk'
|
|
AND channel_installation.status = 'active'
|
|
FOR UPDATE
|
|
), prior_identity AS (
|
|
SELECT
|
|
COALESCE(identity.bot_name, '')::text AS bot_name,
|
|
COALESCE(identity.bot_identity_issue, '')::text AS bot_identity_issue
|
|
FROM target
|
|
LEFT JOIN dingtalk_bot_identity identity ON identity.installation_id = target.id
|
|
), upserted_identity AS (
|
|
INSERT INTO dingtalk_bot_identity (
|
|
workspace_id, installation_id, bot_name, bot_identity_issue
|
|
)
|
|
SELECT
|
|
target.workspace_id,
|
|
target.id,
|
|
COALESCE(NULLIF($3::text, ''), prior_identity.bot_name),
|
|
CASE
|
|
WHEN $4::text <> '' THEN $4::text
|
|
WHEN $3::text <> '' THEN ''
|
|
ELSE prior_identity.bot_identity_issue
|
|
END
|
|
FROM target, prior_identity
|
|
ON CONFLICT (installation_id) DO UPDATE SET
|
|
workspace_id = EXCLUDED.workspace_id,
|
|
bot_name = EXCLUDED.bot_name,
|
|
bot_identity_issue = EXCLUDED.bot_identity_issue,
|
|
updated_at = now()
|
|
WHERE dingtalk_bot_identity.workspace_id IS DISTINCT FROM EXCLUDED.workspace_id
|
|
OR dingtalk_bot_identity.bot_name IS DISTINCT FROM EXCLUDED.bot_name
|
|
OR dingtalk_bot_identity.bot_identity_issue IS DISTINCT FROM EXCLUDED.bot_identity_issue
|
|
RETURNING installation_id
|
|
)
|
|
SELECT target.id FROM target
|
|
`
|
|
|
|
type UpsertDingTalkBotIdentityParams struct {
|
|
InstallationID pgtype.UUID `json:"installation_id"`
|
|
WorkspaceID pgtype.UUID `json:"workspace_id"`
|
|
BotName string `json:"bot_name"`
|
|
BotIdentityIssue string `json:"bot_identity_issue"`
|
|
}
|
|
|
|
// Bot identity and the optional permission issue belong to the installation,
|
|
// not to any one group. Keep this as a separate command so the mixed-version
|
|
// compatibility trigger can observe the completed command state when the
|
|
// group-presence command runs next.
|
|
func (q *Queries) UpsertDingTalkBotIdentity(ctx context.Context, arg UpsertDingTalkBotIdentityParams) (pgtype.UUID, error) {
|
|
row := q.db.QueryRow(ctx, upsertDingTalkBotIdentity,
|
|
arg.InstallationID,
|
|
arg.WorkspaceID,
|
|
arg.BotName,
|
|
arg.BotIdentityIssue,
|
|
)
|
|
var id pgtype.UUID
|
|
err := row.Scan(&id)
|
|
return id, err
|
|
}
|