shinnosuke
2026-08-27 8dbcc40964ee315fc35abad757ae9a368f298bf9
fix: correct benefit tags and verification statistics
2个文件已添加
5个文件已修改
313 ■■■■■ 已修改文件
rencaika/pages/entertainment-benefits/entertainment-benefits.uvue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rencaika/services/portal.uts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/portal/PortalController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/portal/PortalService.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/mapper/AdminWorkbenchStatisticsMapper.java 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/test/java/com/talentcard/server/business/portal/PortalControllerTests.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/test/java/com/talentcard/server/business/portal/PortalServiceTests.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rencaika/pages/entertainment-benefits/entertainment-benefits.uvue
@@ -26,7 +26,7 @@
                <view v-if="initialLoading" class="list-state"><view class="loading-spinner"></view><text>正在加载观影服务</text></view>
                <view v-else-if="initialError" class="list-state error-state"><text>观影服务加载失败</text><text class="retry-text" @tap="retryInitialLoad">重新加载</text></view>
                <view v-else-if="hasLoaded && visibleServices.length == 0" class="list-state"><text>暂无相关观影服务</text></view>
                <view v-else-if="hasLoaded"><view class="service-item" :class="item.available ? '' : 'is-unavailable'" v-for="item in visibleServices" :key="item.id" @tap="openService(item)"><image class="service-image" :src="item.image" mode="aspectFill" @error="replaceServiceImage(item)"></image><view class="service-info"><text class="service-name">{{item.name}}</text><text class="discount-tag">{{item.available ? item.discount : '已下架'}}</text><view class="address"><image src="/static/benefits-assets/map-pin.svg" mode="aspectFit"></image><text>{{item.address}}</text></view></view></view></view>
                <view v-else-if="hasLoaded"><view class="service-item" :class="item.available ? '' : 'is-unavailable'" v-for="item in visibleServices" :key="item.id" @tap="openService(item)"><image class="service-image" :src="item.image" mode="aspectFill" @error="replaceServiceImage(item)"></image><view class="service-info"><text class="service-name">{{item.name}}</text><text v-if="!item.available || item.hasCoupon" class="discount-tag">{{item.available ? '券' : '已下架'}}</text><view class="address"><image src="/static/benefits-assets/map-pin.svg" mode="aspectFit"></image><text>{{item.address}}</text></view></view></view></view>
                <view v-if="loadingMore" class="list-state compact"><view class="loading-spinner"></view><text>正在加载</text></view>
                <view v-else-if="loadMoreError" class="list-state compact load-error" @tap="retryLoadMore"><text>加载失败,点击重试</text></view>
                <view v-else-if="hasLoaded && !hasMore && visibleServices.length > 0" class="no-more"><text>已经到底了</text></view>
@@ -138,7 +138,8 @@
            const next = append ? visibleServices.value.slice() : []
            for (let index = 0; index < records.length; index++) {
                const item = records[index]
                next.push({ id: '' + item.id, merchantType: item.merchantType != null ? item.merchantType as string : 'cinema', name: item.merchantName != null ? item.merchantName as string : '观影服务', discount: item.discountText != null ? item.discountText as string : '人才专享', address: item.address != null ? item.address as string : '', image: item.coverUrl != null && item.coverUrl != '' ? resolveApiUrl(item.coverUrl as string) : fallbackImage, available: item.available !== false })
                const hasCoupon = item.couponAvailable === true || Number(item.couponAvailable) == 1 || String(item.discountText).trim() == '券'
                next.push({ id: '' + item.id, merchantType: item.merchantType != null ? item.merchantType as string : 'cinema', name: item.merchantName != null ? item.merchantName as string : '观影服务', discount: hasCoupon ? '券' : '', hasCoupon: hasCoupon, address: item.address != null ? item.address as string : '', image: item.coverUrl != null && item.coverUrl != '' ? resolveApiUrl(item.coverUrl as string) : fallbackImage, available: item.available !== false })
            }
            visibleServices.value = next
            currentPage.value++
