You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot Kotlin Reactive:如何合并多MongoDB查询结果返回单一响应?

实现Spring Boot 2 Kotlin Reactive手机号关联信息查询接口

看起来你正在搭建一个基于Spring Boot 2 Kotlin Reactive的手机号关联信息查询接口,我来分享一个完整的实现思路和代码示例,刚好匹配你的需求:

需求梳理

  • 基于Spring Boot 2 + Kotlin Reactive技术栈构建接口
  • 根据手机号从MongoDB查询关联的个人、公司、部门数据
  • 通过请求参数灵活控制最终返回的显示名称格式:
    • asString:控制是否返回拼接后的字符串(示例中默认返回字符串)
    • firstname/lastname/company/department:配置对应内容的显示规则(比如截取长度、显示优先级等)

核心实现步骤

1. 定义MongoDB实体类

首先定义关联的实体模型,用PhoneAssociation存储手机号与各实体的关联关系,保证灵活性:

import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.mapping.Document

@Document("persons")
data class Person(
    @Id val id: String? = null,
    val firstName: String,
    val lastName: String
)

@Document("companies")
data class Company(
    @Id val id: String? = null,
    val name: String
)

@Document("departments")
data class Department(
    @Id val id: String? = null,
    val name: String,
    val companyId: String
)

@Document("phone_associations")
data class PhoneAssociation(
    @Id val id: String? = null,
    val phoneNumber: String,
    val personId: String? = null,
    val companyId: String? = null,
    val departmentId: String? = null
)

2. 编写Reactive Repository接口

使用Spring Data Reactive MongoDB提供的ReactiveMongoRepository实现异步数据操作:

import org.springframework.data.mongodb.repository.ReactiveMongoRepository
import reactor.core.publisher.Mono

interface PhoneAssociationRepository : ReactiveMongoRepository<PhoneAssociation, String> {
    fun findByPhoneNumber(phoneNumber: String): Mono<PhoneAssociation>
}

interface PersonRepository : ReactiveMongoRepository<Person, String>
interface CompanyRepository : ReactiveMongoRepository<Company, String>
interface DepartmentRepository : ReactiveMongoRepository<Department, String>

3. 实现业务逻辑服务类

核心是处理请求参数的规则(示例中假设参数值为内容截取长度,你可以根据需求改成显示优先级、是否显示等规则),并异步组装显示名称:

import org.springframework.stereotype.Service
import reactor.core.publisher.Mono

@Service
class PhoneInfoService(
    private val phoneAssociationRepo: PhoneAssociationRepository,
    private val personRepo: PersonRepository,
    private val companyRepo: CompanyRepository,
    private val departmentRepo: DepartmentRepository
) {

    fun getDisplayName(
        phoneNumber: String,
        asString: Boolean,
        firstNameLen: Int?,
        lastNameLen: Int?,
        companyLen: Int?,
        departmentLen: Int?
    ): Mono<String> {
        return phoneAssociationRepo.findByPhoneNumber(phoneNumber)
            .flatMap { association ->
                // 并行获取所有关联数据,提升响应效率
                val personMono = association.personId?.let(personRepo::findById) ?: Mono.empty()
                val companyMono = association.companyId?.let(companyRepo::findById) ?: Mono.empty()
                val departmentMono = association.departmentId?.let(departmentRepo::findById) ?: Mono.empty()

                Mono.zip(personMono, companyMono, departmentMono)
                    .map { (person, company, department) ->
                        val displayParts = mutableListOf<String>()

                        // 处理个人名称
                        person?.let {
                            val firstName = firstNameLen?.takeIf { it > 0 }?.let(it.firstName::take) ?: it.firstName
                            val lastName = lastNameLen?.takeIf { it > 0 }?.let(it.lastName::take) ?: it.lastName
                            displayParts.add("$firstName $lastName")
                        }

                        // 处理公司名称
                        company?.let {
                            val companyName = companyLen?.takeIf { it > 0 }?.let(it.name::take) ?: it.name
                            displayParts.add(companyName)
                        }

                        // 处理部门名称
                        department?.let {
                            val deptName = departmentLen?.takeIf { it > 0 }?.let(it.name::take) ?: it.name
                            displayParts.add(deptName)
                        }

                        // 按规则拼接成最终字符串
                        displayParts.joinToString(" | ")
                    }
            }
            .defaultIfEmpty("No associated information found for phone number: $phoneNumber")
    }
}

4. 编写Reactive Controller

定义接口端点,接收请求参数并调用业务逻辑:

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import reactor.core.publisher.Mono

@RestController
@RequestMapping("/api/v1/numbers")
class PhoneInfoController(private val phoneInfoService: PhoneInfoService) {

    @GetMapping("/find/{phoneNumber}")
    fun getDisplayName(
        @PathVariable phoneNumber: String,
        @RequestParam(value = "asString", defaultValue = "true") asString: Boolean,
        @RequestParam(value = "firstname", required = false) firstNameLen: Int?,
        @RequestParam(value = "lastname", required = false) lastNameLen: Int?,
        @RequestParam(value = "company", required = false) companyLen: Int?,
        @RequestParam(value = "department", required = false) departmentLen: Int?
    ): Mono<String> {
        return phoneInfoService.getDisplayName(phoneNumber, asString, firstNameLen, lastNameLen, companyLen, departmentLen)
    }
}

测试示例

使用你提供的请求地址:

http://localhost:8080/api/v1/numbers/find/xxxxxxxxxxx?asString=true&firstname=2&lastname=1&company=3&department=4

假设关联数据为:

  • 个人:firstName="张三", lastName="李四"
  • 公司:name="XX科技有限公司"
  • 部门:name="软件研发部"

则返回的显示字符串为:"张三 李 | XX科 | 软件研发"(对应参数截取长度规则)

如果需要调整参数规则(比如按优先级排序拼接),只需要修改PhoneInfoService中组装displayParts的逻辑即可,比如根据参数值的大小排序后再拼接。

内容的提问来源于stack exchange,提问作者SteVio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:08:11