1047 lines
37 KiB
Go
1047 lines
37 KiB
Go
package host
|
||
|
||
import (
|
||
"context"
|
||
"database/sql"
|
||
"fmt"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"hyapp/pkg/appcode"
|
||
"hyapp/pkg/xerr"
|
||
hostdomain "hyapp/services/user-service/internal/domain/host"
|
||
hostservice "hyapp/services/user-service/internal/service/host"
|
||
)
|
||
|
||
// SearchAgencies 返回用户当前国家内可加入的有效 Agency。
|
||
func (r *Repository) SearchAgencies(ctx context.Context, command hostservice.SearchAgenciesCommand) ([]hostdomain.Agency, error) {
|
||
country, err := r.userCountry(ctx, r.db, command.UserID, "")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if country == "" {
|
||
// 国家是 App 推荐列表的硬边界,缺失时不能退化成跨国或全局列表。
|
||
return nil, xerr.New(xerr.InvalidArgument, "user country is required")
|
||
}
|
||
|
||
query := `
|
||
SELECT
|
||
a.agency_id,
|
||
a.owner_user_id,
|
||
COALESCE(owner.region_id, 0),
|
||
a.parent_bd_user_id,
|
||
a.name,
|
||
a.avatar,
|
||
a.status,
|
||
a.join_enabled,
|
||
a.max_hosts,
|
||
a.created_by_user_id,
|
||
a.created_at_ms,
|
||
a.updated_at_ms,
|
||
COUNT(active_member.membership_id)
|
||
FROM agencies a
|
||
INNER JOIN users owner
|
||
ON owner.app_code = a.app_code AND owner.user_id = a.owner_user_id
|
||
LEFT JOIN agency_memberships active_member
|
||
ON active_member.app_code = a.app_code
|
||
AND active_member.agency_id = a.agency_id
|
||
AND active_member.status = ?
|
||
WHERE a.app_code = ?
|
||
AND owner.country = ?
|
||
AND owner.status = 'active'
|
||
AND a.status = ?
|
||
AND a.join_enabled = TRUE
|
||
`
|
||
args := []any{hostdomain.MembershipStatusActive, appcode.FromContext(ctx), country, hostdomain.AgencyStatusActive}
|
||
if strings.TrimSpace(command.Keyword) != "" {
|
||
keyword := strings.TrimSpace(command.Keyword)
|
||
query += ` AND (a.name LIKE ? OR a.agency_id = ? OR owner.current_display_user_id = ?)`
|
||
args = append(args, "%"+keyword+"%", int64FromKeyword(keyword), keyword)
|
||
}
|
||
query += `
|
||
GROUP BY
|
||
a.agency_id,
|
||
a.owner_user_id,
|
||
owner.region_id,
|
||
a.parent_bd_user_id,
|
||
a.name,
|
||
a.avatar,
|
||
a.status,
|
||
a.join_enabled,
|
||
a.max_hosts,
|
||
a.created_by_user_id,
|
||
a.created_at_ms,
|
||
a.updated_at_ms
|
||
ORDER BY a.created_at_ms DESC, a.agency_id DESC
|
||
LIMIT ?
|
||
`
|
||
args = append(args, command.PageSize)
|
||
|
||
rows, err := r.db.QueryContext(ctx, query, args...)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
|
||
agencies := make([]hostdomain.Agency, 0, command.PageSize)
|
||
for rows.Next() {
|
||
agency, err := scanSearchAgency(rows)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
agencies = append(agencies, agency)
|
||
}
|
||
|
||
return agencies, rows.Err()
|
||
}
|
||
|
||
// GetHostProfile 读取 Host 身份事实。
|
||
func (r *Repository) GetHostProfile(ctx context.Context, userID int64) (hostdomain.HostProfile, error) {
|
||
return queryHostProfile(ctx, r.db, "WHERE user_id = ?", userID)
|
||
}
|
||
|
||
// BatchGetHostProfiles 批量读取 Host 身份事实,缺失 user_id 不填充空 profile。
|
||
func (r *Repository) BatchGetHostProfiles(ctx context.Context, userIDs []int64) (map[int64]hostdomain.HostProfile, error) {
|
||
if len(userIDs) == 0 {
|
||
return map[int64]hostdomain.HostProfile{}, nil
|
||
}
|
||
placeholders := make([]string, 0, len(userIDs))
|
||
args := make([]any, 0, len(userIDs)+1)
|
||
args = append(args, appcode.FromContext(ctx))
|
||
for _, userID := range userIDs {
|
||
placeholders = append(placeholders, "?")
|
||
args = append(args, userID)
|
||
}
|
||
|
||
rows, err := r.db.QueryContext(ctx, fmt.Sprintf(`
|
||
SELECT %s
|
||
FROM host_profiles
|
||
WHERE app_code = ? AND user_id IN (%s)
|
||
`, hostProfileColumns, strings.Join(placeholders, ",")), args...)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
|
||
result := make(map[int64]hostdomain.HostProfile, len(userIDs))
|
||
for rows.Next() {
|
||
// 返回 map 以 user_id 为键,gateway 可以精确区分“非主播/缺失”和“主播但状态不可入账”。
|
||
profile, err := scanHostProfile(rows)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
result[profile.UserID] = profile
|
||
}
|
||
return result, rows.Err()
|
||
}
|
||
|
||
// GetBDProfile 按 role 读取普通 BD 或 BD Leader 身份事实。
|
||
func (r *Repository) GetBDProfile(ctx context.Context, userID int64, role string) (hostdomain.BDProfile, error) {
|
||
if role == hostdomain.BDRoleLeader {
|
||
return queryBDLeaderProfile(ctx, r.db, "WHERE user_id = ?", userID)
|
||
}
|
||
return queryBDProfile(ctx, r.db, "WHERE user_id = ?", userID)
|
||
}
|
||
|
||
// GetCoinSellerProfile 读取币商身份事实。
|
||
func (r *Repository) GetCoinSellerProfile(ctx context.Context, userID int64) (hostdomain.CoinSellerProfile, error) {
|
||
return queryCoinSellerProfile(ctx, r.db, "WHERE user_id = ?", userID)
|
||
}
|
||
|
||
// GetAgency 读取 Agency 当前事实。
|
||
func (r *Repository) GetAgency(ctx context.Context, agencyID int64) (hostdomain.Agency, error) {
|
||
return queryAgency(ctx, r.db, "WHERE agency_id = ?", agencyID)
|
||
}
|
||
|
||
// ListActiveCoinSellersInMyRegion 返回当前用户所在区域的启用币商列表。
|
||
// 该查询从 users.region_id 出发过滤,避免把区域条件交给 gateway 或客户端传入。
|
||
func (r *Repository) ListActiveCoinSellersInMyRegion(ctx context.Context, userID int64) ([]hostdomain.CoinSellerListItem, error) {
|
||
regionID, err := r.userRegion(ctx, r.db, userID, "")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if regionID <= 0 {
|
||
// 币商列表是区域内充值入口,缺少当前区域时不能降级成全局列表。
|
||
return nil, xerr.New(xerr.InvalidArgument, "user region is required")
|
||
}
|
||
if err := requireActiveRegion(ctx, r.db, regionID); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
rows, err := r.db.QueryContext(ctx, `
|
||
SELECT
|
||
u.user_id,
|
||
u.current_display_user_id,
|
||
COALESCE(u.username, ''),
|
||
COALESCE(u.avatar, ''),
|
||
COALESCE(country.country_id, 0),
|
||
COALESCE(u.country, ''),
|
||
COALESCE(country.country_name, ''),
|
||
COALESCE(country.country_display_name, ''),
|
||
u.region_id,
|
||
COALESCE(region.region_code, ''),
|
||
COALESCE(region.name, ''),
|
||
seller.status,
|
||
seller.merchant_asset_type,
|
||
COALESCE(NULLIF(u.contact_info, ''), seller.contact_info, ''),
|
||
seller.updated_at_ms
|
||
FROM users u
|
||
INNER JOIN coin_seller_profiles seller
|
||
ON seller.app_code = u.app_code AND seller.user_id = u.user_id
|
||
LEFT JOIN countries country
|
||
ON country.app_code = u.app_code AND country.country_code = u.country
|
||
LEFT JOIN regions region
|
||
ON region.app_code = u.app_code AND region.region_id = u.region_id
|
||
WHERE u.app_code = ?
|
||
AND u.region_id = ?
|
||
AND u.status = 'active'
|
||
AND seller.status = ?
|
||
AND seller.merchant_asset_type = ?
|
||
ORDER BY seller.updated_at_ms DESC, u.user_id DESC
|
||
`, appcode.FromContext(ctx), regionID, hostdomain.CoinSellerStatusActive, hostdomain.CoinSellerMerchantAssetType)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
|
||
items := make([]hostdomain.CoinSellerListItem, 0)
|
||
for rows.Next() {
|
||
var item hostdomain.CoinSellerListItem
|
||
if err := rows.Scan(
|
||
&item.UserID,
|
||
&item.DisplayUserID,
|
||
&item.Username,
|
||
&item.Avatar,
|
||
&item.CountryID,
|
||
&item.CountryCode,
|
||
&item.CountryName,
|
||
&item.CountryDisplayName,
|
||
&item.RegionID,
|
||
&item.RegionCode,
|
||
&item.RegionName,
|
||
&item.Status,
|
||
&item.MerchantAssetType,
|
||
&item.ContactInfo,
|
||
&item.UpdatedAtMs,
|
||
); err != nil {
|
||
return nil, err
|
||
}
|
||
items = append(items, item)
|
||
}
|
||
return items, rows.Err()
|
||
}
|
||
|
||
// GetUserRoleSummary 用一组索引点查构造我的页角色摘要。
|
||
// 摘要只表达入口显隐需要的当前身份,不加载成员列表、团队统计或薪资数据。
|
||
func (r *Repository) GetUserRoleSummary(ctx context.Context, userID int64) (hostdomain.UserRoleSummary, error) {
|
||
app := appcode.FromContext(ctx)
|
||
summary := hostdomain.UserRoleSummary{UserID: userID}
|
||
var hostStatus string
|
||
var ownerAgencyID int64
|
||
var bdUserID int64
|
||
var bdStatus string
|
||
var leaderUserID int64
|
||
var leaderStatus string
|
||
var managerUserID int64
|
||
var coinSellerStatus string
|
||
var merchantAssetType string
|
||
|
||
err := r.db.QueryRowContext(ctx, `
|
||
SELECT
|
||
COALESCE(h.status, ''),
|
||
COALESCE(a.agency_id, 0),
|
||
COALESCE(b.user_id, 0),
|
||
COALESCE(b.status, ''),
|
||
COALESCE(bl.user_id, 0),
|
||
COALESCE(bl.status, ''),
|
||
COALESCE(m.user_id, 0),
|
||
COALESCE(c.status, ''),
|
||
COALESCE(c.merchant_asset_type, '')
|
||
FROM (SELECT ? AS user_id) u
|
||
LEFT JOIN host_profiles h
|
||
ON h.app_code = ? AND h.user_id = u.user_id
|
||
LEFT JOIN agencies a
|
||
ON a.app_code = ? AND a.owner_user_id = u.user_id AND a.status = ?
|
||
LEFT JOIN bd_profiles b
|
||
ON b.app_code = ? AND b.user_id = u.user_id
|
||
LEFT JOIN bd_leader_profiles bl
|
||
ON bl.app_code = ? AND bl.user_id = u.user_id
|
||
LEFT JOIN manager_profiles m
|
||
ON m.app_code = ? AND m.user_id = u.user_id AND m.status = ?
|
||
LEFT JOIN coin_seller_profiles c
|
||
ON c.app_code = ? AND c.user_id = u.user_id`,
|
||
userID,
|
||
app,
|
||
app, hostdomain.AgencyStatusActive,
|
||
app,
|
||
app,
|
||
app, hostdomain.ManagerStatusActive,
|
||
app,
|
||
).Scan(&hostStatus, &ownerAgencyID, &bdUserID, &bdStatus, &leaderUserID, &leaderStatus, &managerUserID, &coinSellerStatus, &merchantAssetType)
|
||
if err != nil {
|
||
return hostdomain.UserRoleSummary{}, err
|
||
}
|
||
|
||
summary.HostStatus = hostStatus
|
||
summary.IsHost = hostStatus == hostdomain.HostStatusActive
|
||
// Agency 身份只来自 active Agency owner 行;普通 Host 即使有 current_agency_id,也只是成员归属,不能暴露 Agency Center。
|
||
summary.IsAgency = ownerAgencyID > 0
|
||
if summary.IsAgency {
|
||
summary.AgencyID = ownerAgencyID
|
||
}
|
||
// 经理身份来自独立 manager_profiles 表;Agency owner、Host、BD 或 BD Leader 身份都不会隐式获得经理中心入口。
|
||
summary.IsManager = managerUserID > 0
|
||
summary.BDStatus = bdStatus
|
||
if bdUserID > 0 && bdStatus == hostdomain.BDStatusActive {
|
||
summary.BDID = bdUserID
|
||
summary.IsBD = true
|
||
}
|
||
if leaderUserID > 0 && leaderStatus == hostdomain.BDStatusActive {
|
||
if summary.BDID == 0 {
|
||
summary.BDID = leaderUserID
|
||
}
|
||
if summary.BDStatus == "" {
|
||
summary.BDStatus = leaderStatus
|
||
}
|
||
summary.IsBDLeader = true
|
||
}
|
||
summary.CoinSellerStatus = coinSellerStatus
|
||
summary.IsCoinSeller = coinSellerStatus == hostdomain.CoinSellerStatusActive && merchantAssetType == hostdomain.CoinSellerMerchantAssetType
|
||
if err := r.db.QueryRowContext(ctx, `
|
||
SELECT COUNT(*)
|
||
FROM role_invitations
|
||
WHERE app_code = ? AND target_user_id = ? AND status = ?`,
|
||
app, userID, hostdomain.InvitationStatusPending,
|
||
).Scan(&summary.PendingRoleInvitations); err != nil {
|
||
return hostdomain.UserRoleSummary{}, err
|
||
}
|
||
|
||
return summary, nil
|
||
}
|
||
|
||
// HasActiveAgencyOwner 精确判断用户是否为 active Agency owner。
|
||
// 普通 Agency 成员只保留 host_profile.current_agency_id 归属,不会在 UserRoleSummary 中获得 Agency 入口。
|
||
func (r *Repository) HasActiveAgencyOwner(ctx context.Context, userID int64) (int64, bool, error) {
|
||
agency, ok, err := queryActiveAgencyByOwner(ctx, r.db, userID, false)
|
||
if err != nil || !ok {
|
||
return 0, ok, err
|
||
}
|
||
return agency.AgencyID, true, nil
|
||
}
|
||
|
||
// HasActiveManagerProfile 精确判断用户是否拥有 active 经理身份。
|
||
// 经理是 BD Leader/Admin 的上级身份,不能从 Agency owner 或 BD/BD Leader 身份推导出来。
|
||
func (r *Repository) HasActiveManagerProfile(ctx context.Context, userID int64) (bool, error) {
|
||
var id int64
|
||
err := r.db.QueryRowContext(ctx, `
|
||
SELECT user_id
|
||
FROM manager_profiles
|
||
WHERE app_code = ? AND user_id = ? AND status = ?
|
||
LIMIT 1
|
||
`, appcode.FromContext(ctx), userID, hostdomain.ManagerStatusActive).Scan(&id)
|
||
if err == sql.ErrNoRows {
|
||
return false, nil
|
||
}
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
return id > 0, nil
|
||
}
|
||
|
||
// HasActiveManagerCapability 精确校验经理中心单项能力。
|
||
// 这里把 legacy 的 manager_resource_grant 收敛成任一资源赠送权限,写入口仍会按真实资源类型再校验一次。
|
||
func (r *Repository) HasActiveManagerCapability(ctx context.Context, userID int64, capability string) (bool, error) {
|
||
columnSQL := ""
|
||
switch capability {
|
||
case hostservice.CapabilityManagerResourceGrant:
|
||
columnSQL = "(can_grant_avatar_frame = 1 OR can_grant_vehicle = 1 OR can_grant_badge = 1)"
|
||
case hostservice.CapabilityManagerGrantAvatarFrame:
|
||
columnSQL = "can_grant_avatar_frame = 1"
|
||
case hostservice.CapabilityManagerGrantVehicle:
|
||
columnSQL = "can_grant_vehicle = 1"
|
||
case hostservice.CapabilityManagerGrantBadge:
|
||
columnSQL = "can_grant_badge = 1"
|
||
case hostservice.CapabilityManagerGrantVIP:
|
||
columnSQL = "can_grant_vip = 1"
|
||
case hostservice.CapabilityManagerUpdateUserLevel:
|
||
columnSQL = "can_update_user_level = 1"
|
||
case hostservice.CapabilityManagerAddBDLeader:
|
||
columnSQL = "can_add_bd_leader = 1"
|
||
case hostservice.CapabilityManagerAddAdmin:
|
||
columnSQL = "can_add_admin = 1"
|
||
case hostservice.CapabilityManagerAddSuperadmin:
|
||
columnSQL = "can_add_superadmin = 1"
|
||
case hostservice.CapabilityManagerBlockUser:
|
||
columnSQL = "can_block_user = 1"
|
||
case hostservice.CapabilityManagerTransferCountry:
|
||
columnSQL = "can_transfer_user_country = 1"
|
||
default:
|
||
return false, nil
|
||
}
|
||
var id int64
|
||
err := r.db.QueryRowContext(ctx, `
|
||
SELECT user_id
|
||
FROM manager_profiles
|
||
WHERE app_code = ? AND user_id = ? AND status = ? AND `+columnSQL+`
|
||
LIMIT 1
|
||
`, appcode.FromContext(ctx), userID, hostdomain.ManagerStatusActive).Scan(&id)
|
||
if err == sql.ErrNoRows {
|
||
return false, nil
|
||
}
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
return id > 0, nil
|
||
}
|
||
|
||
// ListBDLeaderBDs 读取 BD Leader 直属普通 BD;调用前先校验独立 Leader 行当前仍有效,避免停用后继续读取团队数据。
|
||
func (r *Repository) ListBDLeaderBDs(ctx context.Context, command hostservice.ListBDLeaderBDsCommand) ([]hostdomain.BDProfile, error) {
|
||
leader, err := queryBDLeaderProfile(ctx, r.db, "WHERE user_id = ?", command.LeaderUserID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if leader.Status != hostdomain.BDStatusActive {
|
||
return nil, xerr.New(xerr.PermissionDenied, "leader is not active")
|
||
}
|
||
status := command.Status
|
||
if status == "" {
|
||
status = hostdomain.BDStatusActive
|
||
}
|
||
clause, args := appScopedClause(ctx, "WHERE role = ? AND parent_leader_user_id = ? AND status = ? ORDER BY created_at_ms DESC, user_id DESC LIMIT ?", hostdomain.BDRoleBD, command.LeaderUserID, status, command.PageSize)
|
||
return queryBDProfiles(ctx, r.db, clause, args...)
|
||
}
|
||
|
||
// ListBDLeaderAgencies 读取 BD Leader 团队 Agency;包含直属 leader 自己创建的 Agency 和下属普通 BD 创建的 Agency。
|
||
func (r *Repository) ListBDLeaderAgencies(ctx context.Context, command hostservice.ListBDLeaderAgenciesCommand) ([]hostdomain.Agency, error) {
|
||
leader, err := queryBDLeaderProfile(ctx, r.db, "WHERE user_id = ?", command.LeaderUserID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if leader.Status != hostdomain.BDStatusActive {
|
||
return nil, xerr.New(xerr.PermissionDenied, "leader is not active")
|
||
}
|
||
status := command.Status
|
||
if status == "" {
|
||
status = hostdomain.AgencyStatusActive
|
||
}
|
||
clause, args := appScopedClause(ctx, `WHERE status = ? AND (
|
||
parent_bd_user_id = ?
|
||
OR parent_bd_user_id IN (
|
||
SELECT user_id
|
||
FROM bd_profiles
|
||
WHERE app_code = agencies.app_code
|
||
AND role = ?
|
||
AND parent_leader_user_id = ?
|
||
AND status = ?
|
||
)
|
||
) ORDER BY created_at_ms DESC, agency_id DESC LIMIT ?`, status, command.LeaderUserID, hostdomain.BDRoleBD, command.LeaderUserID, hostdomain.BDStatusActive, command.PageSize)
|
||
return queryAgencies(ctx, r.db, clause, args...)
|
||
}
|
||
|
||
// ListBDAgencies 只读取一个有效普通 BD 直接拓展的 Agency,不包含同负责人下其他 BD 的数据。
|
||
func (r *Repository) ListBDAgencies(ctx context.Context, command hostservice.ListBDAgenciesCommand) ([]hostdomain.Agency, error) {
|
||
bd, err := queryBDProfile(ctx, r.db, "WHERE user_id = ?", command.BDUserID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if bd.Status != hostdomain.BDStatusActive || bd.Role != hostdomain.BDRoleBD {
|
||
return nil, xerr.New(xerr.PermissionDenied, "bd is not active")
|
||
}
|
||
status := command.Status
|
||
if status == "" {
|
||
status = hostdomain.AgencyStatusActive
|
||
}
|
||
clause, args := appScopedClause(ctx, "WHERE status = ? AND parent_bd_user_id = ? ORDER BY created_at_ms DESC, agency_id DESC LIMIT ?", status, command.BDUserID, command.PageSize)
|
||
return queryAgencies(ctx, r.db, clause, args...)
|
||
}
|
||
|
||
// ListRoleInvitations 读取目标用户收到的角色邀请消息;默认由业务层传入 pending 状态用于消息中心。
|
||
func (r *Repository) ListRoleInvitations(ctx context.Context, command hostservice.ListRoleInvitationsCommand) ([]hostdomain.RoleInvitation, error) {
|
||
query := fmt.Sprintf(`SELECT %s FROM role_invitations WHERE app_code = ? AND target_user_id = ?`, roleInvitationColumns)
|
||
args := []any{appcode.FromContext(ctx), command.TargetUserID}
|
||
if command.Status != "" {
|
||
query += " AND status = ?"
|
||
args = append(args, command.Status)
|
||
}
|
||
// 消息中心优先展示最新邀请;同毫秒内用 invitation_id 保持稳定排序。
|
||
query += " ORDER BY created_at_ms DESC, invitation_id DESC LIMIT ?"
|
||
args = append(args, command.PageSize)
|
||
return queryRoleInvitations(ctx, r.db, query, args...)
|
||
}
|
||
|
||
// ListAgencyMembers 读取 Agency 成员关系列表。
|
||
func (r *Repository) ListAgencyMembers(ctx context.Context, agencyID int64, status string) ([]hostdomain.AgencyMembership, error) {
|
||
query := fmt.Sprintf(`SELECT %s FROM agency_memberships WHERE app_code = ? AND agency_id = ?`, agencyMembershipColumns)
|
||
args := []any{appcode.FromContext(ctx), agencyID}
|
||
if status != "" {
|
||
query += " AND status = ?"
|
||
args = append(args, status)
|
||
}
|
||
query += " ORDER BY joined_at_ms DESC, membership_id DESC"
|
||
return queryAgencyMemberships(ctx, r.db, query, args...)
|
||
}
|
||
|
||
// ListAgencyApplications 读取 Agency 申请列表。
|
||
func (r *Repository) ListAgencyApplications(ctx context.Context, agencyID int64, status string) ([]hostdomain.AgencyApplication, error) {
|
||
query := fmt.Sprintf(`SELECT %s FROM agency_applications WHERE app_code = ? AND agency_id = ?`, agencyApplicationColumns)
|
||
args := []any{appcode.FromContext(ctx), agencyID}
|
||
if status != "" {
|
||
query += " AND status = ?"
|
||
args = append(args, status)
|
||
}
|
||
query += " ORDER BY created_at_ms DESC, application_id DESC"
|
||
return queryAgencyApplications(ctx, r.db, query, args...)
|
||
}
|
||
|
||
func queryHostProfile(ctx context.Context, q sqlQueryer, clause string, args ...any) (hostdomain.HostProfile, error) {
|
||
clause, args = appScopedClause(ctx, clause, args...)
|
||
profile, err := scanHostProfile(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM host_profiles %s`, hostProfileColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.HostProfile{}, xerr.New(xerr.NotFound, "host profile not found")
|
||
}
|
||
return profile, err
|
||
}
|
||
|
||
func queryHostProfileMaybe(ctx context.Context, q sqlQueryer, userID int64, lock bool) (hostdomain.HostProfile, bool, error) {
|
||
clause := "WHERE user_id = ? AND status = ?"
|
||
if lock {
|
||
clause += " FOR UPDATE"
|
||
}
|
||
clause, args := appScopedClause(ctx, clause, userID, hostdomain.HostStatusActive)
|
||
profile, err := scanHostProfile(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM host_profiles %s`, hostProfileColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.HostProfile{}, false, nil
|
||
}
|
||
if err != nil {
|
||
return hostdomain.HostProfile{}, false, err
|
||
}
|
||
return profile, true, nil
|
||
}
|
||
|
||
func queryHostProfileByUserMaybe(ctx context.Context, q sqlQueryer, userID int64, lock bool) (hostdomain.HostProfile, bool, error) {
|
||
clause := "WHERE user_id = ?"
|
||
if lock {
|
||
// 后台创建 Agency owner 时需要覆盖 active/disabled 任意主播身份行,避免重复插入主键。
|
||
clause += " FOR UPDATE"
|
||
}
|
||
clause, args := appScopedClause(ctx, clause, userID)
|
||
profile, err := scanHostProfile(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM host_profiles %s`, hostProfileColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.HostProfile{}, false, nil
|
||
}
|
||
if err != nil {
|
||
return hostdomain.HostProfile{}, false, err
|
||
}
|
||
return profile, true, nil
|
||
}
|
||
|
||
func scanHostProfile(scanner rowScanner) (hostdomain.HostProfile, error) {
|
||
var profile hostdomain.HostProfile
|
||
err := scanner.Scan(
|
||
&profile.UserID,
|
||
&profile.Status,
|
||
&profile.RegionID,
|
||
&profile.CurrentAgencyID,
|
||
&profile.CurrentMembershipID,
|
||
&profile.CurrentAgencyOwnerUserID,
|
||
&profile.Source,
|
||
&profile.FirstBecameHostAtMs,
|
||
&profile.CreatedAtMs,
|
||
&profile.UpdatedAtMs,
|
||
)
|
||
return profile, err
|
||
}
|
||
|
||
func queryAgency(ctx context.Context, q sqlQueryer, clause string, args ...any) (hostdomain.Agency, error) {
|
||
clause, args = appScopedClause(ctx, clause, args...)
|
||
agency, err := scanAgency(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM agencies %s`, agencyColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.Agency{}, xerr.New(xerr.NotFound, "agency not found")
|
||
}
|
||
return agency, err
|
||
}
|
||
|
||
func queryAgencies(ctx context.Context, q sqlQueryer, clause string, args ...any) ([]hostdomain.Agency, error) {
|
||
rows, err := q.QueryContext(ctx, fmt.Sprintf(`SELECT %s FROM agencies %s`, agencyColumns, clause), args...)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
|
||
agencies := make([]hostdomain.Agency, 0)
|
||
for rows.Next() {
|
||
agency, err := scanAgency(rows)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
agencies = append(agencies, agency)
|
||
}
|
||
return agencies, rows.Err()
|
||
}
|
||
|
||
func queryActiveAgencyByOwner(ctx context.Context, q sqlQueryer, ownerUserID int64, lock bool) (hostdomain.Agency, bool, error) {
|
||
clause := "WHERE owner_user_id = ? AND status = ?"
|
||
if lock {
|
||
clause += " FOR UPDATE"
|
||
}
|
||
clause, args := appScopedClause(ctx, clause, ownerUserID, hostdomain.AgencyStatusActive)
|
||
agency, err := scanAgency(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM agencies %s`, agencyColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.Agency{}, false, nil
|
||
}
|
||
if err != nil {
|
||
return hostdomain.Agency{}, false, err
|
||
}
|
||
return agency, true, nil
|
||
}
|
||
|
||
func queryAgencyByOwnerMaybe(ctx context.Context, q sqlQueryer, ownerUserID int64, lock bool) (hostdomain.Agency, bool, error) {
|
||
clause := "WHERE owner_user_id = ?"
|
||
if lock {
|
||
// 后台创建 Agency 时要锁住 owner 名下所有历史 Agency 行,避免 active/closed 切换和新建并发。
|
||
clause += " FOR UPDATE"
|
||
}
|
||
clause, args := appScopedClause(ctx, clause, ownerUserID)
|
||
agency, err := scanAgency(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM agencies %s`, agencyColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.Agency{}, false, nil
|
||
}
|
||
if err != nil {
|
||
return hostdomain.Agency{}, false, err
|
||
}
|
||
return agency, true, nil
|
||
}
|
||
|
||
func scanAgency(scanner rowScanner) (hostdomain.Agency, error) {
|
||
var agency hostdomain.Agency
|
||
err := scanner.Scan(
|
||
&agency.AgencyID,
|
||
&agency.OwnerUserID,
|
||
&agency.RegionID,
|
||
&agency.ParentBDUserID,
|
||
&agency.Name,
|
||
&agency.Avatar,
|
||
&agency.Status,
|
||
&agency.JoinEnabled,
|
||
&agency.MaxHosts,
|
||
&agency.CreatedByUserID,
|
||
&agency.CreatedAtMs,
|
||
&agency.UpdatedAtMs,
|
||
&agency.ActiveHostCount,
|
||
)
|
||
return agency, err
|
||
}
|
||
|
||
func scanSearchAgency(scanner rowScanner) (hostdomain.Agency, error) {
|
||
var agency hostdomain.Agency
|
||
err := scanner.Scan(
|
||
&agency.AgencyID,
|
||
&agency.OwnerUserID,
|
||
&agency.RegionID,
|
||
&agency.ParentBDUserID,
|
||
&agency.Name,
|
||
&agency.Avatar,
|
||
&agency.Status,
|
||
&agency.JoinEnabled,
|
||
&agency.MaxHosts,
|
||
&agency.CreatedByUserID,
|
||
&agency.CreatedAtMs,
|
||
&agency.UpdatedAtMs,
|
||
&agency.ActiveHostCount,
|
||
)
|
||
return agency, err
|
||
}
|
||
|
||
func int64FromKeyword(keyword string) int64 {
|
||
value, err := strconv.ParseInt(strings.TrimSpace(keyword), 10, 64)
|
||
if err != nil {
|
||
return 0
|
||
}
|
||
return value
|
||
}
|
||
|
||
func queryBDProfile(ctx context.Context, q sqlQueryer, clause string, args ...any) (hostdomain.BDProfile, error) {
|
||
clause, args = appScopedClause(ctx, clause, args...)
|
||
profile, err := scanBDProfile(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM bd_profiles %s`, bdProfileColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.BDProfile{}, xerr.New(xerr.NotFound, "bd profile not found")
|
||
}
|
||
return profile, err
|
||
}
|
||
|
||
func queryBDProfiles(ctx context.Context, q sqlQueryer, clause string, args ...any) ([]hostdomain.BDProfile, error) {
|
||
rows, err := q.QueryContext(ctx, fmt.Sprintf(`SELECT %s FROM bd_profiles %s`, bdProfileColumns, clause), args...)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
|
||
profiles := make([]hostdomain.BDProfile, 0)
|
||
for rows.Next() {
|
||
profile, err := scanBDProfile(rows)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
profiles = append(profiles, profile)
|
||
}
|
||
return profiles, rows.Err()
|
||
}
|
||
|
||
func queryBDLeaderProfile(ctx context.Context, q sqlQueryer, clause string, args ...any) (hostdomain.BDProfile, error) {
|
||
clause, args = appScopedClause(ctx, clause, args...)
|
||
profile, err := scanBDProfile(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM bd_leader_profiles %s`, bdLeaderProfileColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.BDProfile{}, xerr.New(xerr.NotFound, "bd leader profile not found")
|
||
}
|
||
return profile, err
|
||
}
|
||
|
||
func queryBDProfileMaybe(ctx context.Context, q sqlQueryer, userID int64, lock bool) (hostdomain.BDProfile, bool, error) {
|
||
clause := "WHERE user_id = ? AND status = ?"
|
||
if lock {
|
||
clause += " FOR UPDATE"
|
||
}
|
||
clause, args := appScopedClause(ctx, clause, userID, hostdomain.BDStatusActive)
|
||
profile, err := scanBDProfile(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM bd_profiles %s`, bdProfileColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.BDProfile{}, false, nil
|
||
}
|
||
if err != nil {
|
||
return hostdomain.BDProfile{}, false, err
|
||
}
|
||
return profile, true, nil
|
||
}
|
||
|
||
func queryBDLeaderProfileMaybe(ctx context.Context, q sqlQueryer, userID int64, lock bool) (hostdomain.BDProfile, bool, error) {
|
||
clause := "WHERE user_id = ? AND status = ?"
|
||
if lock {
|
||
clause += " FOR UPDATE"
|
||
}
|
||
clause, args := appScopedClause(ctx, clause, userID, hostdomain.BDStatusActive)
|
||
profile, err := scanBDProfile(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM bd_leader_profiles %s`, bdLeaderProfileColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.BDProfile{}, false, nil
|
||
}
|
||
if err != nil {
|
||
return hostdomain.BDProfile{}, false, err
|
||
}
|
||
return profile, true, nil
|
||
}
|
||
|
||
func queryBDProfileByUserMaybe(ctx context.Context, q sqlQueryer, userID int64, lock bool) (hostdomain.BDProfile, bool, error) {
|
||
clause := "WHERE user_id = ?"
|
||
if lock {
|
||
// 后台创建/停用角色使用主键锁,覆盖 active 和 disabled 两种状态。
|
||
clause += " FOR UPDATE"
|
||
}
|
||
clause, args := appScopedClause(ctx, clause, userID)
|
||
profile, err := scanBDProfile(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM bd_profiles %s`, bdProfileColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.BDProfile{}, false, nil
|
||
}
|
||
if err != nil {
|
||
return hostdomain.BDProfile{}, false, err
|
||
}
|
||
return profile, true, nil
|
||
}
|
||
|
||
func queryBDLeaderProfileByUserMaybe(ctx context.Context, q sqlQueryer, userID int64, lock bool) (hostdomain.BDProfile, bool, error) {
|
||
clause := "WHERE user_id = ?"
|
||
if lock {
|
||
// 后台创建/停用 Leader 使用主键锁,覆盖 active 和 disabled 两种状态。
|
||
clause += " FOR UPDATE"
|
||
}
|
||
clause, args := appScopedClause(ctx, clause, userID)
|
||
profile, err := scanBDProfile(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM bd_leader_profiles %s`, bdLeaderProfileColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.BDProfile{}, false, nil
|
||
}
|
||
if err != nil {
|
||
return hostdomain.BDProfile{}, false, err
|
||
}
|
||
return profile, true, nil
|
||
}
|
||
|
||
func scanBDProfile(scanner rowScanner) (hostdomain.BDProfile, error) {
|
||
var profile hostdomain.BDProfile
|
||
err := scanner.Scan(
|
||
&profile.UserID,
|
||
&profile.Role,
|
||
&profile.RegionID,
|
||
&profile.ParentLeaderUserID,
|
||
&profile.Status,
|
||
&profile.CreatedByUserID,
|
||
&profile.CreatedAtMs,
|
||
&profile.UpdatedAtMs,
|
||
)
|
||
return profile, err
|
||
}
|
||
|
||
func queryCoinSellerProfile(ctx context.Context, q sqlQueryer, clause string, args ...any) (hostdomain.CoinSellerProfile, error) {
|
||
clause, args = appScopedClause(ctx, clause, args...)
|
||
profile, err := scanCoinSellerProfile(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM coin_seller_profiles %s`, coinSellerProfileColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.CoinSellerProfile{}, xerr.New(xerr.NotFound, "coin_seller profile not found")
|
||
}
|
||
return profile, err
|
||
}
|
||
|
||
func queryCoinSellerProfileByUserMaybe(ctx context.Context, q sqlQueryer, userID int64, lock bool) (hostdomain.CoinSellerProfile, bool, error) {
|
||
clause := "WHERE user_id = ?"
|
||
if lock {
|
||
// 币商身份可以和 Agency/BD/BD Leader 并存,但同一用户只能有一条币商身份事实。
|
||
clause += " FOR UPDATE"
|
||
}
|
||
clause, args := appScopedClause(ctx, clause, userID)
|
||
profile, err := scanCoinSellerProfile(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM coin_seller_profiles %s`, coinSellerProfileColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.CoinSellerProfile{}, false, nil
|
||
}
|
||
if err != nil {
|
||
return hostdomain.CoinSellerProfile{}, false, err
|
||
}
|
||
return profile, true, nil
|
||
}
|
||
|
||
func scanCoinSellerProfile(scanner rowScanner) (hostdomain.CoinSellerProfile, error) {
|
||
var profile hostdomain.CoinSellerProfile
|
||
err := scanner.Scan(
|
||
&profile.UserID,
|
||
&profile.Status,
|
||
&profile.MerchantAssetType,
|
||
&profile.CreatedByUserID,
|
||
&profile.CreatedAtMs,
|
||
&profile.UpdatedAtMs,
|
||
)
|
||
return profile, err
|
||
}
|
||
|
||
func queryAgencyMembership(ctx context.Context, q sqlQueryer, clause string, args ...any) (hostdomain.AgencyMembership, error) {
|
||
clause, args = appScopedClause(ctx, clause, args...)
|
||
membership, err := scanAgencyMembership(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM agency_memberships %s`, agencyMembershipColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.AgencyMembership{}, xerr.New(xerr.NotFound, "agency membership not found")
|
||
}
|
||
return membership, err
|
||
}
|
||
|
||
func queryActiveMembershipByHost(ctx context.Context, q sqlQueryer, hostUserID int64, lock bool) (hostdomain.AgencyMembership, bool, error) {
|
||
clause := "WHERE host_user_id = ? AND status = ?"
|
||
if lock {
|
||
clause += " FOR UPDATE"
|
||
}
|
||
clause, args := appScopedClause(ctx, clause, hostUserID, hostdomain.MembershipStatusActive)
|
||
membership, err := scanAgencyMembership(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM agency_memberships %s`, agencyMembershipColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.AgencyMembership{}, false, nil
|
||
}
|
||
if err != nil {
|
||
return hostdomain.AgencyMembership{}, false, err
|
||
}
|
||
return membership, true, nil
|
||
}
|
||
|
||
func queryAgencyMemberships(ctx context.Context, q sqlQueryer, query string, args ...any) ([]hostdomain.AgencyMembership, error) {
|
||
rows, err := q.QueryContext(ctx, query, args...)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
memberships := make([]hostdomain.AgencyMembership, 0)
|
||
for rows.Next() {
|
||
membership, err := scanAgencyMembership(rows)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
memberships = append(memberships, membership)
|
||
}
|
||
return memberships, rows.Err()
|
||
}
|
||
|
||
func scanAgencyMembership(scanner rowScanner) (hostdomain.AgencyMembership, error) {
|
||
var membership hostdomain.AgencyMembership
|
||
err := scanner.Scan(
|
||
&membership.MembershipID,
|
||
&membership.AgencyID,
|
||
&membership.HostUserID,
|
||
&membership.RegionID,
|
||
&membership.MembershipType,
|
||
&membership.Status,
|
||
&membership.JoinedAtMs,
|
||
&membership.EndedAtMs,
|
||
&membership.EndedByUserID,
|
||
&membership.EndedReason,
|
||
&membership.CreatedAtMs,
|
||
&membership.UpdatedAtMs,
|
||
)
|
||
return membership, err
|
||
}
|
||
|
||
func queryAgencyApplication(ctx context.Context, q sqlQueryer, clause string, args ...any) (hostdomain.AgencyApplication, error) {
|
||
clause, args = appScopedClause(ctx, clause, args...)
|
||
application, err := scanAgencyApplication(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM agency_applications %s`, agencyApplicationColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.AgencyApplication{}, xerr.New(xerr.NotFound, "agency application not found")
|
||
}
|
||
return application, err
|
||
}
|
||
|
||
func queryAgencyApplications(ctx context.Context, q sqlQueryer, query string, args ...any) ([]hostdomain.AgencyApplication, error) {
|
||
rows, err := q.QueryContext(ctx, query, args...)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
applications := make([]hostdomain.AgencyApplication, 0)
|
||
for rows.Next() {
|
||
application, err := scanAgencyApplication(rows)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
applications = append(applications, application)
|
||
}
|
||
return applications, rows.Err()
|
||
}
|
||
|
||
func scanAgencyApplication(scanner rowScanner) (hostdomain.AgencyApplication, error) {
|
||
var application hostdomain.AgencyApplication
|
||
err := scanner.Scan(
|
||
&application.ApplicationID,
|
||
&application.CommandID,
|
||
&application.ApplicantUserID,
|
||
&application.AgencyID,
|
||
&application.RegionID,
|
||
&application.Status,
|
||
&application.ReviewedByUserID,
|
||
&application.ReviewReason,
|
||
&application.ReviewedAtMs,
|
||
&application.CreatedAtMs,
|
||
&application.UpdatedAtMs,
|
||
)
|
||
return application, err
|
||
}
|
||
|
||
func queryRoleInvitation(ctx context.Context, q sqlQueryer, clause string, args ...any) (hostdomain.RoleInvitation, error) {
|
||
clause, args = appScopedClause(ctx, clause, args...)
|
||
invitation, err := scanRoleInvitation(q.QueryRowContext(ctx, fmt.Sprintf(`SELECT %s FROM role_invitations %s`, roleInvitationColumns, clause), args...))
|
||
if err == sql.ErrNoRows {
|
||
return hostdomain.RoleInvitation{}, xerr.New(xerr.NotFound, "role invitation not found")
|
||
}
|
||
return invitation, err
|
||
}
|
||
|
||
func queryRoleInvitations(ctx context.Context, q sqlQueryer, query string, args ...any) ([]hostdomain.RoleInvitation, error) {
|
||
rows, err := q.QueryContext(ctx, query, args...)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
invitations := make([]hostdomain.RoleInvitation, 0)
|
||
for rows.Next() {
|
||
invitation, err := scanRoleInvitation(rows)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
invitations = append(invitations, invitation)
|
||
}
|
||
return invitations, rows.Err()
|
||
}
|
||
|
||
func scanRoleInvitation(scanner rowScanner) (hostdomain.RoleInvitation, error) {
|
||
var invitation hostdomain.RoleInvitation
|
||
err := scanner.Scan(
|
||
&invitation.InvitationID,
|
||
&invitation.CommandID,
|
||
&invitation.InvitationType,
|
||
&invitation.Status,
|
||
&invitation.InviterUserID,
|
||
&invitation.InviterBDUserID,
|
||
&invitation.TargetUserID,
|
||
&invitation.RegionID,
|
||
&invitation.AgencyName,
|
||
&invitation.ParentBDUserID,
|
||
&invitation.ParentLeaderUserID,
|
||
&invitation.ProcessedByUserID,
|
||
&invitation.ProcessReason,
|
||
&invitation.ProcessedAtMs,
|
||
&invitation.CreatedAtMs,
|
||
&invitation.UpdatedAtMs,
|
||
)
|
||
return invitation, err
|
||
}
|
||
|
||
func reviewResultByApplicationID(ctx context.Context, q sqlQueryer, applicationID int64) (hostdomain.ReviewAgencyApplicationResult, error) {
|
||
application, err := queryAgencyApplication(ctx, q, "WHERE application_id = ?", applicationID)
|
||
if err != nil {
|
||
return hostdomain.ReviewAgencyApplicationResult{}, err
|
||
}
|
||
result := hostdomain.ReviewAgencyApplicationResult{Application: application}
|
||
if application.Status == hostdomain.ApplicationStatusApproved {
|
||
profile, err := queryHostProfile(ctx, q, "WHERE user_id = ?", application.ApplicantUserID)
|
||
if err != nil {
|
||
return hostdomain.ReviewAgencyApplicationResult{}, err
|
||
}
|
||
membership, err := queryAgencyMembership(ctx, q, "WHERE membership_id = ?", profile.CurrentMembershipID)
|
||
if err != nil {
|
||
return hostdomain.ReviewAgencyApplicationResult{}, err
|
||
}
|
||
result.HostProfile = profile
|
||
result.Membership = membership
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
func kickResultByMembershipID(ctx context.Context, q sqlQueryer, membershipID int64) (hostdomain.KickAgencyHostResult, error) {
|
||
membership, err := queryAgencyMembership(ctx, q, "WHERE membership_id = ?", membershipID)
|
||
if err != nil {
|
||
return hostdomain.KickAgencyHostResult{}, err
|
||
}
|
||
profile, err := queryHostProfile(ctx, q, "WHERE user_id = ?", membership.HostUserID)
|
||
if err != nil {
|
||
return hostdomain.KickAgencyHostResult{}, err
|
||
}
|
||
return hostdomain.KickAgencyHostResult{Membership: membership, HostProfile: profile}, nil
|
||
}
|
||
|
||
func processResultByInvitationID(ctx context.Context, q sqlQueryer, invitationID int64) (hostdomain.ProcessRoleInvitationResult, error) {
|
||
invitation, err := queryRoleInvitation(ctx, q, "WHERE invitation_id = ?", invitationID)
|
||
if err != nil {
|
||
return hostdomain.ProcessRoleInvitationResult{}, err
|
||
}
|
||
result := hostdomain.ProcessRoleInvitationResult{Invitation: invitation}
|
||
if invitation.Status != hostdomain.InvitationStatusAccepted {
|
||
return result, nil
|
||
}
|
||
|
||
// 只有接受动作会创建后续身份事实;按邀请类型回填前端需要立即刷新的最新关系。
|
||
switch invitation.InvitationType {
|
||
case hostdomain.InvitationTypeAgency:
|
||
if profile, err := queryHostProfile(ctx, q, "WHERE user_id = ?", invitation.TargetUserID); err == nil {
|
||
result.HostProfile = profile
|
||
if profile.CurrentMembershipID > 0 {
|
||
result.Membership, _ = queryAgencyMembership(ctx, q, "WHERE membership_id = ?", profile.CurrentMembershipID)
|
||
}
|
||
}
|
||
if agency, ok, err := queryActiveAgencyByOwner(ctx, q, invitation.TargetUserID, false); err != nil {
|
||
return hostdomain.ProcessRoleInvitationResult{}, err
|
||
} else if ok {
|
||
result.Agency = agency
|
||
}
|
||
case hostdomain.InvitationTypeBD:
|
||
if bd, err := queryBDProfile(ctx, q, "WHERE user_id = ?", invitation.TargetUserID); err == nil {
|
||
result.BDProfile = bd
|
||
}
|
||
case hostdomain.InvitationTypeHost:
|
||
if profile, err := queryHostProfile(ctx, q, "WHERE user_id = ?", invitation.TargetUserID); err == nil {
|
||
result.HostProfile = profile
|
||
if profile.CurrentMembershipID > 0 {
|
||
result.Membership, _ = queryAgencyMembership(ctx, q, "WHERE membership_id = ?", profile.CurrentMembershipID)
|
||
}
|
||
if profile.CurrentAgencyID > 0 {
|
||
result.Agency, _ = queryAgency(ctx, q, "WHERE agency_id = ?", profile.CurrentAgencyID)
|
||
}
|
||
}
|
||
}
|
||
return result, nil
|
||
}
|