rencaika/services/portal.uts
@@ -44,7 +44,7 @@
export const getStayTopicMerchants = (type: string, page: number, size: number, talentId: string, onSuccess: (data: any) => void, onError: (error: HttpError) => void): (() => void) => {
    const data: any = { type: type, page: page, size: size }
    if (talentId != '') data.talentId = Number(talentId)
    return request<any>({ path: getApiEndpoint('appStayTopicMerchants'), method: 'GET', skipAuth: true, data: data }, onSuccess, onError)
    return request<any>({ path: getApiEndpoint('appStayTopicMerchants'), method: 'GET', data: data }, onSuccess, onError)
}
export const getEntertainmentTopic = (onSuccess: (data: any) => void, onError: (error: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('appEntertainmentTopic'), method: 'GET', skipAuth: true }, onSuccess, onError)
export const getEntertainmentTopicMerchants = (page: number, size: number, onSuccess: (data: any) => void, onError: (error: HttpError) => void): (() => void) => request<any>({ path: getApiEndpoint('appEntertainmentTopicMerchants'), method: 'GET', skipAuth: true, data: { page: page, size: size } }, onSuccess, onError)
talent-card-server/src/main/java/com/talentcard/server/business/portal/PortalController.java
@@ -118,9 +118,10 @@
            @RequestParam(defaultValue = "hotel") String type,
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "4") int size,
            @RequestParam(required = false) Long talentId
            @RequestParam(required = false) Long talentId,
            @RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authorization
    ) {
        return ApiResponse.success(portalService.stayMerchants(type, page, size, talentId));
        return ApiResponse.success(portalService.stayMerchants(type, page, size, sessionTalentId(authorization, talentId)));
    }
    @GetMapping("/topics/entertainment")
