yxh
5 天以前 b1f41c066b0b7c38faffddbfe73b9be8d0bd508e
修改了增值税城建税
已添加5个文件
498 ■■■■■ 文件已修改
ruoyi-admin/src/main/java/com/ruoyi/web/controller/project/CasePanoramaController.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/JacksonDateConfig.java 272 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-project/src/main/java/com/ruoyi/project/mapper/CasePanoramaMapper.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-project/src/main/java/com/ruoyi/project/service/ICasePanoramaService.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-project/src/main/resources/mapper/project/CasePanoramaMapper.xml 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/project/CasePanoramaController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
package com.ruoyi.web.controller.project;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.project.domain.CasePanorama;
import com.ruoyi.project.domain.CasePanoramaReq;
import com.ruoyi.project.service.ICasePanoramaService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.List;
/**
 * æ¡ˆä¾‹å…¨æ™¯
 *
 * @author ruoyi
 * @date 2026-10-28
 */
@Slf4j
@Api("案例全景")
@RestController
@RequestMapping("/project/casePanorama")
public class CasePanoramaController extends BaseController {
    @Autowired
    private ICasePanoramaService casePanoramaService;
    /**
     * æŸ¥è¯¢æ¡ˆä¾‹å…¨æ™¯
     */
    @ApiOperation("查询案例全景")
    @GetMapping("/list")
    public TableDataInfo list(CasePanoramaReq req) {
        startPage();
        return getDataTable(casePanoramaService.selectCasePanoramaList(req));
    }
    /**
     * å¯¼å‡ºæ¡ˆä¾‹å…¨æ™¯
     */
    @ApiOperation("导出案例全景")
    @Log(title = "案例全景", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult export(CasePanoramaReq req) {
        List<CasePanorama> list = casePanoramaService.selectCasePanoramaList(req);
        for (CasePanorama row : list) {
            blankZero(row);
        }
        ExcelUtil<CasePanorama> util = new ExcelUtil<CasePanorama>(CasePanorama.class);
        return util.exportExcel(list, "案例全景数据");
    }
    /**
     * ä¸šåŠ¡ç»„ä¸‹æ‹‰é€‰é¡¹
     */
    @ApiOperation("业务组下拉选项")
    @GetMapping("/deptOptions")
    public AjaxResult deptOptions() {
        return AjaxResult.success(casePanoramaService.selectDeptNames());
    }
    /**
     * é‡‘额 0 å¯¼å‡ºä¸ºç©º
     */
    private void blankZero(CasePanorama row) {
        row.setTotalCost(blankZero(row.getTotalCost()));
        row.setDonorFamilyCost(blankZero(row.getDonorFamilyCost()));
        row.setMedCost(blankZero(row.getMedCost()));
        row.setExpertCost(blankZero(row.getExpertCost()));
        row.setItemCode34(blankZero(row.getItemCode34()));
        row.setItemCode42(blankZero(row.getItemCode42()));
        row.setItemCode46(blankZero(row.getItemCode46()));
        row.setItemCode50(blankZero(row.getItemCode50()));
        row.setItemCode44(blankZero(row.getItemCode44()));
        row.setItemCode47(blankZero(row.getItemCode47()));
        row.setItemCode48(blankZero(row.getItemCode48()));
        row.setItemCode21(blankZero(row.getItemCode21()));
        row.setItemCode12(blankZero(row.getItemCode12()));
        row.setItemCode17(blankZero(row.getItemCode17()));
        row.setItemCode23(blankZero(row.getItemCode23()));
        row.setItemCode25(blankZero(row.getItemCode25()));
    }
    private BigDecimal blankZero(BigDecimal value) {
        return (value == null || value.compareTo(BigDecimal.ZERO) == 0) ? null : value;
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/config/JacksonDateConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,272 @@
package com.ruoyi.framework.config;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
/**
 * æ—¶é—´æ ¼å¼å…¨å±€é…ç½®ï¼šæŽ¥å£å‡ºå‚一律精确到分({@code yyyy-MM-dd HH:mm}),入参宽松兼容带/不带秒。
 *
 * <p><b>为什么用全局配置而不是逐字段改注解:</b>系统里
 * {@code @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")} æœ‰ 182 å¤„、{@code @DateTimeFormat} 73 å¤„,
 * é€ä¸ªæ”¹æ—¢å®¹æ˜“漏(漏一处就是提交 400),也覆盖不到未加注解的 {@code Date} å­—段。
 * è¿™é‡Œç”¨ Jackson çš„ {@link BeanSerializerModifier} / {@link BeanDeserializerModifier}
 * åœ¨<b>不改任何注解</b>的前提下统一格式化口径。
 *
 * <p><b>规则:</b>
 * <ol>
 *   <li>序列化:所有 {@code Date} è¾“出 {@code yyyy-MM-dd HH:mm}(沿用
 *       {@link ApplicationConfig} é…ç½®çš„ JVM é»˜è®¤æ—¶åŒºï¼›å­—段上显式声明了
 *       {@code @JsonFormat(timezone=...)} çš„仍按其时区);</li>
 *   <li>反序列化:兼容 {@code yyyy-MM-dd HH:mm:ss} / {@code yyyy-MM-dd HH:mm} /
 *       {@code yyyy-MM-dd} / æ–œæ åˆ†éš” / ISO8601 / æ—¶é—´æˆ³ â€”— è¿™æ ·å‰ç«¯å›žå¡«"无秒"的值原样提交也不会 400;</li>
 *   <li>运维日志(操作日志/登录日志/定时任务日志)<b>保留秒</b>,便于排查问题。</li>
 * </ol>
 *
 * <p><b>不影响的范围:</b>数据库存储(仍为 datetime,秒位照常保留)、
 * Excel å¯¼å…¥å¯¼å‡ºï¼ˆèµ° {@code @Excel.dateFormat},未改动)、
 * è‡´è¿œååŒæŽ¨é€æŠ¥æ–‡ï¼ˆè‡ªå»º {@code SimpleDateFormat},未改动)、
 * é’‰é’‰æ¶ˆæ¯æ—¶é—´ï¼ˆ{@code FundNotifyMessageBuilder} è‡ªå»ºæ ¼å¼ï¼‰ã€‚
 *
 * @author ruoyi
 */
@Configuration
public class JacksonDateConfig {
    /** æ— ç§’格式 */
    private static final String PATTERN_NO_SECONDS = "yyyy-MM-dd HH:mm";
    /** æœ‰ç§’格式(运维日志用) */
    private static final String PATTERN_WITH_SECONDS = "yyyy-MM-dd HH:mm:ss";
    /** ä¿ç•™ç§’的类:运维日志,秒对排查有用 */
    private static final Set<String> KEEP_SECONDS_CLASSES = new HashSet<String>(Arrays.asList(
            "com.ruoyi.system.domain.SysOperLog",
            "com.ruoyi.system.domain.SysLogininfor",
            "com.ruoyi.quartz.domain.SysJobLog"
    ));
    /** ååºåˆ—化尝试顺序:从最具体到最宽松 */
    private static final String[] PARSE_PATTERNS = new String[]{
            "yyyy-MM-dd HH:mm:ss.SSS",
            "yyyy-MM-dd HH:mm:ss",
            "yyyy-MM-dd HH:mm",
            "yyyy-MM-dd'T'HH:mm:ss.SSS",
            "yyyy-MM-dd'T'HH:mm:ss",
            "yyyy-MM-dd'T'HH:mm",
            "yyyy/MM/dd HH:mm:ss",
            "yyyy/MM/dd HH:mm",
            "yyyy-MM-dd",
            "yyyy/MM/dd",
            "yyyyMMddHHmmss",
            "yyyyMMdd"
    };
    /**
     * 2026-10-15 æ”¹é€ ï¼šä¸å†ç”¨ {@code Jackson2ObjectMapperBuilderCustomizer} + {@code modulesToInstall}。
     *
     * <p><b>为什么改:</b>{@code Jackson2ObjectMapperBuilder.modulesToInstall(...)} çš„语义是
     * "设置"模块列表(不是追加),而 Spring Boot è‡ªèº«ä¹Ÿç”¨åŒä¸€æ–¹æ³•注册它自带的模块(JavaTime ç­‰ï¼‰ï¼›
     * ä¸¤ä¸ªå®šåˆ¶å™¨äº’相覆盖,谁后执行谁的列表生效。实测后果:本模块被丢掉 â€”—
     * è¡¨çŽ°ä¸º"类在包里、bean ä¹Ÿåˆ›å»ºäº†ï¼Œä½† Date çš„宽松解析与去秒序列化都不起作用",
     * å‰ç«¯æŒ‰ {@code yyyy-MM-dd} æäº¤æ—¶ç›´æŽ¥ 400(Cannot deserialize ... expected format yyyy-MM-dd HH:mm:ss)。
     *
     * <p>改为把模块声明成 {@code SimpleModule} bean:Spring Boot é€šè¿‡ {@code ObjectProvider<Module>}
     * ç»Ÿä¸€æ”¶é›†æ‰€æœ‰ Module bean å¹¶ä¸€æ¬¡æ€§å®‰è£…,与定制器的执行顺序无关。
     */
    @Bean
    public SimpleModule dateNoSecondsModule() {
        SimpleModule module = new SimpleModule("dateNoSecondsModule");
        module.addSerializer(Date.class, new NoSecondsDateSerializer());
        module.setSerializerModifier(new NoSecondsDateSerializerModifier());
        module.setDeserializerModifier(new LenientDateDeserializerModifier());
        // å¯åŠ¨è‡ªè¯ï¼šè¿™æ¡æ—¥å¿—å‡ºçŽ°å³è¯´æ˜Žæ¨¡å—å·²è¢«åˆ›å»ºå¹¶ä¼šè¢« Spring Boot å®‰è£…
        LoggerFactory.getLogger(JacksonDateConfig.class).info(
                "[JacksonDateConfig] å·²å¯ç”¨ï¼šDate å‡ºå‚去秒({}),入参宽松兼容({})",
                PATTERN_NO_SECONDS, String.join(" / ", PARSE_PATTERNS));
        return module;
    }
    /**
     * åºåˆ—化器:按分钟输出。
     * åªä½œç”¨äºŽåºåˆ—化,<b>不影响入参解析</b>。
     */
    static class NoSecondsDateSerializer extends JsonSerializer<Date> implements ContextualSerializer {
        private final DateTimeFormatter formatter;
        NoSecondsDateSerializer() {
            this.formatter = null;
        }
        private NoSecondsDateSerializer(TimeZone timeZone, boolean keepSeconds) {
            this.formatter = DateTimeFormatter
                    .ofPattern(keepSeconds ? PATTERN_WITH_SECONDS : PATTERN_NO_SECONDS)
                    .withZone((timeZone == null ? TimeZone.getDefault() : timeZone).toZoneId());
        }
        @Override
        public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
            TimeZone timeZone = resolveTimeZone(prov.getConfig().getTimeZone(),
                    property == null ? null : property.getMember());
            return new NoSecondsDateSerializer(timeZone, isKeepSeconds(property == null ? null : property.getMember()));
        }
        @Override
        public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            if (value == null) {
                gen.writeNull();
                return;
            }
            if (formatter != null) {
                gen.writeString(formatter.format(value.toInstant()));
                return;
            }
            // å…œåº•:未被 contextualize æ—¶æŒ‰å½“前配置时区、无秒输出
            gen.writeString(DateTimeFormatter.ofPattern(PATTERN_NO_SECONDS)
                    .withZone(serializers.getConfig().getTimeZone().toZoneId())
                    .format(value.toInstant()));
        }
    }
    /**
     * è®© Bean å±žæ€§ä¸Šçš„æ—¥æœŸä¹Ÿèµ°ä¸Šé¢çš„序列化器。
     * å­—段级 {@code @JsonFormat} ç”Ÿæˆçš„序列化器优先级更高,必须在这里替换掉,否则注解字段仍带秒。
     */
    static class NoSecondsDateSerializerModifier extends BeanSerializerModifier {
        @Override
        @SuppressWarnings({"unchecked", "rawtypes"})
        public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
                                                         List<BeanPropertyWriter> beanProperties) {
            boolean keepSeconds = KEEP_SECONDS_CLASSES.contains(beanDesc.getBeanClass().getName());
            for (BeanPropertyWriter writer : beanProperties) {
                if (Date.class.isAssignableFrom(writer.getType().getRawClass())) {
                    TimeZone timeZone = resolveTimeZone(config.getTimeZone(), writer.getMember());
                    writer.assignSerializer((JsonSerializer) new NoSecondsDateSerializer(timeZone, keepSeconds));
                }
            }
            return beanProperties;
        }
    }
    /**
     * å…¥å‚解析:兼容带秒/不带秒/纯日期/ISO/时间戳,避免"出参无秒、回填后再提交"导致 400。
     */
    static class LenientDateDeserializerModifier extends BeanDeserializerModifier {
        @Override
        @SuppressWarnings({"unchecked", "rawtypes"})
        public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc,
                                                     BeanDeserializerBuilder builder) {
            Iterator<SettableBeanProperty> properties = builder.getProperties();
            List<SettableBeanProperty> replaced = new ArrayList<SettableBeanProperty>();
            while (properties.hasNext()) {
                SettableBeanProperty property = properties.next();
                if (property.getType() != null && Date.class.isAssignableFrom(property.getType().getRawClass())) {
                    TimeZone timeZone = resolveTimeZone(config.getTimeZone(), property.getMember());
                    replaced.add(property.withValueDeserializer(new LenientDateDeserializer(timeZone)));
                }
            }
            // ä¸èƒ½è¾¹éåŽ†è¾¹æ”¹ï¼Œå…ˆæ”¶é›†å†æ›¿æ¢
            for (SettableBeanProperty property : replaced) {
                builder.addOrReplaceProperty(property, true);
            }
            return builder;
        }
    }
    /** å®½æ¾æ—¥æœŸè§£æžå™¨ */
    static class LenientDateDeserializer extends JsonDeserializer<Date> {
        private final TimeZone timeZone;
        LenientDateDeserializer(TimeZone timeZone) {
            this.timeZone = timeZone == null ? TimeZone.getDefault() : timeZone;
        }
        @Override
        public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
                return new Date(p.getLongValue());
            }
            String text = p.getValueAsString();
            if (text == null || text.trim().isEmpty()) {
                return null;
            }
            text = text.trim();
            for (String pattern : PARSE_PATTERNS) {
                SimpleDateFormat format = new SimpleDateFormat(pattern);
                format.setLenient(false);
                format.setTimeZone(timeZone);
                ParsePosition position = new ParsePosition(0);
                Date parsed = format.parse(text, position);
                // å¿…须整串被消费,否则 "yyyy-MM-dd" ä¼šæŠŠ "2026-10-08 15:23" çš„æ—¶åˆ†é™é»˜ä¸¢æŽ‰
                if (parsed != null && position.getIndex() == text.length()) {
                    return parsed;
                }
            }
            try {
                return Date.from(Instant.parse(text));
            } catch (Exception ignore) {
                // è½åˆ°ä¸‹é¢ç»Ÿä¸€æŠ¥é”™
            }
            throw ctxt.weirdStringException(text, Date.class,
                    "无法解析的时间格式,支持 yyyy-MM-dd[ HH:mm[:ss]]");
        }
    }
    /** å±žæ€§ä¸Šæ˜¾å¼å£°æ˜Žçš„æ—¶åŒºä¼˜å…ˆï¼Œå¦åˆ™ç”¨å…¨å±€é…ç½®ï¼ˆå³ JVM é»˜è®¤æ—¶åŒºï¼‰ */
    private static TimeZone resolveTimeZone(TimeZone fallback, AnnotatedMember member) {
        if (member != null) {
            try {
                JsonFormat jsonFormat = member.getAnnotation(JsonFormat.class);
                if (jsonFormat != null && jsonFormat.timezone() != null
                        && !jsonFormat.timezone().isEmpty()
                        && !"##default".equals(jsonFormat.timezone())) {
                    return TimeZone.getTimeZone(jsonFormat.timezone());
                }
            } catch (Exception ignore) {
                // å–不到注记时用全局时区
            }
        }
        return fallback == null ? TimeZone.getDefault() : fallback;
    }
    /** è¿ç»´æ—¥å¿—类保留秒 */
    private static boolean isKeepSeconds(AnnotatedMember member) {
        return member != null && KEEP_SECONDS_CLASSES.contains(member.getDeclaringClass().getName());
    }
}
ruoyi-project/src/main/java/com/ruoyi/project/mapper/CasePanoramaMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package com.ruoyi.project.mapper;
import com.ruoyi.project.domain.CasePanorama;
import com.ruoyi.project.domain.CasePanoramaReq;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * æ¡ˆä¾‹å…¨æ™¯Mapper
 *
 * @author ruoyi
 * @date 2026-10-28
 */
