From b1f41c066b0b7c38faffddbfe73b9be8d0bd508e Mon Sep 17 00:00:00 2001
From: yxh <172933527@qq.com>
Date: 星期日, 08 十一月 2026 23:42:09 +0800
Subject: [PATCH] 修改了增值税城建税
---
ruoyi-project/src/main/resources/mapper/project/CasePanoramaMapper.xml | 76 ++++++++
ruoyi-project/src/main/java/com/ruoyi/project/mapper/CasePanoramaMapper.java | 28 +++
ruoyi-project/src/main/java/com/ruoyi/project/service/ICasePanoramaService.java | 25 ++
ruoyi-framework/src/main/java/com/ruoyi/framework/config/JacksonDateConfig.java | 272 ++++++++++++++++++++++++++++++
ruoyi-admin/src/main/java/com/ruoyi/web/controller/project/CasePanoramaController.java | 97 ++++++++++
5 files changed, 498 insertions(+), 0 deletions(-)
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/project/CasePanoramaController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/project/CasePanoramaController.java
new file mode 100644
index 0000000..150837c
--- /dev/null
+++ b/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;
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/JacksonDateConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/JacksonDateConfig.java
new file mode 100644
index 0000000..206a4e3
--- /dev/null
+++ b/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;
+
+/**
+ * 鏃堕棿鏍煎紡鍏ㄥ眬閰嶇疆锛氭帴鍙e嚭鍙備竴寰嬬簿纭埌鍒嗭紙{@code yyyy-MM-dd HH:mm}锛夛紝鍏ュ弬瀹芥澗鍏煎甯�/涓嶅甫绉掋��
+ *
+ * <p><b>涓轰粈涔堢敤鍏ㄥ眬閰嶇疆鑰屼笉鏄�愬瓧娈垫敼娉ㄨВ锛�</b>绯荤粺閲�
+ * {@code @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")} 鏈� 182 澶勩�亄@code @DateTimeFormat} 73 澶勶紝
+ * 閫愪釜鏀规棦瀹规槗婕忥紙婕忎竴澶勫氨鏄彁浜� 400锛夛紝涔熻鐩栦笉鍒版湭鍔犳敞瑙g殑 {@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 鑷韩涔熺敤鍚屼竴鏂规硶娉ㄥ唽瀹冭嚜甯︾殑妯″潡锛圝avaTime 绛夛級锛�
+ * 涓や釜瀹氬埗鍣ㄤ簰鐩歌鐩栵紝璋佸悗鎵ц璋佺殑鍒楄〃鐢熸晥銆傚疄娴嬪悗鏋滐細鏈ā鍧楄涓㈡帀 鈥斺��
+ * 琛ㄧ幇涓�"绫诲湪鍖呴噷銆乥ean 涔熷垱寤轰簡锛屼絾 Date 鐨勫鏉捐В鏋愪笌鍘荤搴忓垪鍖栭兘涓嶈捣浣滅敤"锛�
+ * 鍓嶇鎸� {@code yyyy-MM-dd} 鎻愪氦鏃剁洿鎺� 400锛圕annot deserialize ... expected format yyyy-MM-dd HH:mm:ss锛夈��
+ *
+ * <p>鏀逛负鎶婃ā鍧楀0鏄庢垚 {@code SimpleModule} bean锛歋pring 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;
+ }
+ }
+
+ /**
+ * 鍏ュ弬瑙f瀽锛氬吋瀹瑰甫绉�/涓嶅甫绉�/绾棩鏈�/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;
+ }
+ }
+
+ /** 瀹芥澗鏃ユ湡瑙f瀽鍣� */
+ 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,
+ "鏃犳硶瑙f瀽鐨勬椂闂存牸寮忥紝鏀寔 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());
+ }
+}
diff --git a/ruoyi-project/src/main/java/com/ruoyi/project/mapper/CasePanoramaMapper.java b/ruoyi-project/src/main/java/com/ruoyi/project/mapper/CasePanoramaMapper.java
new file mode 100644
index 0000000..fa67089
--- /dev/null
+++ b/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();
+}
diff --git a/ruoyi-project/src/main/java/com/ruoyi/project/service/ICasePanoramaService.java b/ruoyi-project/src/main/java/com/ruoyi/project/service/ICasePanoramaService.java
new file mode 100644
index 0000000..42c283e
--- /dev/null
+++ b/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();
+}
diff --git a/ruoyi-project/src/main/resources/mapper/project/CasePanoramaMapper.xml b/ruoyi-project/src/main/resources/mapper/project/CasePanoramaMapper.xml
new file mode 100644
index 0000000..f1ce44a
--- /dev/null
+++ b/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 >= #{req.donateTimeBegin}
+ </if>
+ <if test="req.donateTimeEnd != null and req.donateTimeEnd != ''">
+ AND DonateTime <= #{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>
--
Gitblit v1.9.3