talent-card-server/src/main/java/com/talentcard/server/business/portal/PortalService.java
@@ -410,6 +410,18 @@
        PageResult<Map<String, Object>> result = page("""
                select id, merchant_name, merchant_type_code as merchant_type, contact_name, contact_mobile, logo_url, cover_url, address,
                       service_label, status, sort_no, longitude, latitude, business_status,
                       (exists(select 1 from merchant_coupon c
                               where c.merchant_id = merchant.id and c.deleted = 0 and c.status = 1
                                 and (c.valid_start is null or c.valid_start <= current_date)
                                 and (c.valid_end is null or c.valid_end >= current_date))
                        or exists(select 1 from ticket_package p
                                  where p.deleted = 0 and p.status = 1
                                    and (p.valid_start is null or p.valid_start <= current_date)
                                    and (p.valid_end is null or p.valid_end >= current_date)
                                    and (p.merchant_id = merchant.id or exists (
                                        select 1 from ticket_package_merchant pm
                                        where pm.package_id = p.id and pm.merchant_id = merchant.id and pm.deleted = 0
                                    )))) as coupon_available,
                       (select min(p.sale_price) from merchant_product p where p.merchant_id = merchant.id and p.deleted = 0 and p.status = 1 and p.audit_status = 2) as sale_price,
                       (select min(p.original_price) from merchant_product p where p.merchant_id = merchant.id and p.deleted = 0 and p.status = 1 and p.audit_status = 2) as original_price
                from merchant
@@ -417,7 +429,9 @@
                "select count(*) from merchant" + merchantWhere, page, size);
        List<Map<String, Object>> records = result.records().stream().map(row -> {
            Map<String, Object> item = enrichMerchantSearchRow(row);
            item.put("discountText", textOrDefault(item.get("serviceLabel"), textOrDefault(item.get("discountText"), "人才专享")));
            boolean couponAvailable = booleanValue(item.get("couponAvailable"));
            item.put("couponAvailable", couponAvailable);
            item.put("discountText", couponAvailable ? "券" : "");
            return item;
        }).toList();
        return new PageResult<>(records, result.page(), result.size(), result.total());
@@ -538,10 +552,10 @@
                    && booleanValue(configuredRight.get("storeConsume"))
                    && booleanValue(configuredRight.get("consumeEnabled")));
            if (configuredRight != null) {
                item.put("discountText", textOrDefault(configuredRight.get("tag"), textOrDefault(item.get("serviceLabel"), "")));
                item.put("discountText", merchantRightDiscountText(configuredRight));
                item.put("rightsAvailable", true);
            } else if (levelRight != null || !rights.isEmpty()) {
                item.put("discountText", levelRight == null ? "" : textOrDefault(levelRight.get("tag"), ""));
                item.put("discountText", levelRight == null ? "" : merchantRightDiscountText(levelRight));
                item.put("rightsAvailable", false);
                item.put("available", false);
                item.put("unavailableReason", levelRight == null ? "当前人才卡暂未配置该权益" : "当前人才卡暂未开通该权益");
@@ -555,6 +569,19 @@
        return new PageResult<>(records, result.page(), result.size(), result.total());
    }
    private String merchantRightDiscountText(Map<String, Object> right) {
        if (!booleanValue(right.get("storeConsume")) || !booleanValue(right.get("consumeEnabled"))) return "";
        String discount = textOrDefault(right.get("discount"), "").trim();
        if (discount.isBlank()) return "";
        try {
            BigDecimal value = new BigDecimal(discount).stripTrailingZeros();
            if (value.compareTo(BigDecimal.ZERO) <= 0 || value.compareTo(BigDecimal.TEN) > 0) return "";
            return value.toPlainString() + "折";
        } catch (NumberFormatException ignored) {
            return "";
        }
    }
    @Transactional(readOnly = true)
    public Map<String, Object> premiumTopic() {
        Map<String, Object> topic = one("""
talent-card-server/src/main/java/com/talentcard/server/mapper/AdminWorkbenchStatisticsMapper.java
@@ -18,10 +18,24 @@
                when coalesce(o.subsidy_amount, 0) > 0
                     and (o.product_id is null or coalesce(p.is_government_subsidy, 0) = 1)
                  then o.subsidy_amount else 0 end), 0) as subsidyAmountCents,
              (select count(*) from verify_record v
                 where v.status = 1
                   and v.verify_time >= date_format(current_date, '%Y-%m-01')
                   and v.verify_time < date_add(last_day(current_date), interval 1 day)) as monthlyVerificationCount,
              (select count(*) from benefit_order verified_order
                 where verified_order.deleted = 0
                   and verified_order.order_status = 1
                   and verified_order.verify_status = 1
                   and coalesce(
                         (select max(v.verify_time) from verify_record v
                           where v.order_id = verified_order.id and v.status = 1),
                         (select max(pt.paid_at) from payment_transaction pt
                           where pt.order_no = verified_order.order_no and pt.status = 'SUCCEEDED' and pt.paid_at is not null),
                         verified_order.updated_at
                       ) >= date_format(current_date, '%Y-%m-01')
                   and coalesce(
                         (select max(v.verify_time) from verify_record v
                           where v.order_id = verified_order.id and v.status = 1),
                         (select max(pt.paid_at) from payment_transaction pt
                           where pt.order_no = verified_order.order_no and pt.status = 'SUCCEEDED' and pt.paid_at is not null),
                         verified_order.updated_at
                       ) < date_add(last_day(current_date), interval 1 day)) as monthlyVerificationCount,
              (select count(*) from talent_user u where u.deleted = 0) as talentCount,
              (select count(*) from benefit_order po where po.deleted = 0 and po.order_status = 0) as pendingOrderCount,
              (select count(*) from merchant_product pp where pp.deleted = 0 and pp.audit_status = 1) as pendingProductCount,
