fix: align parking plate management and paid benefits
| | |
| | | </view> |
| | | |
| | | <view v-if="!merchantError" class="tabs"> |
| | | <view class="tab-item" :class="currentTab == 'primary' ? 'active' : ''" @tap="changeTab('primary')"><text>{{isMedicalMerchant ? '预约' : (showUsageInstructions ? '权益内容' : '消费购买')}}</text><view class="tab-line"></view></view> |
| | | <view class="tab-item" :class="currentTab == 'primary' ? 'active' : ''" @tap="changeTab('primary')"><text>{{isMedicalMerchant ? '预约' : (isParkingMerchant && parkingBenefitText == '付费' ? '使用说明' : (showUsageInstructions ? '权益内容' : '消费购买'))}}</text><view class="tab-line"></view></view> |
| | | <view class="tab-item" :class="currentTab == 'rights' ? 'active' : ''" @tap="changeTab('rights')"><text>{{isParkingMerchant ? '停车场地址' : (isTransportMerchant ? '适用门店' : '权益详情')}}</text><view class="tab-line"></view></view> |
| | | </view> |
| | | |
| | |
| | | const listLoaded = ref(false) |
| | | const listError = ref(false) |
| | | // 餐饮商家没有上架商品时,改为展示当前人才卡等级可用的到店消费说明。 |
| | | const showUsageInstructions = computed(() => !isMedicalMerchant.value && (!groupBuyingEnabled.value || (merchantType.value == 'food' && listLoaded.value && visiblePackages.value.length == 0))) |
| | | const showUsageInstructions = computed(() => !isMedicalMerchant.value && ((isParkingMerchant.value && parkingBenefitText.value == '付费') || !groupBuyingEnabled.value || (merchantType.value == 'food' && listLoaded.value && visiblePackages.value.length == 0))) |
| | | const failOnce = ref(false) |
| | | const forceEmpty = ref(false) |
| | | let parkingDetailLoaded = false |
| | |
| | | ? parkingRightDetail |
| | | : (data != null && data.rightsText != null && data.rightsText != '' ? data.rightsText as string : '请咨询商户') |
| | | merchantRightsText.value = data != null && data.rightsText != null ? data.rightsText as string : '请咨询商户' |
| | | parkingActionMode.value = data != null && data.actionMode == 'talentCode' ? 'talentCode' : 'plate' |
| | | parkingActionMode.value = data != null && (data.actionMode == 'talentCode' || data.parkingType == 'paid') ? 'talentCode' : 'plate' |
| | | parkingPlateMode.value = data != null && data.plateFormMode == 'withParking' ? 'withParking' : 'simple' |
| | | parkingBenefitId.value = data != null && data.benefit != null && data.benefit.benefitId != null ? '' + data.benefit.benefitId : '' |
| | | groupBuyingEnabled.value = false |
| | |
| | | discount: '', |
| | | governmentSubsidy: false, |
| | | consumeDesc: '', |
| | | consumeEnabled: false, |
| | | consumeEnabled: true, |
| | | parkingType: 'paid', |
| | | }, |
| | | { |
| | |
| | | discount: '', |
| | | governmentSubsidy: false, |
| | | consumeDesc: '', |
| | | consumeEnabled: false, |
| | | consumeEnabled: true, |
| | | parkingType: 'paid', |
| | | }, |
| | | ] |
| | |
| | | function serializeMerchantRightsConfig() { |
| | | return rightsConfigs.map((item, index) => { |
| | | const payload = { ...item } |
| | | if (!supportsStoreConsumption.value) { |
| | | const supportsItemStoreConsumption = supportsStoreConsumption.value |
| | | || (isParkingMerchant.value && item.parkingType === 'paid') |
| | | if (!supportsItemStoreConsumption) { |
| | | payload.storeConsume = false |
| | | payload.discount = '' |
| | | payload.governmentSubsidy = false |
| | |
| | | </div> |
| | | </div> |
| | | |
| | | <template v-if="supportsStoreConsumption && item.consumeEnabled"> |
| | | <template v-if="(supportsStoreConsumption && item.consumeEnabled) || (isParkingMerchant && item.parkingType === 'paid')"> |
| | | <template v-if="isHotelMerchant"> |
| | | <div class="merchant-consume-row hotel-voucher-row"> |
| | | <strong>免费券</strong> |
| | |
| | | </div> |
| | | </template> |
| | | |
| | | <div v-if="isParkingMerchant" class="merchant-rights-detail-row consume-desc-row"> |
| | | <div v-if="isParkingMerchant && item.parkingType === 'paid'" class="merchant-rights-detail-row consume-desc-row"> |
| | | <label>到店消费使用描述</label> |
| | | <el-input v-model="item.consumeDesc" type="textarea" :rows="4" resize="none" placeholder="请输入" /> |
| | | </div> |
| | |
| | | <script setup lang="ts"> |
| | | import { computed, onMounted, ref } from 'vue' |
| | | import { useRoute, useRouter } from 'vue-router' |
| | | import { ElMessage } from 'element-plus' |
| | | import { getParkingPlateOperationLogs, getParkingPlateSubmissionDetail, updateParkingPlateSubmissionStatus } from '../api/admin' |
| | | import { useRoute } from 'vue-router' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { getParkingPlateSubmissionDetail, updateParkingPlateSubmissionStatus } from '../api/admin' |
| | | |
| | | const route = useRoute() |
| | | const router = useRouter() |
| | | const loading = ref(false) |
| | | const processing = ref(false) |
| | | const detail = ref<Record<string, any>>({}) |
| | | const logs = ref<Record<string, any>[]>([]) |
| | | |
| | | function text(value: unknown) { return value === undefined || value === null || value === '' ? '-' : String(value) } |
| | | function field(...keys: string[]) { const groups = [detail.value, detail.value.talentInfo || {}, detail.value.submissionInfo || {}, detail.value.statusInfo || {}]; for (const group of groups) { for (const key of keys) { if (group[key] !== undefined && group[key] !== null && group[key] !== '') return group[key] } } return '' } |
| | | function formatDate(value: unknown) { return text(value).replace('T', ' ') } |
| | | const overview = computed(() => [ |
| | | ['提交单号', field('submissionNo', 'applicationNo')], ['持卡人姓名', field('talentName')], ['手机号', field('talentMobile')], ['人才等级', field('talentLevel', 'cardLevel')], |
| | | ['车牌号', field('plateNumber', 'plateNo')], ['停车场', field('parkingLotName', 'parkingLot', 'serviceName')], ['提交类型', field('submissionType')], ['提交时间', formatDate(field('submittedAt', 'createdAt'))], |
| | | ['状态', field('status')], ['处理时间', formatDate(field('handledAt', 'processedAt'))], ['处理人', field('handlerName', 'processedBy')], |
| | | function text(value: unknown) { |
| | | return value === undefined || value === null || value === '' ? '-' : String(value) |
| | | } |
| | | |
| | | function pick(group: Record<string, any> | undefined, ...keys: string[]) { |
| | | if (!group) return '' |
| | | for (const key of keys) { |
| | | if (group[key] !== undefined && group[key] !== null && group[key] !== '') return group[key] |
| | | } |
| | | return '' |
| | | } |
| | | |
| | | function formatDate(value: unknown) { |
| | | return text(value).replace('T', ' ') |
| | | } |
| | | |
| | | function statusName(value: unknown, fallback = '') { |
| | | const raw = String(value ?? '').trim().toUpperCase().replace('-', '_') |
| | | const label = fallback.trim() |
| | | if (label === '已拒绝') return '已驳回' |
| | | if (label) return label |
| | | if (raw === 'ACTIVE' || raw === 'PROCESSED' || raw === 'PASSED' || raw === 'APPROVED') return '已处理' |
| | | if (raw === 'REJECTED' || raw === 'REFUSED' || raw === 'DENIED') return '已驳回' |
| | | if (raw === 'CANCELLED' || raw === 'CANCELED') return '已取消' |
| | | return '待处理' |
| | | } |
| | | |
| | | const talentInfo = computed(() => detail.value.talentInfo || {}) |
| | | const submissionInfo = computed(() => detail.value.submissionInfo || {}) |
| | | const statusInfo = computed(() => detail.value.statusInfo || {}) |
| | | |
| | | const cardholderItems = computed(() => [ |
| | | { label: '持卡人姓名:', value: text(pick(talentInfo.value, 'talentName') || pick(detail.value, 'talentName')) }, |
| | | { label: '手机号:', value: text(pick(talentInfo.value, 'talentMobile') || pick(detail.value, 'talentMobile')) }, |
| | | { label: '人才等级:', value: text(pick(talentInfo.value, 'cardLevel') || pick(detail.value, 'cardLevel', 'talentLevel')) }, |
| | | ]) |
| | | async function loadData() { loading.value = true; try { const id = String(route.params.id); const [info, operationLogs] = await Promise.all([getParkingPlateSubmissionDetail(id), getParkingPlateOperationLogs(id)]); detail.value = info || {}; logs.value = operationLogs || [] } finally { loading.value = false } } |
| | | async function mark(status: string) { const id = detail.value.id || detail.value.registrationId; if (!id || processing.value) return; processing.value = true; try { detail.value = await updateParkingPlateSubmissionStatus(id, status); logs.value = await getParkingPlateOperationLogs(String(id)); ElMessage.success(status === '已驳回' ? '已驳回该车牌申请' : '已处理该车牌申请') } finally { processing.value = false } } |
| | | |
| | | const submissionItems = computed(() => [ |
| | | { label: '车牌号:', value: text(pick(submissionInfo.value, 'plateNumber', 'plateNo') || pick(detail.value, 'plateNumber', 'plateNo')) }, |
| | | { label: '停车场:', value: text(pick(submissionInfo.value, 'parkingLotName', 'parkingLot', 'serviceName') || pick(detail.value, 'parkingLotName', 'parkingLot', 'serviceName')) }, |
| | | { label: '提交时间:', value: formatDate(pick(submissionInfo.value, 'submittedAt', 'createdAt') || pick(detail.value, 'submittedAt', 'createdAt')) }, |
| | | ]) |
| | | |
| | | const currentStatus = computed(() => statusName( |
| | | pick(statusInfo.value, 'status') || pick(detail.value, 'status'), |
| | | String(pick(statusInfo.value, 'statusName') || pick(detail.value, 'statusName') || ''), |
| | | )) |
| | | |
| | | const statusItems = computed(() => [ |
| | | { label: '状态:', value: currentStatus.value }, |
| | | { label: '处理时间:', value: formatDate(pick(statusInfo.value, 'handledAt', 'processedAt') || pick(detail.value, 'handledAt', 'processedAt')) }, |
| | | { label: '处理人:', value: text(pick(statusInfo.value, 'handledByName', 'handlerName', 'processedBy') || pick(detail.value, 'handledByName', 'handlerName', 'processedBy')) }, |
| | | ]) |
| | | |
| | | const canProcess = computed(() => !['已处理', '已驳回', '已取消'].includes(currentStatus.value)) |
| | | |
| | | async function loadData() { |
| | | loading.value = true |
| | | try { |
| | | detail.value = await getParkingPlateSubmissionDetail(String(route.params.id)) || {} |
| | | } finally { |
| | | loading.value = false |
| | | } |
| | | } |
| | | |
| | | async function markHandled() { |
| | | const id = detail.value.id || detail.value.registrationId |
| | | if (!id || processing.value || !canProcess.value) return |
| | | try { |
| | | await ElMessageBox.confirm('确认将该停车车牌申请标记为已处理吗?', '确认处理', { |
| | | type: 'warning', |
| | | confirmButtonText: '确认处理', |
| | | cancelButtonText: '取消', |
| | | }) |
| | | } catch { |
| | | return |
| | | } |
| | | processing.value = true |
| | | try { |
| | | detail.value = await updateParkingPlateSubmissionStatus(id, '已处理') |
| | | ElMessage.success('已处理该车牌申请') |
| | | } finally { |
| | | processing.value = false |
| | | } |
| | | } |
| | | |
| | | onMounted(loadData) |
| | | </script> |
| | | |
| | | <template> |
| | | <div v-loading="loading" class="page-stack parking-plate-detail"> |
| | | <div class="toolbar"><div><h2>停车车牌详情</h2><p>查看人才提交的车牌信息,并在详情页完成处理。</p></div><el-button @click="router.back()">返回</el-button></div> |
| | | <section class="panel detail-panel"> |
| | | <h3>持卡人信息</h3><div class="info-grid"><div v-for="[label, value] in overview.slice(0, 4)" :key="label" class="info-item"><span>{{ label }}</span><strong>{{ value }}</strong></div></div> |
| | | <h3>提交信息</h3><div class="info-grid"><div v-for="[label, value] in overview.slice(4, 8)" :key="label" class="info-item"><span>{{ label }}</span><strong>{{ value }}</strong></div></div> |
| | | <h3>状态信息</h3><div class="info-grid"><div v-for="[label, value] in overview.slice(8)" :key="label" class="info-item"><span>{{ label }}</span><strong>{{ value }}</strong></div></div> |
| | | <div class="detail-actions"><el-button type="primary" :loading="processing" :disabled="field('status') === '已处理'" @click="mark('已处理')">已处理</el-button><el-button type="danger" :loading="processing" :disabled="field('status') === '已驳回'" @click="mark('已驳回')">驳回</el-button></div> |
| | | <div v-loading="loading" class="parking-plate-detail-page"> |
| | | <section class="parking-plate-detail-panel"> |
| | | <div class="reservation-detail-section"> |
| | | <h3>持卡人信息</h3> |
| | | <div class="reservation-detail-grid parking-plate-cardholder-grid"> |
| | | <div v-for="item in cardholderItems" :key="item.label" class="reservation-detail-item"> |
| | | <span>{{ item.label }}</span><strong>{{ item.value }}</strong> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="reservation-detail-section"> |
| | | <h3>提交信息</h3> |
| | | <div class="reservation-detail-grid parking-plate-submit-grid"> |
| | | <div v-for="item in submissionItems" :key="item.label" class="reservation-detail-item"> |
| | | <span>{{ item.label }}</span><strong>{{ item.value }}</strong> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="reservation-detail-section"> |
| | | <h3>状态信息</h3> |
| | | <div class="reservation-detail-grid parking-plate-status-grid"> |
| | | <div v-for="item in statusItems" :key="item.label" class="reservation-detail-item"> |
| | | <span>{{ item.label }}</span><strong>{{ item.value }}</strong> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </section> |
| | | <section class="panel"><div class="section-heading"><h3>操作记录</h3></div><el-table :data="logs" border empty-text="暂无操作记录"><el-table-column prop="operationType" label="操作类型" min-width="120" /><el-table-column prop="operatorName" label="操作人" min-width="120" /><el-table-column prop="beforeStatus" label="处理前状态" min-width="110" /><el-table-column prop="afterStatus" label="处理后状态" min-width="110" /><el-table-column prop="remark" label="备注" min-width="180" show-overflow-tooltip /><el-table-column label="操作时间" min-width="170"><template #default="{ row }">{{ formatDate(row.createdAt) }}</template></el-table-column></el-table></section> |
| | | |
| | | <el-button v-if="canProcess" type="primary" class="reservation-detail-action" :loading="processing" @click="markHandled">已处理</el-button> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | .detail-panel { padding: 24px 28px 28px; }.detail-panel h3 { margin: 0 0 14px; color: #1f2937; font-size: 16px; }.detail-panel h3:not(:first-child) { margin-top: 28px; }.info-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px; overflow: hidden; border: 1px solid #e7ebf0; background: #e7ebf0; }.info-item { display: flex; min-height: 66px; padding: 14px 16px; box-sizing: border-box; background: #f8fafc; flex-direction: column; justify-content: center; gap: 7px; }.info-item span { color: #7b8794; font-size: 13px; }.info-item strong { color: #263445; font-size: 14px; font-weight: 500; word-break: break-all; }.detail-actions { display: flex; margin-top: 28px; gap: 12px; }.section-heading { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }.section-heading h3 { margin: 0; color: #1f2937; font-size: 16px; }@media (max-width: 1000px) { .info-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } } |
| | | .parking-plate-detail-page { |
| | | display: grid; |
| | | gap: 20px; |
| | | } |
| | | |
| | | .parking-plate-detail-panel { |
| | | min-height: 0; |
| | | padding: 20px 18px 26px; |
| | | background: #ffffff; |
| | | } |
| | | |
| | | .parking-plate-detail-panel .reservation-detail-section { |
| | | margin-bottom: 24px; |
| | | } |
| | | |
| | | .parking-plate-detail-panel .reservation-detail-section:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .parking-plate-detail-panel .reservation-detail-grid { |
| | | grid-template-columns: repeat(3, minmax(0, 1fr)); |
| | | min-height: 74px; |
| | | padding: 22px 16px; |
| | | background: #f1f3f5; |
| | | } |
| | | |
| | | .parking-plate-detail-panel .parking-plate-submit-grid { |
| | | min-height: 92px; |
| | | align-content: start; |
| | | } |
| | | |
| | | @media (max-width: 900px) { |
| | | .parking-plate-detail-panel .reservation-detail-grid { |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | } |
| | | } |
| | | </style> |
| | |
| | | const statusOptions = ['待处理', '已处理', '已驳回', '已提交商家', '已录入'] |
| | | const submissionTypes = ['新增', '修改'] |
| | | function text(value: unknown) { return value === undefined || value === null || value === '' ? '-' : String(value) } |
| | | function formatDate(value: unknown) { return text(value).replace('T', ' ') } |
| | | function statusText(value: unknown) { |
| | | const label = text(value) |
| | | return label === '已拒绝' ? '已驳回' : label |
| | | } |
| | | function queryParams(size = pagination.size) { return { page: pagination.page, size, keyword: filters.keyword || undefined, serviceKeyword: filters.serviceKeyword || undefined, plateNumber: filters.plateNumber || undefined, cardLevel: filters.cardLevel || undefined, status: filters.status || undefined, submissionType: filters.submissionType || undefined, submittedStart: filters.submittedTime?.[0], submittedEnd: filters.submittedTime?.[1] } } |
| | | async function loadData() { loading.value = true; try { const result = await getParkingPlateSubmissions(queryParams()); rows.value = result.records || []; pagination.total = result.total || 0 } finally { loading.value = false } } |
| | | function search() { pagination.page = 1; loadData() } |
| | | function resetFilters() { Object.assign(filters, { keyword: '', serviceKeyword: '', plateNumber: '', cardLevel: '', status: '', submissionType: '', submittedTime: [] }); search() } |
| | | function viewDetail(row: Record<string, any>) { router.push(`/parking-plates/${row.id}`) } |
| | | async function download() { const result = await getParkingPlateSubmissions({ ...queryParams(1000), page: 1 }); downloadExcel('停车车牌管理', [{ title: '提交单号', value: row => text(row.submissionNo || row.applicationNo) }, { title: '持卡人', value: row => text(row.talentName) }, { title: '手机号', value: row => text(row.talentMobile) }, { title: '车牌号', value: row => text(row.plateNumber || row.plateNo) }, { title: '停车场', value: row => text(row.parkingLotName || row.parkingLot || row.serviceName) }, { title: '提交类型', value: row => text(row.submissionType) }, { title: '提交时间', value: row => text(row.submittedAt || row.createdAt) }, { title: '状态', value: row => text(row.status) }], result.records || []) } |
| | | async function download() { const result = await getParkingPlateSubmissions({ ...queryParams(1000), page: 1 }); downloadExcel('停车车牌管理', [{ title: '提交单号', value: row => text(row.submissionNo || row.applicationNo) }, { title: '持卡人', value: row => text(row.talentName) }, { title: '手机号', value: row => text(row.talentMobile) }, { title: '车牌号', value: row => text(row.plateNumber || row.plateNo) }, { title: '停车场', value: row => text(row.parkingLotName || row.parkingLot || row.serviceName) }, { title: '提交类型', value: row => text(row.submissionType) }, { title: '提交时间', value: row => formatDate(row.submittedAt || row.createdAt) }, { title: '状态', value: row => statusText(row.statusName || row.status) }], result.records || []) } |
| | | onMounted(loadData) |
| | | </script> |
| | | <template> |
| | |
| | | <el-table-column label="车牌号" min-width="120"><template #default="{ row }">{{ text(row.plateNumber || row.plateNo) }}</template></el-table-column> |
| | | <el-table-column label="停车场" min-width="160" show-overflow-tooltip><template #default="{ row }">{{ text(row.parkingLotName || row.parkingLot || row.serviceName) }}</template></el-table-column> |
| | | <el-table-column prop="submissionType" label="提交类型" min-width="100" /> |
| | | <el-table-column label="提交时间" min-width="170"><template #default="{ row }">{{ text(row.submittedAt || row.createdAt) }}</template></el-table-column> |
| | | <el-table-column prop="status" label="状态" min-width="110" /> |
| | | <el-table-column label="提交时间" min-width="170"><template #default="{ row }">{{ formatDate(row.submittedAt || row.createdAt) }}</template></el-table-column> |
| | | <el-table-column label="状态" min-width="110"><template #default="{ row }">{{ statusText(row.statusName || row.status) }}</template></el-table-column> |
| | | <el-table-column label="操作" width="100" fixed="right"><template #default="{ row }"><el-button link type="primary" @click="viewDetail(row)">查看</el-button></template></el-table-column> |
| | | </el-table> |
| | | <div class="pagination-row"><el-pagination v-model:current-page="pagination.page" v-model:page-size="pagination.size" background layout="total, prev, pager, next" :total="pagination.total" @change="loadData" /></div> |
| | |
| | | Map<String, Object> benefit = parkingBenefit(merchantId); |
| | | String cardLevel = talentCardId == null ? "" : talentCardLevel(talentCardId); |
| | | List<Map<String, Object>> rights = parseList(merchant.get("merchantRightsConfig")); |
| | | Map<String, Object> right = matchingRight(rights, cardLevel); |
| | | boolean hasCardRightsConfig = !rights.isEmpty() && !cardLevel.isBlank(); |
| | | Map<String, Object> legacyRight = matchingRight(rights, cardLevel); |
| | | Map<String, Object> configuredRight = parkingCardLevelRight(merchantId, cardLevel); |
| | | Map<String, Object> right = mergeRight(legacyRight, configuredRight); |
| | | boolean hasCardRightsConfig = (!rights.isEmpty() || configuredRight != null) && !cardLevel.isBlank(); |
| | | boolean available = number(merchant.get("status")) == 1 |
| | | && (!hasCardRightsConfig || right != null) |
| | | && (right == null || booleanValue(right.get("enabled"))); |
| | | && (right == null || (booleanValue(right.get("enabled")) && rightStatusEnabled(right))); |
| | | |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("merchantId", merchant.get("id")); |
| | |
| | | result.put("serviceLabel", text(merchant.get("serviceLabel"), "停车服务")); |
| | | result.put("benefitText", benefitText(merchant, right, benefit)); |
| | | result.put("applicableCards", applicableCards(right, benefit)); |
| | | result.put("usageInstructions", text(merchant.get("usageInstructions"), "请咨询商户")); |
| | | result.put("usageInstructions", usageInstructions(merchant, right)); |
| | | result.put("rightsText", rightsText(merchant, right)); |
| | | String parkingType = text(right == null ? null : right.get("parkingType"), "paid"); |
| | | result.put("actionMode", "free".equalsIgnoreCase(parkingType) ? "talentCode" : "plate"); |
| | | // 付费停车权益按到店消费规则使用,前端展示使用说明并出示人才码。 |
| | | result.put("actionMode", "paid".equalsIgnoreCase(parkingType) || "free".equalsIgnoreCase(parkingType) ? "talentCode" : "plate"); |
| | | result.put("plateFormMode", text(benefit.get("plateFormMode"), "simple")); |
| | | result.put("benefit", benefitView(benefit)); |
| | | result.put("applicableStores", parkingStores(merchant.get("usageInstructions"))); |
| | |
| | | return rows.isEmpty() ? new LinkedHashMap<>() : rows.get(0); |
| | | } |
| | | |
| | | private Map<String, Object> parkingCardLevelRight(Long merchantId, String cardLevel) { |
| | | if (cardLevel == null || cardLevel.isBlank()) return null; |
| | | List<Map<String, Object>> rows = query(""" |
| | | select card_level as level, enabled, label as tag, benefit_detail as detail, |
| | | charge_type as chargeType, selection_mode as selectionMode, |
| | | store_consume_enabled as storeConsumeEnabled, discount_text as discountText, |
| | | government_subsidy as governmentSubsidy, consume_usage_text as consumeDesc, |
| | | status as rightStatus |
| | | from transport_parking_card_level_right |
| | | where merchant_id = ? and card_level = ? and deleted = 0 |
| | | limit 1 |
| | | """, merchantId, cardLevel); |
| | | if (rows.isEmpty()) return null; |
| | | Map<String, Object> right = rows.get(0); |
| | | String chargeType = text(right.get("chargeType"), "FREE"); |
| | | right.put("parkingType", "PAID".equalsIgnoreCase(chargeType) ? "paid" : "free"); |
| | | String selectionMode = text(right.get("selectionMode"), "ALL"); |
| | | right.put("parkingScope", "ONE".equalsIgnoreCase(selectionMode) ? "selected" : "all"); |
| | | // 付费权益的到店消费使用描述就是详情页的使用说明。 |
| | | if ("paid".equals(right.get("parkingType")) && text(right.get("consumeDesc"), "").isBlank()) { |
| | | right.put("consumeDesc", text(right.get("detail"), "")); |
| | | } |
| | | return right; |
| | | } |
| | | |
| | | private Map<String, Object> mergeRight(Map<String, Object> legacyRight, Map<String, Object> configuredRight) { |
| | | if (legacyRight == null && configuredRight == null) return null; |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | if (legacyRight != null) result.putAll(legacyRight); |
| | | if (configuredRight != null) { |
| | | for (Map.Entry<String, Object> entry : configuredRight.entrySet()) { |
| | | Object value = entry.getValue(); |
| | | if (value != null && (!(value instanceof String text) || !text.isBlank())) result.put(entry.getKey(), value); |
| | | } |
| | | } |
| | | String parkingType = text(result.get("parkingType"), "free"); |
| | | if ("paid".equalsIgnoreCase(parkingType) && !text(result.get("consumeDesc"), "").isBlank()) { |
| | | result.put("detail", text(result.get("consumeDesc"), "")); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private boolean rightStatusEnabled(Map<String, Object> right) { |
| | | Object status = right.get("rightStatus"); |
| | | return status == null || number(status) != 0; |
| | | } |
| | | |
| | | private String usageInstructions(Map<String, Object> merchant, Map<String, Object> right) { |
| | | if (right != null && "paid".equalsIgnoreCase(text(right.get("parkingType"), ""))) { |
| | | String consumeDesc = text(right.get("consumeDesc"), text(right.get("detail"), "")); |
| | | if (!consumeDesc.isBlank()) return consumeDesc; |
| | | } |
| | | return text(merchant.get("usageInstructions"), "请咨询商户"); |
| | | } |
| | | |
| | | private List<Map<String, Object>> parkingStores(Object usageInstructions) { |
| | | List<Map<String, Object>> stores = parseList(usageInstructions); |
| | | int index = 0; |