需求:列表接口查询速度慢,需要用MongoDB每五分钟进行一次数据同步,原接口改用MongoDB查而不是用RestTemplate访问
此项目分:1.core公共模块,2.background背景任务模块,3.api模块
build-gradle添加依赖和yml配置
yml
spring:
data:
mongodb:
uri: mongodb://localhost:27017/local
build-gradle
api 'org.springframework.boot:spring-boot-starter-data-mongodb'
core
package com.umh.medicalbookingplatform.core.properties
import org.slf4j.LoggerFactory
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.stereotype.Component
import javax.annotation.PostConstruct
@Component
@ConfigurationProperties("app")
class ApplicationProperties {
private val logger = LoggerFactory.getLogger(javaClass)
var environment: String? = null
var s3: S3Properties = S3Properties()
var staticSmsVerificationEnabled: Boolean? = null
var staticSmsVerificationValue: String? = null
var keycloakAuthServerUrl: String? = null
var keycloakGlobalCmsRealm: String? = null
var keycloakGlobalCmsClient: String? = null
var keycloakGlobalProfileRealm: String? = null
var keycloakGlobalProfileClient: String? = null
var keycloakBookingSystemRealm: String? = null
var keycloakBookingSystemClient: String? = null
var keycloakUmhBookingSystemRealm: String? = null
var keycloakUmhBookingSystemClient: String? = null
var keycloakApiUsername: String? = null
var keycloakApiPassword: String? = null
var globalCmsApiEndpoint: String? = null
var globalGPApiEndpoint: String? = null
var bookingSystemApiEndpoint: String? = null
var umhBookingSystemAPIEndpoint: String? = null
var twilioProperties: TwilioProperties = TwilioProperties()
var firebaseAdminSdk: String? = null
var wechatAppId: String? = null
var wechatAppSecret: String? = null
@PostConstruct
fun postConstruct() {
logger.debug("environment=$environment")
}
}
使用此类会自动搜索使用模块下的yml配置
core
package com.umh.medicalbookingplatform.core.datainitializer
import com.umh.medicalbookingplatform.core.model.background.BackgroundTask
import com.umh.medicalbookingplatform.core.model.background.BackgroundTaskLock
import com.umh.medicalbookingplatform.core.model.background.BackgroundTaskRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.util.*
import javax.persistence.EntityManager
@Service
class BackgroundTaskDataInitializer : DataInitializer {
@Autowired
private lateinit var em: EntityManager
@Autowired
private lateinit var backgroundTaskRepository: BackgroundTaskRepository
@Transactional
override fun initialize() {
//测试时改为了1分钟
createBackgroundTask(
BackgroundTask.BackgroundTaskCode.GLOBAL_CMS_SYNCHRONIZATION_TASK,
"Global CMS Synchronization",
BackgroundTask.BackgroundTaskIntervalType.MINUTE,
1,
true
)
}
private fun createBackgroundTask(
code: BackgroundTask.BackgroundTaskCode,
name: String,
intervalType: BackgroundTask.BackgroundTaskIntervalType,
interval: Int,
enabled: Boolean
) {
if (backgroundTaskRepository.findByCode(code) == null) {
val currentDate = Date()
val bt = BackgroundTask().apply bt@{
this.code = code
this.name = name
this.intervalType = intervalType
this.interval = interval;
nextProcessTime = currentDate
lastProcessStartTime = currentDate
lastProcessEndTime = currentDate
lastProcessStatus = BackgroundTask.LastProcessStatus.SUCCESS
this.enabled = enabled
backgroundTaskLock = BackgroundTaskLock().apply {
backgroundTask = this@bt
}
}
em.merge(bt)
}
}
}
当前模块的主启动类-background
package com.umh.medicalbookingplatform.background
import com.umh.medicalbookingplatform.core.audit.StaticAuditorAware
import com.umh.medicalbookingplatform.core.config.CoreConfiguration
import com.umh.medicalbookingplatform.core.properties.ApplicationProperties
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder
import org.keycloak.OAuth2Constants
import org.keycloak.admin.client.Keycloak
import org.keycloak.admin.client.KeycloakBuilder
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.SpringApplication
import org.springframework.boot.WebApplicationType
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.cache.annotation.EnableCaching
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Import
import org.springframework.data.domain.AuditorAware
import org.springframework.data.jpa.repository.config.EnableJpaAuditing
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.scheduling.annotation.EnableScheduling
import org.springframework.scheduling.annotation.SchedulingConfigurer
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
import org.springframework.scheduling.config.ScheduledTaskRegistrar
import java.security.Security
import java.util.*
@EnableJpaAuditing
@EnableCaching
@EnableScheduling
@SpringBootApplication
@Import(CoreConfiguration::class)
open class BackgroundApplication : SchedulingConfigurer {
@Bean
internal fun auditorProvider(): StaticAuditorAware {
return StaticAuditorAware()
}
@Autowired
private lateinit var appProperties: ApplicationProperties
//配置bean
@Bean(name = ["keycloakGlobalCmsApi"])
fun keycloakGlobalCmsApiInstance(): Keycloak {
return KeycloakBuilder.builder()
.serverUrl(appProperties.keycloakAuthServerUrl)
.realm(appProperties.keycloakGlobalCmsRealm)
.clientId(appProperties.keycloakGlobalCmsClient)
.username(appProperties.keycloakApiUsername)
.password(appProperties.keycloakApiPassword)
.grantType(OAuth2Constants.PASSWORD)
.resteasyClient(
ResteasyClientBuilder()
.connectionPoolSize(10).build()
).build()
}
override fun configureTasks(taskRegistrar: ScheduledTaskRegistrar) {
val taskScheduler = ThreadPoolTaskScheduler()
taskScheduler.poolSize = 200
taskScheduler.initialize()
taskRegistrar.setTaskScheduler(taskScheduler)
}
}
fun main(args: Array<String>) {
Security.setProperty("crypto.policy", "unlimited")
val app = SpringApplication(BackgroundApplication::class.java)
// app.webApplicationType = WebApplicationType.NONE
val ctx = app.run(*args)
}
background-service
package com.umh.medicalbookingplatform.background.service
import com.umh.medicalbookingplatform.core.error.ErrorCode
import com.umh.medicalbookingplatform.core.exception.ApplicationException
import com.umh.medicalbookingplatform.core.properties.ApplicationProperties
import com.umh.medicalbookingplatform.core.utils.RestTemplateUtils
import com.alibaba.fastjson.JSONObject
import com.umh.medicalbookingplatform.core.model.i18n.I18nString
import com.umh.medicalbookingplatform.core.utils.LocaleUtils
import org.apache.commons.lang3.StringUtils
import org.keycloak.admin.client.Keycloak
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.domain.Pageable
import org.springframework.http.HttpEntity
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpMethod
import org.springframework.http.ResponseEntity
import org.springframework.stereotype.Service
import org.springframework.web.util.UriComponentsBuilder
import java.lang.StringBuilder
import java.util.*
import com.alibaba.fastjson.JSON
import com.umh.medicalbookingplatform.core.model.doctor.*
import com.umh.medicalbookingplatform.core.utils.ApplicationJsonObjectMapper
import org.bson.Document
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.data.mongodb.core.query.Query
/**
* @Description :
* @Author xiaomh
* @create 2021/9/8 14:27
*/
@Service
class ApiCmsInvokeService {
private val dayArray = arrayOf("mon", "tue", "wed", "thu", "fri", "sat", "sun", "gong")
private val logger = LoggerFactory.getLogger(this.javaClass)
@Autowired
private lateinit var keycloakGlobalCmsApi: Keycloak
@Autowired
private lateinit var applicationProperties: ApplicationProperties
@Autowired
private lateinit var mongoTemplate: MongoTemplate
fun getDoctorList(param: DoctorListParam): DoctorListDto {
val p = parseDoctorsRequest(param)
val headers = HttpHeaders()
val listDto = DoctorListDto()
headers.set("Accept", "application/json")
headers.set("version", "v1")
headers.set("x-umhappweb-authorization", keycloakGlobalCmsApi.tokenManager().accessToken.token)
val entity = HttpEntity(null, headers)
val apiCmsInvokeUri = "/v1/api/onlinebookingdoctors_list?channel_id=4"
val response: ResponseEntity<Object>
try {
logger.info("[START] Call globalcms '$apiCmsInvokeUri$p'")
response = RestTemplateUtils.getRestTemplate().exchange(
"${applicationProperties.globalCmsApiEndpoint}$apiCmsInvokeUri$p",
HttpMethod.GET,
entity,
Object::class.java,
)
logger.info("[END] Call globalcms '$apiCmsInvokeUri$p'")
} catch (e: Exception) {
logger.error(e.message, e)
throw ApplicationException(ErrorCode.ERR_GLOBAL_CMS_DOCTOR_LIST_API)
}
val body = response.body ?: return listDto
val doctorList = mutableListOf<DoctorsDto>()
val mongodbIds = mutableListOf<String>()
val jsonBody = JSONObject.toJSONString(body)
val jsonObject = JSONObject.parseObject(jsonBody)
if (jsonObject["result_code"].toString() != "0") {
logger.error("Failed To Call CMS API: 'doctors_list' ")
logger.error("error_code: ${jsonObject["result_code"]}, error_msg: ${jsonObject["result_msg"]}")
throw ApplicationException(ErrorCode.ERR_GLOBAL_CMS_DOCTOR_LIST_API)
}
val resultObject = jsonObject.getJSONObject("result")
val resultArray = resultObject.getJSONArray("result")
val iterator = resultArray.iterator()
val objectMapper = ApplicationJsonObjectMapper()
while (iterator.hasNext()) {
val next = iterator.next() as JSONObject
val doctor = DoctorsDto()
doctor._id = next["therapistid"]?.toString()
doctor.doctorId = next["therapistid"]?.toString()
doctor.personnelId = next["personnel_id"]?.toString()
doctor.version = next["version"]?.toString()
doctor.hospitalFee = (next["hospital_fee"]?.toString())?.toDoubleOrNull()
doctor.medicalTicket = next["medical_ticket"]?.toString() ?: "0"
doctor.doctorCode = next["staffcode"]?.toString() ?: next["username"]?.toString()
val cnName = if (StringUtils.isNotBlank(next["cn_name"]?.toString())) next["cn_name"] else next["name"]
val tcName = if (StringUtils.isNotBlank(next["tc_name"]?.toString())) next["tc_name"] else next["name"]
val enName = if (StringUtils.isNotBlank(next["en_name"]?.toString())) next["en_name"] else next["name"]
doctor.gender = if ("1" == next["sex"]) "M" else "F"
doctor.doctorName.add(I18nString(LocaleUtils.SIMPLIFIED_CHINESE, cnName.toString() ?: ""))
doctor.doctorName.add(I18nString(LocaleUtils.TRADITIONAL_CHINESE, tcName.toString() ?: ""))
doctor.doctorName.add(I18nString(LocaleUtils.ENGLISH, enName.toString() ?: ""))
val languages = next.getJSONArray("language_list")
val languagesIterator = languages.iterator()
while (languagesIterator.hasNext()) {
val languagesNext = languagesIterator.next() as JSONObject
val language = Language()
language.languageId = languagesNext["language_id"]?.toString()
language.language.add(I18nString(LocaleUtils.SIMPLIFIED_CHINESE, languagesNext["cn_name"].toString()))
language.language.add(I18nString(LocaleUtils.TRADITIONAL_CHINESE, languagesNext["tc_name"].toString()))
language.language.add(I18nString(LocaleUtils.ENGLISH, languagesNext["en_name"].toString()))
doctor.languages.add(language)
}
val labelListObj = next.getJSONArray("label_list")
val labelListIterator = labelListObj.iterator()
while (labelListIterator.hasNext()) {
val labelListNext = labelListIterator.next() as JSONObject
val label = Label()
label.labelId = labelListNext["label_id"].toString()
label.label.add(I18nString(LocaleUtils.SIMPLIFIED_CHINESE, labelListNext["cn_name"].toString() ?: ""))
label.label.add(I18nString(LocaleUtils.TRADITIONAL_CHINESE, labelListNext["tc_name"].toString() ?: ""))
label.label.add(I18nString(LocaleUtils.ENGLISH, labelListNext["en_name"].toString() ?: ""))
doctor.labels.add(label)
}
val specialtiesJsonObject = next.getJSONArray("specialty_name_list")
val specialtiesIterator = specialtiesJsonObject.iterator()
while (specialtiesIterator.hasNext()) {
val specialtyNext = specialtiesIterator.next() as JSONObject
val specialtyDto = SpecialtyDto()
specialtyDto.specialtyId = specialtyNext["specialty_id"].toString()
specialtyDto.specialtyDoctor.add(
I18nString(
LocaleUtils.SIMPLIFIED_CHINESE,
parseSyTitle(specialtyNext["doctor_short_cn_name"]?.toString())
)
)
specialtyDto.specialtyDoctor.add(
I18nString(
LocaleUtils.TRADITIONAL_CHINESE,
parseSyTitle(specialtyNext["doctor_short_tc_name"]?.toString())
)
)
specialtyDto.specialtyDoctor.add(
I18nString(
LocaleUtils.ENGLISH,
parseSyTitle(specialtyNext["doctor_short_en_name"]?.toString())
)
)
doctor.specialties.add(specialtyDto)
}
val brandJsonObject = next.getJSONArray("brand_list")
val brandIterator = brandJsonObject.iterator()
while (brandIterator.hasNext()) {
val brandNext = brandIterator.next() as JSONObject
val brandDto = BrandDto()
brandDto.brandId = brandNext["brand_id"].toString()
brandDto.brandName.add(
I18nString(
LocaleUtils.SIMPLIFIED_CHINESE,
parseSyTitle(brandNext["cn_name"]?.toString())
)
)
brandDto.brandName.add(
I18nString(
LocaleUtils.TRADITIONAL_CHINESE,
parseSyTitle(brandNext["tc_name"]?.toString())
)
)
brandDto.brandName.add(
I18nString(
LocaleUtils.ENGLISH,
parseSyTitle(brandNext["en_name"]?.toString())
)
)
doctor.brands.add(brandDto)
}
val regionJsonObject = next["region_list"]
if (regionJsonObject is JSONObject) {
val jsonObj = JSON.parseObject(regionJsonObject.toString())
for ((key, value) in jsonObj) {
val regionList = (regionJsonObject as JSONObject).getJSONArray(key)
val regionListIterator = regionList.iterator()
while (regionListIterator.hasNext()) {
val regionListNext = regionListIterator.next() as JSONObject
val shop = MongoDBShopsDto()
shop.shopId = regionListNext["shopid"]?.toString()
shop.shopCode = regionListNext["shopcode"]?.toString()
shop.regionId = regionListNext["x_id"]?.toString()
shop.locations.add(
I18nString(
LocaleUtils.SIMPLIFIED_CHINESE, regionListNext["atitle"]?.toString() ?: ""
)
)
shop.locations.add(
I18nString(
LocaleUtils.TRADITIONAL_CHINESE,
regionListNext["tc_atitle"]?.toString() ?: ""
)
)
shop.locations.add(
I18nString(
LocaleUtils.ENGLISH,
regionListNext["en_atitle"]?.toString() ?: ""
)
)
shop.clinics.add(
I18nString(
LocaleUtils.SIMPLIFIED_CHINESE,
regionListNext["cn_name"].toString()
)
)
shop.clinics.add(
I18nString(
LocaleUtils.TRADITIONAL_CHINESE,
regionListNext["tc_name"].toString()
)
)
shop.clinics.add(I18nString(LocaleUtils.ENGLISH, regionListNext["en_name"].toString()))
shop.address.add(
I18nString(
LocaleUtils.SIMPLIFIED_CHINESE,
parseAddress(regionListNext["cn_address"]?.toString() ?: "")
)
)
shop.address.add(
I18nString(
LocaleUtils.TRADITIONAL_CHINESE,
parseAddress(regionListNext["tc_address"]?.toString() ?: "")
)
)
shop.address.add(
I18nString(
LocaleUtils.ENGLISH,
parseAddress(regionListNext["en_address"]?.toString() ?: "")
)
)
shop.mail = regionListNext["mail"]?.toString()
shop.phone = parsePhone(regionListNext["phone"]?.toString())
shop.businessDays = parseBusinessDays(regionListNext["business_hours"]?.toString())
doctor.shops.add(shop)
}
}
}
//此处开始看
//把doctorId都存起来,把本次更新数据没有的doctor的清除掉
doctor.doctorId?.let { mongodbIds.add(it) }
//查询集合,有数据则更新,没有数据则添加。注意,创建的时候如果有_id,则_id为MongoDB的ObjectId(主键)
val query = Query()
query.addCriteria(Criteria.where("_id").`is`(doctor.doctorId))
val json = objectMapper.writeValueAsString(doctor)
val doc = Document.parse(json)
if (!mongoTemplate.exists(query,"doctors")){
mongoTemplate.insert(doc,"doctors")
}
mongoTemplate.findAndReplace(query,doc,"doctors")
doctorList.add(doctor)
}
//把本次更新数据没有的doctor的清除掉
//结束
val query = Query()
query.addCriteria(Criteria.where("_id").nin(mongodbIds))
mongoTemplate.remove(query,"doctors")
listDto.content = doctorList
listDto.total = resultObject["count"]?.toString()?.toInt()
return listDto
}
private fun parseBusinessDays(businessHoursStr: String?): MutableList<BusinessHourDto> {
val jsonObject = JSONObject.parseObject(businessHoursStr)
return parseBusinessHours(jsonObject)
}
private fun parseBusinessHours(jsonObject: JSONObject?): MutableList<BusinessHourDto> {
val businessHours = mutableListOf<BusinessHourDto>()
for (i in dayArray.indices) {
val hoursJson = jsonObject?.getJSONArray(dayArray[i])
val iterator = hoursJson?.iterator() ?: mutableListOf<BusinessHourDto>().iterator()
val hourDto = BusinessHourDto()
while (iterator.hasNext()) {
val next = iterator.next() as JSONObject
hourDto.periods!!.add(
BusinessHourPeriodDto().apply {
this.startTime = next["starttime"]?.toString() ?: ""
this.endTime = next["endtime"]?.toString() ?: ""
}
)
}
if (dayArray[i] == "gong") {
hourDto.day = "holiday"
} else {
hourDto.day = dayArray[i]
}
businessHours.add(hourDto)
}
return businessHours
}
fun parseSyTitle(syTitle: String?): String {
val joiner = StringJoiner(",")
syTitle?.split(",")?.forEach {
if (StringUtils.isNotBlank(it)) {
joiner.add(it)
}
}
return joiner.toString()
}
fun parsePersonnel(location: String): String {
val builder = StringBuilder()
val split = location.split("\n")
split.forEach {
if (!builder.contains(it)) {
builder.append(it).append(", ")
}
}
if (builder.isNotEmpty()) {
builder.deleteCharAt(builder.length - 2)
}
return builder.toString()
}
fun parseAddress(location: String): String {
val builder = StringBuilder()
val split = location.split("\"],[\"")
split.forEach {
if (!builder.contains(it)) {
builder.append(it).append(", ")
}
}
if (builder.isNotEmpty()) {
builder.delete(builder.length - 4, builder.length)
builder.delete(0, 2)
}
val builderSecond = StringBuilder()
val splitSecond = builder.split("\",\"")
splitSecond.forEach {
if (!builderSecond.contains(it)) {
builderSecond.append(it)
}
}
return builderSecond.toString()
}
fun parsePhone(phoneStr: String?): MutableList<Phone> {
val jsonBody = JSONObject.toJSONString(phoneStr)
val jsonObject = JSONObject.parseObject("{\"phone\": " + jsonBody + "}")
val phoneList = mutableListOf<Phone>()
val phones = jsonObject.getJSONArray("phone")
val iteratorPhone = phones?.iterator()
if (iteratorPhone != null) {
while (iteratorPhone.hasNext()) {
val next = iteratorPhone.next() as JSONObject
val phone = Phone()
phone.mobileNoAreaCode = next["phonearea"]?.toString()
phone.mobileNo = next["phoneval"]?.toString()
phoneList.add(phone)
}
}
return phoneList
}
private fun parseDoctorsRequest(param: DoctorListParam): String {
val searchKey = param.searchKey?.let { "&search_key=${it}" } ?: ""
val shopId = param.shopId?.let { "&shop_id=${it}" } ?: ""
val brandId = param.brandId?.let { "&brand_id=${it}" } ?: ""
val specialtyId = param.specialtyId?.let { "&specialty_id=${it}" } ?: ""
val region = param.regionId?.let { "®ion=${it}" } ?: ""
val startTime = param.startTime?.let { "&start_time=${it}" } ?: ""
val endTime = param.endTime?.let { "&end_time=${it}" } ?: ""
val medicalTicket = param.medicalTicket?.let { "&medical_ticket=${it}" } ?: ""
val startHospitalFee = param.startHospitalFee?.let { "&start_hospital_fee=${it}" } ?: ""
val endHospitalFee = param.endHospitalFee?.let { "&end_hospital_fee=${it}" } ?: ""
val languageId = param.languageId?.let { "&language_id=${it}" } ?: ""
val gender = param.gender?.let {
"&sex=${it}"
.replace("%2C", ",")
.replace("M", "1")
.replace("F", "2")
} ?: ""
val orderby = param.orderby?.let { "&orderby=${it}" } ?: ""
val page = param.page.let { "&page=${it?.plus(1) ?: 1}" }
val pageSize = param.pageSize.let { "&page_size=${it ?: 0}" }
return "$searchKey$shopId$brandId$specialtyId$region$startTime$endTime$medicalTicket$startHospitalFee$endHospitalFee$languageId$gender$orderby$page$pageSize"
}
}
时间数据处理
class BusinessHourDto {
@Schema(description = "星期几")
var day = ""
@Schema(description = "时段")
var periods: MutableList<BusinessHourPeriodDto>? = mutableListOf()
}
class BusinessHourPeriodDto {
@Schema(description = "时段 start")
var startTime = ""
@Schema(description = "时段 end")
var endTime = ""
}
background-controller
package com.umh.medicalbookingplatform.background.task.globalcmssynchronization
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.umh.medicalbookingplatform.background.service.ApiCmsInvokeService
import com.umh.medicalbookingplatform.background.task.BackgroundTaskExecutor
import com.umh.medicalbookingplatform.core.error.ErrorCode
import com.umh.medicalbookingplatform.core.exception.ApplicationException
import com.umh.medicalbookingplatform.core.model.background.BackgroundTask
import com.umh.medicalbookingplatform.core.model.doctor.*
import com.umh.medicalbookingplatform.core.model.i18n.I18nString
import com.umh.medicalbookingplatform.core.properties.ApplicationProperties
import com.umh.medicalbookingplatform.core.utils.ApplicationJsonObjectMapper
import com.umh.medicalbookingplatform.core.utils.LocaleUtils
import com.umh.medicalbookingplatform.core.utils.RestTemplateUtils
import org.apache.commons.lang3.StringUtils
import org.bson.Document
import org.keycloak.admin.client.Keycloak
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.data.mongodb.core.query.Query
import org.springframework.http.HttpEntity
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpMethod
import org.springframework.http.ResponseEntity
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional
import javax.persistence.EntityManager
import javax.persistence.PersistenceContext
@Component
class GlobalCmsSynchronizationTask : BackgroundTaskExecutor() {
private val logger = LoggerFactory.getLogger(this.javaClass)
@PersistenceContext
private lateinit var em: EntityManager
@Autowired
private lateinit var apiCmsInvokeService: ApiCmsInvokeService
@Scheduled(fixedRate = 1000 * 1)
@Transactional(rollbackFor = [Exception::class])
fun startTask() {
super.startTask(BackgroundTask.BackgroundTaskCode.GLOBAL_CMS_SYNCHRONIZATION_TASK)
}
override fun run() {
val param = DoctorListParam()
apiCmsInvokeService.getDoctorList(param)
}
}
api模块-service(如果输入特殊字符,tomcat会拦截并报错,此时需要后端转义!!!!)
package com.umh.medicalbookingplatform.api.service
import com.umh.medicalbookingplatform.api.model.*
import com.umh.medicalbookingplatform.core.model.doctor.DoctorListDto
import com.umh.medicalbookingplatform.core.model.doctor.DoctorListParam
import com.umh.medicalbookingplatform.core.model.doctor.DoctorsDto
import com.umh.medicalbookingplatform.core.model.doctor.Language
import io.swagger.v3.oas.annotations.Operation
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.domain.Pageable
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.data.mongodb.core.query.Query
import org.springframework.stereotype.Service
import java.util.regex.Pattern
/**
* @Description :
* @Author xiaomh
* @create 2021/9/8 14:22
*/
@Service
class ApiDoctorService {
@Autowired
private lateinit var apiCmsInvokeService: ApiCmsInvokeService
@Autowired
private lateinit var apiBookingInvokeService: ApiBookingInvokeService
@Autowired
private lateinit var mongoTemplate: MongoTemplate
fun getDoctorListToMongoDB(param: DoctorListParam): DoctorListDto {
val query = Query()
val dto = DoctorListDto()
/* 关键字 */
if (param.searchKey?.isNotEmpty() == true){
//val pattern=Pattern.compile("^.*${param.searchKey}.*$", Pattern.CASE_INSENSITIVE)//此处代码是有问题的,当有特殊字符如[等,会报错,需进行转换!!!
val pattern=Pattern.compile("^.*${Pattern.quote(param.searchKey)}.*$", Pattern.CASE_INSENSITIVE)
query.addCriteria(Criteria.where("doctorName.value").regex(pattern))
}
/* 商店id */
if (param.shopId?.isNotEmpty() == true){
query.addCriteria(Criteria.where("shops.shopId").`is`(param.shopId))
}
/* 品牌id */
if (param.brandId?.isNotEmpty() == true){
query.addCriteria(Criteria.where("brands.brandId").`is`(param.brandId))
}
/* 专科id */
if (param.specialtyId?.isNotEmpty() == true) {
val ids = param.specialtyId!!.replace("%2C",",")
val split = ids.split(",")
query.addCriteria(Criteria.where("specialties.specialtyId").`in`(split))
}
/* 地区id */
if (param.regionId?.isNotEmpty() == true) {
val ids = param.regionId!!.replace("%2C",",")
val split = ids.split(",")
query.addCriteria(Criteria.where("shops.regionId").`in`(split))
}
/* 医疗劵id */
if (param.medicalTicket?.isNotEmpty() == true) {
query.addCriteria(Criteria.where("medicalTicket").`is`(param.medicalTicket))
}
/* 起始诊金id */
if (param.startHospitalFee?.isNotEmpty() == true) {
query.addCriteria(Criteria.where("hospitalFee").gt(param.startHospitalFee!!.toInt()))
}
/* 语言id */
if (param.languageId?.isNotEmpty() == true) {
val ids = param.languageId!!.replace("%2C",",")
val split = ids.split(",")
query.addCriteria(Criteria.where("languages.languageId").`in`(split))
}
/* 医生性别 */
if (param.gender?.isNotEmpty() == true) {
val ids = param.gender!!.replace("%2C",",")
val split = ids.split(",")
query.addCriteria(Criteria.where("gender").`in`(split));
}
//此处一定要先查总数再放置分页条件
val count = mongoTemplate.count(query ,DoctorsDto::class.java, "doctors")
/* 分页 */
if (param.page != null && param.pageSize != null) {
query.skip((param.page!!) * (param.pageSize!!))
query.limit(param.pageSize!!)
}
query.with(Sort.by(Sort.Order.desc("doctorName.value")))
val list = mongoTemplate.find(query , DoctorsDto::class.java, "doctors")
dto.content = list
dto.total = count.toInt()
return dto
}
}
Criteria criteria = new Criteria();
criteria.andOperator(Criteria.where("create_time").gte(beginDate),Criteria.where("create_time").lte(endDate));
query.addCriteria(criteria);
>=和<=的查询写法
在自测的情况下,输入[]等特殊字符串还是会报错,但如果把特殊字符“[”变成同等意思的%5B,就可以实现转义
字段查询:db.getCollection("doctors").find({"doctorId":"32"})
注意!!如果浮点数为字符串,使用大于小于等查询会不准确
数组查询:db.getCollection("doctors").find({"specialties.specialtyId":"32"})
非常谢谢“Felix”哥(客户)提供的测试demo
@RequestMapping(value = ["/test"], method = [RequestMethod.GET])
@Throws(Exception::class)
fun test(page : Long,size : Int): Any {
val objectMapper = ApplicationJsonObjectMapper()
/*
插入數據
*/
// val obj = mapOf(
// "test" to "test",
// "inner_test" to mapOf(
// "value" to "123456"
// )
// )
// val json = objectMapper.writeValueAsString(obj)
// val doc = Document.parse(json)
// mongoTemplate.insert(doc, "doctors")
/*
搜索
*/
// val query = Query()
// query.addCriteria(Criteria.where("inner_test.value").regex(".*345.*", "i"));
// val list = mongoTemplate.find(query, Object::class.java, "doctors")
var name = "刘"
val query = Query()
// val ids = arrayListOf<String>()
// ids.add(id)
val pattern= Pattern.compile("^.*${name}.*$", Pattern.CASE_INSENSITIVE)
query.addCriteria(Criteria.where("doctorName.value").regex(pattern))
val list = mongoTemplate.find(query ,Object::class.java, "doctors")
return list
}