@Mapper
public interface CasePanoramaMapper {
    /**
     * æŸ¥è¯¢æ¡ˆä¾‹å…¨æ™¯
     */
    List<CasePanorama> selectCasePanoramaList(@Param("req") CasePanoramaReq req);
    /**
     * æŸ¥è¯¢ä¸šåŠ¡ç»„é€‰é¡¹
     */
    List<String> selectDeptNames();
}
ruoyi-project/src/main/java/com/ruoyi/project/service/ICasePanoramaService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package com.ruoyi.project.service;
import com.ruoyi.project.domain.CasePanorama;
import com.ruoyi.project.domain.CasePanoramaReq;
import java.util.List;
/**
 * æ¡ˆä¾‹å…¨æ™¯Service
 *
 * @author ruoyi
 * @date 2026-10-28
 */
public interface ICasePanoramaService {
    /**
     * æŸ¥è¯¢æ¡ˆä¾‹å…¨æ™¯
     */
    List<CasePanorama> selectCasePanoramaList(CasePanoramaReq req);
    /**
     * æŸ¥è¯¢ä¸šåŠ¡ç»„é€‰é¡¹
     */
    List<String> selectDeptNames();
}
ruoyi-project/src/main/resources/mapper/project/CasePanoramaMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.project.mapper.CasePanoramaMapper">
    <sql id="casePanoramaColumns">
        id,
        DonorName AS donorName,
        ReportTime AS reportTime,
        deptname AS deptName,
        ReporterName AS reporterName,
        Dontiontermination AS terminationFlag,
        workflow AS workflow,
        COALESCE(dd.dict_label, CAST(workflow AS CHAR)) AS workflowName,
        DonateTime AS donateTime,
        DonationHospital AS donationHospital,
        OrganName AS organName,
        OrganProcurement AS organProcurement,
        OrganTransplantation AS organTransplantation,
        COALESCE(DonorFamilysCost, 0) + COALESCE(medCost, 0) + COALESCE(ExpertCost, 0) + COALESCE(ItemCode3_4, 0) AS totalCost,
        DonorFamilysCost AS donorFamilyCost,
        medCost AS medCost,
        ExpertCost AS expertCost,
        ItemCode3_4 AS itemCode34,
        ItemCode42 AS itemCode42,
        ItemCode46 AS itemCode46,
        ItemCode50 AS itemCode50,
        ItemCode44 AS itemCode44,
        ItemCode47 AS itemCode47,
        ItemCode48 AS itemCode48,
        ItemCode21 AS itemCode21,
        ItemCode12 AS itemCode12,
        ItemCode17 AS itemCode17,
        ItemCode23 AS itemCode23,
        ItemCode25 AS itemCode25
    </sql>
    <select id="selectCasePanoramaList" resultType="com.ruoyi.project.domain.CasePanorama">
        SELECT
        <include refid="casePanoramaColumns"/>
        FROM v_donationfeestat v
        LEFT JOIN (
            SELECT dict_value, MAX(dict_label) AS dict_label
            FROM sys_dict_data
            WHERE dict_type = 'sys_donornode'
            GROUP BY dict_value
        ) dd ON dd.dict_value COLLATE utf8mb4_general_ci = CAST(v.workflow AS CHAR) COLLATE utf8mb4_general_ci
        <where>
            <if test="req.donorName != null and req.donorName != ''">
                AND DonorName LIKE CONCAT('%', #{req.donorName}, '%')
            </if>
            <if test="req.donateTimeBegin != null and req.donateTimeBegin != ''">
                AND DonateTime &gt;= #{req.donateTimeBegin}
            </if>
            <if test="req.donateTimeEnd != null and req.donateTimeEnd != ''">
                AND DonateTime &lt;= #{req.donateTimeEnd}
            </if>
            <if test="req.deptName != null and req.deptName != ''">
                AND deptname = #{req.deptName}
            </if>
            <if test="req.reporterName != null and req.reporterName != ''">
                AND ReporterName LIKE CONCAT('%', #{req.reporterName}, '%')
            </if>
        </where>
        ORDER BY ReportTime DESC, id DESC
    </select>
    <select id="selectDeptNames" resultType="java.lang.String">
        SELECT DISTINCT deptname
        FROM v_donationfeestat
        WHERE deptname IS NOT NULL AND deptname != ''
        ORDER BY deptname
    </select>
</mapper>