@@ -37,14 +51,42 @@
              coalesce((select count(*) from talent_user u
                         where u.deleted = 0
                           and date(u.created_at) = date_sub(current_date, interval 1 day)), 0) as talentCompare,
              (coalesce((select count(*) from verify_record current_month_verify
                          where current_month_verify.status = 1
                            and current_month_verify.verify_time >= date_format(current_date, '%Y-%m-01')
                            and current_month_verify.verify_time < date_add(last_day(current_date), interval 1 day)), 0)
               - coalesce((select count(*) from verify_record previous_month_verify
                            where previous_month_verify.status = 1
                              and previous_month_verify.verify_time >= date_sub(date_format(current_date, '%Y-%m-01'), interval 1 month)
                              and previous_month_verify.verify_time < date_format(current_date, '%Y-%m-01')), 0)) as monthlyVerificationCompare,
              (coalesce((select count(*) from benefit_order current_month_order
                          where current_month_order.deleted = 0
                            and current_month_order.order_status = 1
                            and current_month_order.verify_status = 1
                            and coalesce(
                                  (select max(v.verify_time) from verify_record v
                                    where v.order_id = current_month_order.id and v.status = 1),
                                  (select max(pt.paid_at) from payment_transaction pt
                                    where pt.order_no = current_month_order.order_no and pt.status = 'SUCCEEDED' and pt.paid_at is not null),
                                  current_month_order.updated_at
                                ) >= date_format(current_date, '%Y-%m-01')
                            and coalesce(
                                  (select max(v.verify_time) from verify_record v
                                    where v.order_id = current_month_order.id and v.status = 1),
                                  (select max(pt.paid_at) from payment_transaction pt
                                    where pt.order_no = current_month_order.order_no and pt.status = 'SUCCEEDED' and pt.paid_at is not null),
                                  current_month_order.updated_at
                                ) < date_add(last_day(current_date), interval 1 day)), 0)
               - coalesce((select count(*) from benefit_order previous_month_order
                            where previous_month_order.deleted = 0
                              and previous_month_order.order_status = 1
                              and previous_month_order.verify_status = 1
                              and coalesce(
                                    (select max(v.verify_time) from verify_record v
                                      where v.order_id = previous_month_order.id and v.status = 1),
                                    (select max(pt.paid_at) from payment_transaction pt
                                      where pt.order_no = previous_month_order.order_no and pt.status = 'SUCCEEDED' and pt.paid_at is not null),
                                    previous_month_order.updated_at
                                  ) >= date_sub(date_format(current_date, '%Y-%m-01'), interval 1 month)
                              and coalesce(
                                    (select max(v.verify_time) from verify_record v
                                      where v.order_id = previous_month_order.id and v.status = 1),
                                    (select max(pt.paid_at) from payment_transaction pt
                                      where pt.order_no = previous_month_order.order_no and pt.status = 'SUCCEEDED' and pt.paid_at is not null),
                                    previous_month_order.updated_at
                                  ) < date_format(current_date, '%Y-%m-01')), 0)) as monthlyVerificationCompare,
              coalesce(sum(case
                when coalesce(o.subsidy_amount, 0) > 0
                     and (o.product_id is null or coalesce(p.is_government_subsidy, 0) = 1)
@@ -71,10 +113,24 @@
                     where u.deleted = 0
                       and u.created_at < date_add(days.bucket_day, interval 1 day)) as talent,
                   (select count(*)
                      from verify_record v
                     where v.status = 1
                       and v.verify_time >= days.bucket_day
                       and v.verify_time < date_add(days.bucket_day, interval 1 day)) as verification,
                      from benefit_order verified_order
                     where verified_order.deleted = 0
                       and verified_order.order_status = 1
                       and verified_order.verify_status = 1
                       and coalesce(
                             (select max(v.verify_time) from verify_record v
                               where v.order_id = verified_order.id and v.status = 1),
                             (select max(pt.paid_at) from payment_transaction pt
                               where pt.order_no = verified_order.order_no and pt.status = 'SUCCEEDED' and pt.paid_at is not null),
                             verified_order.updated_at
                           ) >= days.bucket_day
                       and coalesce(
                             (select max(v.verify_time) from verify_record v
                               where v.order_id = verified_order.id and v.status = 1),
                             (select max(pt.paid_at) from payment_transaction pt
                               where pt.order_no = verified_order.order_no and pt.status = 'SUCCEEDED' and pt.paid_at is not null),
                             verified_order.updated_at
                           ) < date_add(days.bucket_day, interval 1 day)) as verification,
                   round(coalesce((select sum(case
                       when coalesce(o.subsidy_amount, 0) > 0
                            and (o.product_id is null or coalesce(p.is_government_subsidy, 0) = 1)
@@ -107,7 +163,7 @@
    @Select("""
            select buckets.bucket_time as time,
                   coalesce(count(v.id), 0) as `order`,
                   round(coalesce(sum(v.verify_amount), 0) / 100, 2) as amount
                   round(coalesce(sum(v.pay_amount), 0) / 100, 2) as amount
            from (
              select 0 bucket_hour, '0:00' bucket_time union all select 2, '2:00'
              union all select 4, '4:00' union all select 6, '6:00'
@@ -116,10 +172,19 @@
              union all select 16, '16:00' union all select 18, '18:00'
              union all select 20, '20:00' union all select 22, '22:00'
            ) buckets
            left join verify_record v on v.status = 1
              and v.verify_time >= current_date
              and v.verify_time < date_add(current_date, interval 1 day)
              and floor(hour(v.verify_time) / 2) * 2 = buckets.bucket_hour
            left join (
              select o.id, o.pay_amount,
                     coalesce(
                       (select max(vr.verify_time) from verify_record vr where vr.order_id = o.id and vr.status = 1),
                       (select max(pt.paid_at) from payment_transaction pt
                         where pt.order_no = o.order_no and pt.status = 'SUCCEEDED' and pt.paid_at is not null),
                       o.updated_at
                     ) as effective_time
                from benefit_order o
               where o.deleted = 0 and o.order_status = 1 and o.verify_status = 1
            ) v on v.effective_time >= current_date
              and v.effective_time < date_add(current_date, interval 1 day)
              and floor(hour(v.effective_time) / 2) * 2 = buckets.bucket_hour
            group by buckets.bucket_hour, buckets.bucket_time
            order by buckets.bucket_hour
            """)
@@ -128,7 +193,7 @@
    @Select("""
            select date_format(days.bucket_day, '%c/%e') as time,
                   coalesce(count(v.id), 0) as `order`,
                   round(coalesce(sum(v.verify_amount), 0) / 100, 2) as amount
                   round(coalesce(sum(v.pay_amount), 0) / 100, 2) as amount
            from (
              select date_sub(current_date, interval 6 day) as bucket_day union all
              select date_sub(current_date, interval 5 day) union all
@@ -138,9 +203,18 @@
              select date_sub(current_date, interval 1 day) union all
              select current_date
            ) days
            left join verify_record v on v.status = 1
              and v.verify_time >= days.bucket_day
              and v.verify_time < date_add(days.bucket_day, interval 1 day)
            left join (
              select o.id, o.pay_amount,
                     coalesce(
                       (select max(vr.verify_time) from verify_record vr where vr.order_id = o.id and vr.status = 1),
                       (select max(pt.paid_at) from payment_transaction pt
                         where pt.order_no = o.order_no and pt.status = 'SUCCEEDED' and pt.paid_at is not null),
                       o.updated_at
                     ) as effective_time
                from benefit_order o
               where o.deleted = 0 and o.order_status = 1 and o.verify_status = 1
            ) v on v.effective_time >= days.bucket_day
              and v.effective_time < date_add(days.bucket_day, interval 1 day)
            group by days.bucket_day
            order by days.bucket_day
            """)
@@ -149,7 +223,7 @@
    @Select("""
            select concat(weeks.week_start, '~', weeks.week_end) as time,
                   coalesce(count(v.id), 0) as `order`,
                   round(coalesce(sum(v.verify_amount), 0) / 100, 2) as amount
                   round(coalesce(sum(v.pay_amount), 0) / 100, 2) as amount
            from (
              select 0 bucket_index,
                     date_format(date_add(date_sub(current_date, interval dayofmonth(current_date) - 1 day), interval 0 day), '%c/%e') as week_start,
@@ -181,9 +255,18 @@
                     date_add(date_sub(current_date, interval dayofmonth(current_date) - 1 day), interval 28 day),
                     last_day(current_date)
            ) weeks
            left join verify_record v on v.status = 1
              and v.verify_time >= weeks.start_date
              and v.verify_time < date_add(weeks.end_date, interval 1 day)
            left join (
              select o.id, o.pay_amount,
                     coalesce(
                       (select max(vr.verify_time) from verify_record vr where vr.order_id = o.id and vr.status = 1),
                       (select max(pt.paid_at) from payment_transaction pt
                         where pt.order_no = o.order_no and pt.status = 'SUCCEEDED' and pt.paid_at is not null),
                       o.updated_at
                     ) as effective_time
                from benefit_order o
               where o.deleted = 0 and o.order_status = 1 and o.verify_status = 1
            ) v on v.effective_time >= weeks.start_date
              and v.effective_time < date_add(weeks.end_date, interval 1 day)
            where weeks.start_date <= last_day(current_date)
            group by weeks.bucket_index, weeks.week_start, weeks.week_end, weeks.start_date
            order by weeks.bucket_index
@@ -193,15 +276,24 @@
    @Select("""
            select concat(months.bucket_month, '月') as time,
                   coalesce(count(v.id), 0) as `order`,
                   round(coalesce(sum(v.verify_amount), 0) / 100, 2) as amount
                   round(coalesce(sum(v.pay_amount), 0) / 100, 2) as amount
            from (
              select 1 bucket_month union all select 2 union all select 3 union all select 4
              union all select 5 union all select 6 union all select 7 union all select 8
              union all select 9 union all select 10 union all select 11 union all select 12
            ) months
            left join verify_record v on v.status = 1
              and year(v.verify_time) = year(current_date)
              and month(v.verify_time) = months.bucket_month
            left join (
              select o.id, o.pay_amount,
                     coalesce(
                       (select max(vr.verify_time) from verify_record vr where vr.order_id = o.id and vr.status = 1),
                       (select max(pt.paid_at) from payment_transaction pt
                         where pt.order_no = o.order_no and pt.status = 'SUCCEEDED' and pt.paid_at is not null),
                       o.updated_at
                     ) as effective_time
                from benefit_order o
               where o.deleted = 0 and o.order_status = 1 and o.verify_status = 1
            ) v on year(v.effective_time) = year(current_date)
              and month(v.effective_time) = months.bucket_month
            group by months.bucket_month
            order by months.bucket_month
            """)
talent-card-server/src/test/java/com/talentcard/server/business/portal/PortalControllerTests.java
New file
@@ -0,0 +1,50 @@
package com.talentcard.server.business.portal;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.List;
import com.talentcard.server.business.PageResult;
import com.talentcard.server.business.auth.TalentAuthService;
import com.talentcard.server.business.auth.TalentSessionPrincipal;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class PortalControllerTests {
    @Mock
    private PortalService portalService;
    @Mock
    private TalentAuthService talentAuthService;
    @InjectMocks
    private PortalController controller;
    @Test
    void stayMerchantsUsesTalentIdFromAuthenticatedSession() {
        when(talentAuthService.authenticate("Bearer session-token"))
                .thenReturn(new TalentSessionPrincipal(42L));
        when(portalService.stayMerchants("hotel", 1, 4, 42L))
                .thenReturn(new PageResult<>(List.of(), 1, 4, 0));
        controller.stayMerchants("hotel", 1, 4, 7L, "Bearer session-token");
        verify(portalService).stayMerchants("hotel", 1, 4, 42L);
    }
    @Test
    void stayMerchantsKeepsQueryTalentIdForUnauthenticatedRequests() {
        when(portalService.stayMerchants("hotel", 1, 4, 7L))
                .thenReturn(new PageResult<>(List.of(), 1, 4, 0));
        controller.stayMerchants("hotel", 1, 4, 7L, null);
        verify(portalService).stayMerchants("hotel", 1, 4, 7L);
    }
}
talent-card-server/src/test/java/com/talentcard/server/business/portal/PortalServiceTests.java
New file
@@ -0,0 +1,60 @@
package com.talentcard.server.business.portal;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.jdbc.core.JdbcTemplate;
@ExtendWith(MockitoExtension.class)
class PortalServiceTests {
    @Mock
    private JdbcTemplate jdbc;
    @InjectMocks
    private PortalService service;
    @Test
    void cinemaMerchantOnlyShowsCouponTagWhenAnActiveCouponExists() {
        when(jdbc.queryForList(anyString(), any(Object[].class))).thenReturn(List.of(
                Map.of("id", 1L, "merchant_name", "有券影院", "merchant_type", "cinema", "status", 1, "coupon_available", 1),
                Map.of("id", 2L, "merchant_name", "无券影院", "merchant_type", "cinema", "status", 1, "coupon_available", 0)));
        when(jdbc.queryForObject(anyString(), eq(Long.class), any(Object[].class))).thenReturn(2L);
        var result = service.entertainmentMerchants(1, 4);
        assertThat(result.records().get(0))
                .containsEntry("couponAvailable", true)
                .containsEntry("discountText", "券");
        assertThat(result.records().get(1))
                .containsEntry("couponAvailable", false)
                .containsEntry("discountText", "");
    }
    @Test
    void shoppingMerchantUsesConfiguredStoreDiscountInsteadOfRightsTag() throws Exception {
        Method method = PortalService.class.getDeclaredMethod("merchantRightDiscountText", Map.class);
        method.setAccessible(true);
        String discount = (String) method.invoke(service, Map.of(
                "enabled", true,
                "tag", "卓越人才到店消费可用",
                "storeConsume", true,
                "consumeEnabled", true,
                "discount", "8.50"));
        assertThat(discount).isEqualTo("8.5折");
    }
}