zzw пре 2 месеци
родитељ
комит
7b353225f0
19 измењених фајлова са 2173 додато и 361 уклоњено
  1. 28 17
      assembly/src/main/resources/application-dev.yml
  2. 8 0
      auth-server/src/main/java/com/hotent/auth/server/controller/AuthenticationRestController.java
  3. 3 0
      auth-server/src/main/java/com/hotent/auth/server/service/AuthenticationService.java
  4. 22 0
      auth-server/src/main/java/com/hotent/auth/server/service/impl/AuthenticationServiceImpl.java
  5. 3 2
      base/src/main/java/com/hotent/base/conf/WebSecurityConfig.java
  6. 30 1
      pricing/src/main/java/com/hotent/pricing/controller/base/FileUploadController.java
  7. 14 9
      pricing/src/main/java/com/hotent/pricing/controller/base/SSOLoginController.java
  8. 1398 120
      pricing/src/main/java/com/hotent/pricing/controller/report/FapReportScenicController.java
  9. 49 67
      pricing/src/main/java/com/hotent/pricing/controller/report/api/FapReportPortalController.java
  10. 25 0
      pricing/src/main/java/com/hotent/pricing/controller/report/api/IconManager.java
  11. 14 0
      pricing/src/main/java/com/hotent/pricing/dao/FapScenicFinanceDao.java
  12. 15 1
      pricing/src/main/java/com/hotent/pricing/manager/DataMiddlePlatformManager.java
  13. 183 122
      pricing/src/main/java/com/hotent/pricing/manager/impl/DataMiddlePlatformManagerImpl.java
  14. 89 0
      pricing/src/main/java/com/hotent/pricing/manager/impl/FapScenicFinanceManagerImpl.java
  15. 7 0
      pricing/src/main/java/com/hotent/pricing/model/entity/report/FapReportScenic.java
  16. 271 0
      pricing/src/main/java/com/hotent/pricing/model/entity/report/FapScenicFinance.java
  17. 2 0
      pricing/src/main/java/com/hotent/pricing/model/vo/report/PriceInfoVO.java
  18. 10 2
      pricing/src/main/java/com/hotent/pricing/util/FileUploadUtil.java
  19. 2 20
      uc/src/main/java/com/hotent/uc/manager/impl/UserManagerImpl.java

+ 28 - 17
assembly/src/main/resources/application-dev.yml

@@ -5,16 +5,15 @@ spring:
     dynamic:
       datasource:
         master:
-          username: root
-          password: root
-#          password: Buguniao@123
-          driver-class-name: com.mysql.cj.jdbc.Driver
-          url: jdbc:mysql://localhost:3306/hotent_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&allowMultiQueries=true
 #          username: root
-#          password: June-15$Password
+#          password: Buguniao@123
 #          driver-class-name: com.mysql.cj.jdbc.Driver
-#          url: jdbc:mysql://116.204.108.139:3306/hotent_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&allowMultiQueries=true
-#
+#          url: jdbc:mysql://localhost:3306/hotent_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&allowMultiQueries=true
+          username: root
+          password: Chemicals_20181101!
+          driver-class-name: com.mysql.cj.jdbc.Driver
+          url: jdbc:mysql://1.71.91.48:3306/dj_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false&maxReconnects=10&allowMultiQueries=true&rewriteBatchedStatements=true
+
           druid:
             # filters: stat
             # druid oracle 验证语句
@@ -206,15 +205,27 @@ third:
 # 上传附件基础路径配置
 system:
   attachment:
+    # Windows开发环境:使用绝对路径,确保目录存在
     path: D:/fy/uploadPath
-
-# 数据中台配置
-data:
-  middle:
-    platform:
-      base-url: http://10.7.14.236:8280/stage-api/
-      client-id: cbjsxt
-      client-secret: cbjsxt
-      public-key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMnhQ99yP-eEU2jXdQWc6j-wWbqNLqOLinEGBY11WJUCmzHiEycDXPc6-3YMOvrdAiHZcjkMCzU_eRnBLUqkcNw9nhQrCak-sTpEVlAV21LskD6KMf-6PsfttUvpXeCO5g3Hg48F_vbLKxb8s_lcvQgCpKBIpsUdYRcp_PgSg8BQIDAQAB
+#     Linux生产环境:使用绝对路径(必须以file:开头或确保目录存在)
+#    path: /root/qingzong/upload
+
+
+# OAuth2单点登录配置
+oauth2:
+  # mvue端OAuth2配置
+  mvue:
+    base-url: http://10.7.14.236:8280/stage-api/
+    client-id: cbjsxt
+    client-secret: cbjsxt
+    public-key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMnhQ99yP-eEU2jXdQWc6j-wWbqNLqOLinEGBY11WJUCmzHiEycDXPc6-3YMOvrdAiHZcjkMCzU_eRnBLUqkcNw9nhQrCak-sTpEVlAV21LskD6KMf-6PsfttUvpXeCO5g3Hg48F_vbLKxb8s_lcvQgCpKBIpsUdYRcp_PgSg8BQIDAQAB
+    redirect-uri: http://10.7.14.130:81/mvue/
+  # front端OAuth2配置
+  front:
+    base-url: http://10.7.14.236:8280/stage-api/
+    client-id: cbjsxt
+    client-secret: cbjsxt
+    public-key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMnhQ99yP-eEU2jXdQWc6j-wWbqNLqOLinEGBY11WJUCmzHiEycDXPc6-3YMOvrdAiHZcjkMCzU_eRnBLUqkcNw9nhQrCak-sTpEVlAV21LskD6KMf-6PsfttUvpXeCO5g3Hg48F_vbLKxb8s_lcvQgCpKBIpsUdYRcp_PgSg8BQIDAQAB
+    redirect-uri: http://10.7.14.130:81/front/
 
 

+ 8 - 0
auth-server/src/main/java/com/hotent/auth/server/controller/AuthenticationRestController.java

@@ -1,5 +1,6 @@
 package com.hotent.auth.server.controller;
 
+import cn.hutool.json.JSONObject;
 import com.hotent.auth.server.service.AuthenticationService;
 import com.hotent.base.annotation.ApiGroup;
 import com.hotent.base.constants.ApiGroupConsts;
@@ -196,4 +197,11 @@ public class AuthenticationRestController {
         return authenticationService.getWelcomeDage(orgId);
     }
 
+
+    @RequestMapping(value="/ztSignout", method= RequestMethod.POST, produces = {"application/json; charset=utf-8"})
+    @ApiOperation(value = "中台退出登录", httpMethod = "POST", notes = "中台调用退出本地登录")
+    public void ztSignout(@RequestBody JSONObject jsonObject) throws Exception {
+        authenticationService.ztSignout(jsonObject);
+    }
+
 }

+ 3 - 0
auth-server/src/main/java/com/hotent/auth/server/service/AuthenticationService.java

@@ -1,5 +1,6 @@
 package com.hotent.auth.server.service;
 
+import cn.hutool.json.JSONObject;
 import com.hotent.base.jwt.JwtAuthenticationRequest;
 import com.hotent.base.jwt.JwtAuthenticationResponse;
 import com.hotent.base.jwt.JwtCustomAuthenticationRequest;
@@ -282,4 +283,6 @@ public interface AuthenticationService {
      * @return  com.hotent.base.model.CommonResult<?>
      */
     CommonResult<?> getWelcomeDage(String orgId) throws AuthenticationException;
+
+    void ztSignout(JSONObject jsonObject);
 }

+ 22 - 0
auth-server/src/main/java/com/hotent/auth/server/service/impl/AuthenticationServiceImpl.java

@@ -1,6 +1,7 @@
 package com.hotent.auth.server.service.impl;
 
 import cn.hutool.crypto.digest.MD5;
+import cn.hutool.json.JSONObject;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -54,6 +55,7 @@ import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import java.io.IOException;
 import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
 
 /**
@@ -1158,4 +1160,24 @@ public class AuthenticationServiceImpl implements AuthenticationService {
         }
         return false;
     }
+
+    @Override
+    public void ztSignout(JSONObject jsonObject) {
+        logger.info("jsonObject:{}", jsonObject.toString());
+        LocalDateTime now = LocalDateTime.now();
+        // 定义日期时间格式
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        // 将 LocalDateTime 对象格式化为字符串
+        String formattedDateTime = now.format(formatter);
+        StringBuffer sb = new StringBuffer();
+        sb.append("\n");
+        sb.append("=======================start=========================\n");
+        sb.append("中台退出时间:" + formattedDateTime + "\n");
+        sb.append("中台退出数据包json:" + jsonObject.toString() + "\n");
+        sb.append("=======================end=========================\n");
+        logger.info("中台退出数据:{}", sb.toString());
+
+        String account = (String) jsonObject.get("account");//账号
+        jwtTokenHandler.removeFromCache("pc", "-1", account);
+    }
 }

+ 3 - 2
base/src/main/java/com/hotent/base/conf/WebSecurityConfig.java

@@ -193,8 +193,9 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
 				"/actuator/cert",
 				"/form/formServiceController/v1/getFormAndBoExportXml",
 				"/**/bpmRuntimeStartUp",
-				"/**/api/org/v1/pushOrgData"
-				)
+				"/**/api/org/v1/pushOrgData",
+				"/ztSignout"
+		)
 		.antMatchers(
 				HttpMethod.GET,
 				"/sso/**",

+ 30 - 1
pricing/src/main/java/com/hotent/pricing/controller/base/FileUploadController.java

@@ -17,7 +17,7 @@ import java.util.List;
 public class FileUploadController {
 
     // 由配置指定基础上传路径,与 WebMvcConfig 的静态映射一致
-    @Value("${system.attachment.path:D:/fy/uploadPath}")
+    @Value("${system.attachment.path}")
     private String uploadPath;
 
     /**
@@ -28,6 +28,9 @@ public class FileUploadController {
     public CommonResult<FileUploadResult> uploadFile(
             @RequestParam("file") MultipartFile file) {
 
+        // 确保基础上传目录存在
+        ensureUploadPathExists();
+
         FileUploadResult fileUploadResult = FileUploadUtil.uploadFile(file, "1", uploadPath);
         CommonResult<FileUploadResult> result = new CommonResult<>();
         result.value(fileUploadResult);
@@ -42,6 +45,10 @@ public class FileUploadController {
     @PostMapping("/uploads")
     public CommonResult<List<FileUploadResult>> uploadFiles(
             @RequestParam("files") MultipartFile[] files) {
+
+        // 确保基础上传目录存在
+        ensureUploadPathExists();
+
         List<FileUploadResult> fileUploadResults = FileUploadUtil.uploadFiles(Arrays.asList(files), uploadPath);
         CommonResult<List<FileUploadResult>> result = new CommonResult<>();
         result.value(fileUploadResults);
@@ -49,5 +56,27 @@ public class FileUploadController {
         return result;
     }
 
+    /**
+     * 确保上传路径存在
+     */
+    private void ensureUploadPathExists() {
+        // 打印实际读取到的路径,用于诊断
+        System.out.println("=== 文件上传路径配置 ===");
+        System.out.println("配置的uploadPath: " + uploadPath);
+
+        java.io.File uploadDir = new java.io.File(uploadPath);
+        System.out.println("File对象的绝对路径: " + uploadDir.getAbsolutePath());
+        System.out.println("是否为绝对路径: " + uploadDir.isAbsolute());
+        System.out.println("目录是否存在: " + uploadDir.exists());
+
+        if (!uploadDir.exists()) {
+            boolean created = uploadDir.mkdirs();
+            System.out.println("创建目录结果: " + created);
+            if (!created) {
+                System.err.println("警告:无法创建上传目录: " + uploadDir.getAbsolutePath());
+            }
+        }
+        System.out.println("======================");
+    }
 
 }

+ 14 - 9
pricing/src/main/java/com/hotent/pricing/controller/base/SSOLoginController.java

@@ -38,16 +38,21 @@ public class SSOLoginController {
     }
 
 
-    @GetMapping("/login")
-    @ApiOperation(value = "中台系统单点登录", notes = "中台系统单点登录")
-    public ResponseEntity<?> ssoDataMiddlePlatform(
-            @ApiParam(name="code",value="随机码", required = true)
+    @GetMapping("/mvue/login")
+    @ApiOperation(value = "mvue端单点登录", notes = "mvue端单点登录")
+    public CommonResult<?> ssoForMvue(
+            @ApiParam(name="code",value="授权码", required = true)
             @RequestParam (value="code",required=true) String code) {
-        try {
-            return dataMiddlePlatformManager.ssoDataMiddlePlatform(code);
-        } catch (Exception e) {
-            throw new RuntimeException("中台单点登录失败");
-        }
+        return dataMiddlePlatformManager.ssoForMvue(code);
+    }
+
+
+    @GetMapping("/front/login")
+    @ApiOperation(value = "front端单点登录", notes = "front端单点登录")
+    public ResponseEntity<?> ssoForFront(
+            @ApiParam(name="code",value="授权码", required = true)
+            @RequestParam (value="code",required=true) String code) {
+        return dataMiddlePlatformManager.ssoForFront(code);
     }
 
 

+ 1398 - 120
pricing/src/main/java/com/hotent/pricing/controller/report/FapReportScenicController.java

@@ -7,9 +7,13 @@ import com.hotent.base.model.CommonResult;
 import com.hotent.base.query.PageList;
 import com.hotent.base.util.AuthenticationUtil;
 import com.hotent.pricing.manager.FapReportScenicManager;
+import com.hotent.pricing.manager.FapScenicFinanceManager;
 import com.hotent.pricing.model.entity.report.FapReportScenic;
+import com.hotent.pricing.model.entity.report.FapScenicFinance;
 import com.hotent.pricing.util.ExcelUtil;
 import com.hotent.pricing.util.OrgHierarchyUtil;
+import com.hotent.sys.persistence.manager.DataDictManager;
+import com.hotent.sys.persistence.model.DataDict;
 import com.hotent.uc.manager.OrgManager;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -19,10 +23,17 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.*;
 
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFFont;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
 /**
  * 报表-景区基本信息主表 前端控制器
  *
@@ -40,6 +51,12 @@ public class FapReportScenicController{
     OrgManager orgService;
     @Autowired
     private OrgHierarchyUtil orgHierarchyUtil;
+    @Autowired
+    private FapScenicFinanceManager financeManager;
+
+    @Resource
+    private DataDictManager dataDictManager;
+
 
 
     /**
@@ -103,7 +120,7 @@ public class FapReportScenicController{
                     new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<FapReportScenic>()
                         .eq(FapReportScenic::getRefId, record.getId())
                         .eq(FapReportScenic::getIsHistory, "1")
-                        .orderByDesc(FapReportScenic::getUpdateTime)
+                        .orderByDesc(FapReportScenic::getCreateTime)
                 );
                 record.setHistoryList(historyList);
             }
@@ -209,17 +226,6 @@ public class FapReportScenicController{
     }
 
 
-
-    /**
-     * 批量删除
-     */
-    @DeleteMapping("/delete")
-    @ApiOperation("根据ID集合批量删除")
-    public CommonResult<Void> deleteBatch(@ApiParam(value = "ID集合", required = true) @RequestParam String... ids) {
-        fapReportScenicManager.removeByIds(java.util.Arrays.asList(ids));
-        return CommonResult.<Void>ok();
-    }
-
     /**
      * 启用/停用景区
      */
@@ -240,163 +246,1435 @@ public class FapReportScenicController{
     }
 
     /**
-     * 下载Excel模板(基于选中记录
+     * 下载景区价格信息统计表模板(包含景区基本信息+收支情况+游客数据
      */
     @PostMapping("/template")
     @ApiOperation("下载景区基本信息Excel模板")
     public void downloadTemplate(@ApiParam(value = "选中的记录ID", required = true) @RequestBody FapReportScenic fapReportScenic,
-                                HttpServletResponse response) throws IOException {
-        // 定义表头(只包含用户需要填写的价格字段)
-        String[] headers = {
-            "门票价格-淡季", "门票价格-旺季", "景交车", "观光车价格", "缆车价格", "索道价格", "电梯价格", "游船价格", "停车场", "其它"
-        };
-
-        // 定义字段名(只包含价格字段)
-        String[] fieldNames = {
-            "ticketLow", "ticketHigh", "price1", "price2", "price3", "price4", "price5", "price6", "price7", "price8"
-        };
-
-        // 验证选中记录是否存在
-        if (fapReportScenic.getId()==null) {
-            throw new IllegalArgumentException("请先选择要操作的记录");
-        }
-
-        FapReportScenic selectedRecord = fapReportScenicManager.getById(fapReportScenic.getId());
-        if (selectedRecord == null) {
-            throw new IllegalArgumentException("选中的记录不存在");
-        }
-
-        // 使用选中记录的数据作为模板(只包含价格字段)
-        List<Map<String, Object>> sampleData = new ArrayList<>();
-        Map<String, Object> data = new HashMap<>();
-        data.put("ticketLow", selectedRecord.getTicketLow());
-        data.put("ticketHigh", selectedRecord.getTicketHigh());
-        data.put("price1", selectedRecord.getPrice1());
-        data.put("price2", selectedRecord.getPrice2());
-        data.put("price3", selectedRecord.getPrice3());
-        data.put("price4", selectedRecord.getPrice4());
-        data.put("price5", selectedRecord.getPrice5());
-        data.put("price6", selectedRecord.getPrice6());
-        data.put("price7", selectedRecord.getPrice7());
-        data.put("price8", selectedRecord.getPrice8());
-        sampleData.add(data);
-
-        ExcelUtil.downloadTemplate(response, "景区基本信息", headers, fieldNames, sampleData);
+                                 HttpServletResponse response) throws IOException {
+
+        // 获取景区基本信息
+        FapReportScenic scenic = fapReportScenicManager.getById(fapReportScenic.getId());
+        if (scenic == null) {
+            throw new IllegalArgumentException("景区不存在");
+        }
+
+        // 创建工作簿
+        XSSFWorkbook workbook = new XSSFWorkbook();
+        XSSFSheet sheet = workbook.createSheet("景区价格信息统计表");
+
+        // 创建样式
+        CellStyle titleStyle = createTitleStyle(workbook);
+        CellStyle headerStyle = createHeaderStyle(workbook);
+        CellStyle dataStyle = createDataStyle(workbook);
+        CellStyle centerStyle = createCenterStyle(workbook);
+
+        int rowNum = 0;
+
+        // 第1行:标题
+        Row titleRow = sheet.createRow(rowNum++);
+        titleRow.setHeightInPoints(35); // 设置行高
+        Cell titleCell = titleRow.createCell(0);
+        String title = scenic.getScenicName() + "景区门票价格信息统计表";
+        titleCell.setCellValue(title);
+        titleCell.setCellStyle(titleStyle);
+        addMergedRegionWithBorder(sheet, 0, 0, 0, 19);
+
+        // 第2行:单位和填表时间
+        Row unitRow = sheet.createRow(rowNum++);
+        unitRow.setHeightInPoints(25); // 设置行高
+
+        // 单位标签(第0-1列合并)
+        Cell unitLabelCell = unitRow.createCell(0);
+        unitLabelCell.setCellValue("单位:");
+        unitLabelCell.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 0, 1);
+
+        // 单位值(第2-11列合并,空白供用户填写)
+        Cell unitValueCell = unitRow.createCell(2);
+        unitValueCell.setCellValue("");
+        unitValueCell.setCellStyle(dataStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 2, 11);
+
+        // 填表时间标签(第12-13列合并)
+        Cell dateLabelCell = unitRow.createCell(12);
+        dateLabelCell.setCellValue("填表时间:");
+        dateLabelCell.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 12, 13);
+
+        // 填表时间值(第14-19列合并,空白供用户填写)
+        Cell dateValueCell = unitRow.createCell(14);
+        dateValueCell.setCellValue("");
+        dateValueCell.setCellStyle(dataStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 14, 19);
+
+        // 第3行:景区基本信息表头
+        Row basicInfoHeaderRow = sheet.createRow(rowNum++);
+        basicInfoHeaderRow.setHeightInPoints(30); // 设置行高
+
+        // 第0列:"基本情况"标签(稍后合并第3-7行)
+        Cell basicSituationLabel = basicInfoHeaderRow.createCell(0);
+        basicSituationLabel.setCellValue("基本情况");
+        basicSituationLabel.setCellStyle(headerStyle);
+
+        basicInfoHeaderRow.createCell(1).setCellValue("景区名称");
+        basicInfoHeaderRow.createCell(2).setCellValue("景区类型");
+        basicInfoHeaderRow.createCell(3).setCellValue("景区等级");
+        basicInfoHeaderRow.createCell(4).setCellValue("价格管理形式");
+        basicInfoHeaderRow.createCell(5).setCellValue("景区面积");
+        basicInfoHeaderRow.createCell(6).setCellValue("主管部门");
+        basicInfoHeaderRow.createCell(7).setCellValue("运营性质");
+        basicInfoHeaderRow.createCell(8).setCellValue("管理属性");
+
+        // 门票收支管理方式(第9-19列合并)
+        Cell revenueManageHeader = basicInfoHeaderRow.createCell(9);
+        revenueManageHeader.setCellValue("门票收支管理方式及财政资金关系");
+        revenueManageHeader.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 9, 19);
+
+        // 第4行:景区基本信息数据
+        Row basicInfoDataRow = sheet.createRow(rowNum++);
+        basicInfoDataRow.setHeightInPoints(25); // 设置行高
+
+        basicInfoDataRow.createCell(1).setCellValue(scenic.getScenicName() != null ? scenic.getScenicName() : "");
+        basicInfoDataRow.createCell(2).setCellValue(scenic.getScenicType() != null ? scenic.getScenicType() : "");
+        basicInfoDataRow.createCell(3).setCellValue(scenic.getScenicLevel() != null ? scenic.getScenicLevel() : "");
+        basicInfoDataRow.createCell(4).setCellValue(scenic.getPriceManageForm() != null ? scenic.getPriceManageForm() : "");
+        basicInfoDataRow.createCell(5).setCellValue((scenic.getScenicArea() != null ? scenic.getScenicArea() : "") +
+                                                    (scenic.getAreaUnit() != null ? scenic.getAreaUnit() : ""));
+        basicInfoDataRow.createCell(6).setCellValue(scenic.getCompetentOrg() != null ? scenic.getCompetentOrg() : "");
+        // 运营性质
+        DataDict operationNatureDict = dataDictManager.getByDictKey("1965686172473999360", scenic.getOperationNature());
+        basicInfoDataRow.createCell(7).setCellValue(operationNatureDict.getName());
+        DataDict managementAttrDict = dataDictManager.getByDictKey("1965686172473999360", scenic.getManagementAttr());
+        basicInfoDataRow.createCell(8).setCellValue(managementAttrDict.getName());
+
+        // 门票收支管理方式(第9-19列合并)
+        Cell revenueManageData = basicInfoDataRow.createCell(9);
+        DataDict revenueManageDict = dataDictManager.getByDictKey("1965686172473999360", scenic.getRevenueManage());
+        revenueManageData.setCellValue(revenueManageDict.getName());
+        revenueManageData.setCellStyle(dataStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 9, 19);
+
+        // 设置前8列样式
+        for (int i = 1; i <= 8; i++) {
+            basicInfoDataRow.getCell(i).setCellStyle(dataStyle);
+        }
+
+        // 第5行:门票价格第一行表头
+        Row priceHeaderRow1 = sheet.createRow(rowNum++);
+        priceHeaderRow1.setHeightInPoints(30); // 设置行高
+
+        // 调整前门票价格(合并第5行的1-3列)
+        Cell beforePriceHeader = priceHeaderRow1.createCell(1);
+        beforePriceHeader.setCellValue("调整前门票价格(元/人)");
+        beforePriceHeader.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 1, 3);
+
+        // 调整后门票价格(合并第5行的4-6列)
+        Cell afterPriceHeader = priceHeaderRow1.createCell(4);
+        afterPriceHeader.setCellValue("调整后门票价格(元/人)");
+        afterPriceHeader.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 4, 6);
+
+        // 调整幅度(合并第5行的7-8列)
+        Cell adjustHeader = priceHeaderRow1.createCell(7);
+        adjustHeader.setCellValue("调整幅度(元/人)");
+        adjustHeader.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 7, 8);
+
+        // 景区内交通运输服务价格(合并第5行的9-16列)
+        Cell transportHeader = priceHeaderRow1.createCell(9);
+        transportHeader.setCellValue("景区内交通运输服务价格(元/人)");
+        transportHeader.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 9, 16);
+
+        // 成本监审、成本调查、价格评估结果(合并第5行的17-18列)
+        Cell costAuditHeader5 = priceHeaderRow1.createCell(17);
+        costAuditHeader5.setCellValue("成本监审、成本调查、价格评估结果");
+        costAuditHeader5.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum, 17, 18);
+
+        // 定价文件(合并第5行的19列,跨2行)
+        Cell pricingDocHeader5 = priceHeaderRow1.createCell(19);
+        pricingDocHeader5.setCellValue("定价文件");
+        pricingDocHeader5.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum, 19, 19);
+
+        // 第6行:基本情况第二行表头
+        Row priceHeaderRow2 = sheet.createRow(rowNum++);
+        priceHeaderRow2.setHeightInPoints(25); // 设置行高
+
+        priceHeaderRow2.createCell(1).setCellValue("旺季");
+        priceHeaderRow2.createCell(2).setCellValue("淡季");
+        priceHeaderRow2.createCell(3).setCellValue("执行时间");
+        priceHeaderRow2.createCell(4).setCellValue("旺季");
+        priceHeaderRow2.createCell(5).setCellValue("淡季");
+        priceHeaderRow2.createCell(6).setCellValue("执行时间");
+        priceHeaderRow2.createCell(7).setCellValue("旺季");
+        priceHeaderRow2.createCell(8).setCellValue("淡季");
+
+        // 交通工具子表头
+        String[] transportHeaders = {"景交车", "观光车", "缆车", "索道", "电梯", "游船", "停车场", "其他"};
+        for (int i = 0; i < transportHeaders.length; i++) {
+            priceHeaderRow2.createCell(9 + i).setCellValue(transportHeaders[i]);
+        }
+
+        // 设置第6行表头样式
+        for (int i = 1; i <= 16; i++) {
+            if (priceHeaderRow2.getCell(i) != null) {
+                priceHeaderRow2.getCell(i).setCellStyle(headerStyle);
+            }
+        }
+
+        // 第7行:门票价格、交通价格、成本监审、定价文件
+        Row priceDataRow = sheet.createRow(rowNum++);
+        priceDataRow.setHeightInPoints(25); // 设置行高
+
+        // 合并"基本情况"标签(第0列,第3-7行,跨5行)
+        addMergedRegionWithBorder(sheet, rowNum - 5, rowNum - 1, 0, 0);
+
+        // 调整前门票价格
+        priceDataRow.createCell(1).setCellValue(scenic.getTicketHigh() != null ? scenic.getTicketHigh() : "");
+        priceDataRow.createCell(2).setCellValue(scenic.getTicketLow() != null ? scenic.getTicketLow() : "");
+        priceDataRow.createCell(3).setCellValue(scenic.getExecuteTime() != null ? scenic.getExecuteTime().toString() : "");
+
+        // 调整后门票价格
+        priceDataRow.createCell(4).setCellValue(scenic.getTicketHighAdjust() != null ? scenic.getTicketHighAdjust() : "");
+        priceDataRow.createCell(5).setCellValue(scenic.getTicketLowAdjust() != null ? scenic.getTicketLowAdjust() : "");
+        priceDataRow.createCell(6).setCellValue(scenic.getExecuteTimeAdjust() != null ? scenic.getExecuteTimeAdjust().toString() : "");
+
+        // 调整幅度(计算百分比)
+        String highAdjust = "";
+        String lowAdjust = "";
+        try {
+            if (scenic.getTicketHigh() != null && scenic.getTicketHighAdjust() != null) {
+                double before = Double.parseDouble(scenic.getTicketHigh());
+                double after = Double.parseDouble(scenic.getTicketHighAdjust());
+                if (before != 0) {
+                    double percentage = ((after - before) / before) * 100;
+                    highAdjust = String.format("%.2f%%", percentage);
+                }
+            }
+            if (scenic.getTicketLow() != null && scenic.getTicketLowAdjust() != null) {
+                double before = Double.parseDouble(scenic.getTicketLow());
+                double after = Double.parseDouble(scenic.getTicketLowAdjust());
+                if (before != 0) {
+                    double percentage = ((after - before) / before) * 100;
+                    lowAdjust = String.format("%.2f%%", percentage);
+                }
+            }
+        } catch (NumberFormatException e) {
+            // 忽略格式错误
+        }
+        priceDataRow.createCell(7).setCellValue(highAdjust);
+        priceDataRow.createCell(8).setCellValue(lowAdjust);
+
+        // 交通价格(第9-16列)
+        priceDataRow.createCell(9).setCellValue(scenic.getPrice1() != null ? scenic.getPrice1() : "");
+        priceDataRow.createCell(10).setCellValue(scenic.getPrice2() != null ? scenic.getPrice2() : "");
+        priceDataRow.createCell(11).setCellValue(scenic.getPrice3() != null ? scenic.getPrice3() : "");
+        priceDataRow.createCell(12).setCellValue(scenic.getPrice4() != null ? scenic.getPrice4() : "");
+        priceDataRow.createCell(13).setCellValue(scenic.getPrice5() != null ? scenic.getPrice5() : "");
+        priceDataRow.createCell(14).setCellValue(scenic.getPrice6() != null ? scenic.getPrice6() : "");
+        priceDataRow.createCell(15).setCellValue(scenic.getPrice7() != null ? scenic.getPrice7() : "");
+        priceDataRow.createCell(16).setCellValue(scenic.getPrice8() != null ? scenic.getPrice8() : "");
+
+        // 成本监审、成本调查、价格评估结果(第17-18列合并)
+        Cell costResultCell = priceDataRow.createCell(17);
+        costResultCell.setCellValue(scenic.getCostAuditResult() != null ? scenic.getCostAuditResult() : "");
+        costResultCell.setCellStyle(dataStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 17, 18);
+
+        // 定价文件(第19列)
+        Cell pricingDocCell = priceDataRow.createCell(19);
+        pricingDocCell.setCellValue(scenic.getPricingDocNo() != null ? scenic.getPricingDocNo() : "");
+        pricingDocCell.setCellStyle(dataStyle);
+
+        // 设置数据行样式
+        for (int i = 1; i <= 16; i++) {
+            if (priceDataRow.getCell(i) != null) {
+                priceDataRow.getCell(i).setCellStyle(dataStyle);
+            }
+        }
+
+        // 第8-9行:收支情况表头
+        Row financeHeaderRow1 = sheet.createRow(rowNum++);
+        financeHeaderRow1.setHeightInPoints(30); // 设置行高
+        Row financeHeaderRow2 = sheet.createRow(rowNum++);
+        financeHeaderRow2.setHeightInPoints(25); // 设置行高
+
+        // 第一列:收支情况标签(跨3行)
+        Cell financeLabelCell = financeHeaderRow1.createCell(0);
+        financeLabelCell.setCellValue("收支情况");
+        financeLabelCell.setCellStyle(headerStyle);
+
+        Cell fh1 = financeHeaderRow1.createCell(1);
+        fh1.setCellValue("员工人数");
+        fh1.setCellStyle(headerStyle);
+
+        Cell fh2 = financeHeaderRow1.createCell(2);
+        fh2.setCellValue("总收入(万元)");
+        fh2.setCellStyle(headerStyle);
+
+        Cell fh3 = financeHeaderRow1.createCell(7);
+        fh3.setCellValue("总支出(万元)");
+        fh3.setCellStyle(headerStyle);
+
+        Cell fh4 = financeHeaderRow1.createCell(13);
+        fh4.setCellValue("利润(万元)");
+        fh4.setCellStyle(headerStyle);
+
+        Cell fh5 = financeHeaderRow1.createCell(14);
+        fh5.setCellValue("税金(万元)");
+        fh5.setCellStyle(headerStyle);
+
+        Cell fh6 = financeHeaderRow1.createCell(15);
+        fh6.setCellValue("税率(%)");
+        fh6.setCellStyle(headerStyle);
+
+        Cell fh7 = financeHeaderRow1.createCell(16);
+        fh7.setCellValue("备注");
+        fh7.setCellStyle(headerStyle);
+
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 1, 1, 1); // 员工人数
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 2, 2, 6); // 总收入
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 2, 7, 12); // 总支出
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 1, 13, 13); // 利润
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 1, 14, 14); // 税金
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 1, 15, 15); // 税率
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 1, 16, 19); // 备注(合并到第19列)
+
+        String[] revenueHeaders = {"合计", "门票", "经营服务", "财政拨款", "其他"};
+        for (int i = 0; i < revenueHeaders.length; i++) {
+            Cell rhCell = financeHeaderRow2.createCell(i + 2);
+            rhCell.setCellValue(revenueHeaders[i]);
+            rhCell.setCellStyle(headerStyle);
+        }
+
+        String[] expenseHeaders = {"合计", "上缴财政", "人员", "办公", "建设维护", "其他"};
+        for (int i = 0; i < expenseHeaders.length; i++) {
+            Cell ehCell = financeHeaderRow2.createCell(i + 7);
+            ehCell.setCellValue(expenseHeaders[i]);
+            ehCell.setCellStyle(headerStyle);
+        }
+
+        // 第10行:收支情况数据
+        Row financeDataRow1 = sheet.createRow(rowNum++);
+        financeDataRow1.setHeightInPoints(25); // 设置行高
+
+        // 合并收支情况标签列(跨3行)- 第8-10行
+        addMergedRegionWithBorder(sheet, rowNum - 3, rowNum - 1, 0, 0);
+
+        // 第10行收支数据:空白供用户填写
+        for (int i = 1; i <= 15; i++) {
+            Cell fdCell = financeDataRow1.createCell(i);
+            fdCell.setCellValue("");
+            fdCell.setCellStyle(dataStyle);
+        }
+
+        // 备注(第16-19列合并)
+        Cell fd16 = financeDataRow1.createCell(16);
+        fd16.setCellValue("");
+        fd16.setCellStyle(dataStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 16, 19);
+
+        // ========== 第11-12行:年月度游客人数表格 ==========
+        Row monthlyVisitorHeaderRow = sheet.createRow(rowNum++);
+        monthlyVisitorHeaderRow.setHeightInPoints(25);
+        Row monthlyVisitorDataRow = sheet.createRow(rowNum++);
+        monthlyVisitorDataRow.setHeightInPoints(25);
+
+        // 第一列:标签(跨2行)
+        Cell monthlyLabel = monthlyVisitorHeaderRow.createCell(0);
+        monthlyLabel.setCellValue("年月度游客人数(人)");
+        monthlyLabel.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 1, 0, 0);
+
+        // 第13行:月度表头(1-12月)
+        String[] monthlyHeaders = {"1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"};
+        for (int i = 0; i < monthlyHeaders.length; i++) {
+            Cell mhCell = monthlyVisitorHeaderRow.createCell(i + 1);
+            mhCell.setCellValue(monthlyHeaders[i]);
+            mhCell.setCellStyle(headerStyle);
+        }
+
+        // 本年度人数合计
+        Cell totalHeader = monthlyVisitorHeaderRow.createCell(13);
+        totalHeader.setCellValue("本年度人数合计");
+        totalHeader.setCellStyle(headerStyle);
+
+        // 优惠人数
+        Cell discountHeader = monthlyVisitorHeaderRow.createCell(14);
+        discountHeader.setCellValue("优惠人数");
+        discountHeader.setCellStyle(headerStyle);
+
+        // 其中免票人数
+        Cell freeHeader = monthlyVisitorHeaderRow.createCell(15);
+        freeHeader.setCellValue("其中免票人数");
+        freeHeader.setCellStyle(headerStyle);
+
+        // 免票人数占比(第16-19列合并)
+        Cell monthlyFreeRateHeader = monthlyVisitorHeaderRow.createCell(16);
+        monthlyFreeRateHeader.setCellValue("免票人数占比(%)");
+        monthlyFreeRateHeader.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 2, 16, 19);
+
+        // 第14行:月度游客数据(空白,用户填写)
+        for (int i = 1; i <= 19; i++) {
+            Cell mvdCell = monthlyVisitorDataRow.createCell(i);
+            mvdCell.setCellValue("");
+            mvdCell.setCellStyle(dataStyle);
+        }
+        // 合并免票占比列
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 16, 19);
+
+        // ========== 第13行:备注说明 ==========
+        Row remarkRow = sheet.createRow(rowNum++);
+        remarkRow.setHeightInPoints(60); // 增加行高以容纳三条备注
+
+        // 创建左对齐、自动换行样式
+        CellStyle remarkStyle = workbook.createCellStyle();
+        remarkStyle.setAlignment(HorizontalAlignment.LEFT);
+        remarkStyle.setVerticalAlignment(VerticalAlignment.TOP);
+        remarkStyle.setWrapText(true); // 启用自动换行
+        XSSFFont remarkFont = workbook.createFont();
+        remarkFont.setFontName("宋体");
+        remarkFont.setFontHeightInPoints((short) 10);
+        remarkStyle.setFont(remarkFont);
+
+        // 三条备注合并在一个单元格中
+        Cell remarkCell = remarkRow.createCell(0);
+        remarkCell.setCellValue("备注:1、表格中'现行门票价格'指定价文件中的价格开始执行时间,非临时优惠价格执行时间;\n" +
+                               "      2、景区面积填写'平方公里'或'平方米'、'亩';\n" +
+                               "      3、税率指企业所得税税率。");
+        remarkCell.setCellStyle(remarkStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 0, 19);
+
+        // ========== 第14行:填表信息 ==========
+        Row fillInfoRow = sheet.createRow(rowNum++);
+        fillInfoRow.setHeightInPoints(20);
+
+        // 创建填表信息样式
+        CellStyle fillInfoStyle = workbook.createCellStyle();
+        fillInfoStyle.setAlignment(HorizontalAlignment.LEFT);
+        fillInfoStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        XSSFFont fillInfoFont = workbook.createFont();
+        fillInfoFont.setFontName("宋体");
+        fillInfoFont.setFontHeightInPoints((short) 10);
+        fillInfoStyle.setFont(fillInfoFont);
+
+        // 景区门票价格工作负责人标签(第0列)
+        Cell responsiblePersonLabel = fillInfoRow.createCell(0);
+        responsiblePersonLabel.setCellValue("景区门票价格工作负责人:");
+        responsiblePersonLabel.setCellStyle(fillInfoStyle);
+
+        // 景区门票价格工作负责人值(第1-9列合并,空白供用户填写)
+        Cell responsiblePersonValue = fillInfoRow.createCell(1);
+        responsiblePersonValue.setCellValue("");
+        responsiblePersonValue.setCellStyle(dataStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 1, 9);
+
+        // 联系电话标签(第10列)
+        Cell contactPhoneLabel = fillInfoRow.createCell(10);
+        contactPhoneLabel.setCellValue("联系电话:");
+        contactPhoneLabel.setCellStyle(fillInfoStyle);
+
+        // 联系电话值(第11-19列合并,空白供用户填写)
+        Cell contactPhoneValue = fillInfoRow.createCell(11);
+        contactPhoneValue.setCellValue("");
+        contactPhoneValue.setCellStyle(dataStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 11, 19);
+
+        // 设置列宽
+        for (int i = 0; i < 20; i++) {
+            sheet.setColumnWidth(i, 4000);
+        }
+
+        // 设置响应头
+        String fileName = scenic.getScenicName() + "_价格信息统计表.xlsx";
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setHeader("Content-Disposition", "attachment; filename=" +
+            new String(fileName.getBytes("UTF-8"), "ISO-8859-1"));
+
+        // 输出到响应流
+        workbook.write(response.getOutputStream());
+        workbook.close();
     }
 
     /**
-     * 导入Excel数据(更新选中记录)
+     * 导入景区收支情况Excel(生成新的调整记录)
      */
     @PostMapping("/import")
-    @ApiOperation("导入景区基本信息Excel数据")
+    @ApiOperation("导入景区价格和收支情况Excel")
     public CommonResult<String> importExcel(@ApiParam(value = "Excel文件", required = true) @RequestParam("file") MultipartFile file,
-                                          @ApiParam(value = "选中的记录ID", required = true) @RequestParam("id") String selectedId) {
+                                            @ApiParam(value = "选中的记录ID", required = true) @RequestParam("id") String id) {
         try {
-            // 定义表头(只包含用户需要填写的价格字段)
-            String[] headers = {
-                "门票价格-淡季", "门票价格-旺季", "景交车", "观光车价格", "缆车价格", "索道价格", "电梯价格", "游船价格", "停车场", "其它"
-            };
-
-            // 定义字段名(只包含价格字段)
-            String[] fieldNames = {
-                "ticketLow", "ticketHigh", "price1", "price2", "price3", "price4", "price5", "price6", "price7", "price8"
-            };
-
-            // 验证选中记录是否存在
-            if (selectedId == null || selectedId.trim().isEmpty()) {
-                return CommonResult.<String>error().message("请先选择要更新的记录");
+            // 验证景区是否存在
+            FapReportScenic scenic = fapReportScenicManager.getById(id);
+            if (scenic == null) {
+                return CommonResult.<String>error().message("景区不存在");
             }
 
-            FapReportScenic selectedRecord = fapReportScenicManager.getById(selectedId);
-            if (selectedRecord == null) {
-                return CommonResult.<String>error().message("选中的记录不存在");
-            }
+            // 使用POI解析Excel
+            // 模板结构(14行):
+            // 第1行: 标题
+            // 第2行: 单位和填表时间
+            // 第3-4行: 基本情况(不导入,保持原值)
+            // 第5-7行: 价格信息(不导入,保持原值)
+            // 第8-10行: 收支情况(第10行导入) ✅
+            // 第11-12行: 年月度游客人数(第12行导入) ✅
+            // 第13行: 备注说明
+            // 第14行: 填表信息
+            XSSFWorkbook workbook = new XSSFWorkbook(file.getInputStream());
+            XSSFSheet sheet = workbook.getSheetAt(0);
 
-            // 解析Excel文件
-            List<Map<String, Object>> dataList = ExcelUtil.parseExcel(file, headers, fieldNames);
+            // 第10行(索引9)是收支数据行
+            Row financeDataRow = sheet.getRow(9);
+            if (financeDataRow == null) {
+                return CommonResult.<String>error().message("Excel格式错误:找不到收支数据行(第10行)");
+            }
 
-            if (dataList.isEmpty()) {
-                return CommonResult.<String>error().message("Excel文件中没有有效数据");
+            // 第12行(索引11)是2024年月度游客数据行
+            Row monthlyVisitorDataRow = sheet.getRow(11);
+            if (monthlyVisitorDataRow == null) {
+                return CommonResult.<String>error().message("Excel格式错误:找不到2024年月度游客数据行(第12行)");
             }
 
-            Map<String, Object> data = dataList.get(0);
+            // ========== 1. 创建新的收支记录 ==========
+            FapScenicFinance finance = new FapScenicFinance();
+            finance.setScenicId(id);
+            finance.setYear(java.time.LocalDate.now().getYear());
 
-            // 先保存历史记录
-            if ("0".equals(selectedRecord.getIsHistory())) {
-                FapReportScenic historyRecord = new FapReportScenic();
-                BeanUtils.copyProperties(selectedRecord, historyRecord);
-                historyRecord.setId(null);
-                historyRecord.setIsHistory("1");
-                historyRecord.setRefId(selectedRecord.getId());
-                // 保存历史记录
-                fapReportScenicManager.save(historyRecord);
+            // 解析填报信息
+            // 第2行(索引1)单位和填表时间
+            Row unitRow = sheet.getRow(1);
+            if (unitRow != null) {
+                // 单位名称(第2-11列合并,取第2列的值)
+                finance.setUnitName(getCellStringValue(unitRow.getCell(2)));
+                // 填表时间(第14-19列合并,取第14列的值)
+                finance.setFillDate(getCellLocalDateValue(unitRow.getCell(14)));
+            }
+
+            // 第14行(索引13)是填表信息行
+            Row fillInfoRow = sheet.getRow(13);
+            if (fillInfoRow != null) {
+                // 景区门票价格工作负责人(第1-9列合并,取第1列的值)
+                finance.setResponsiblePerson(getCellStringValue(fillInfoRow.getCell(1)));
+                // 联系电话(第11-19列合并,取第11列的值)
+                finance.setContactPhone(getCellStringValue(fillInfoRow.getCell(11)));
             }
 
-            // 更新当前记录(只更新价格字段)
-            updatePriceFieldsFromData(selectedRecord, data);
-            fapReportScenicManager.updateById(selectedRecord);
+            // 解析收支数据(第10行)
+            finance.setEmployeeCount(getCellIntValue(financeDataRow.getCell(1)));
+            finance.setRevenueTotal(getCellBigDecimalValue(financeDataRow.getCell(2)));
+            finance.setRevenueTicket(getCellBigDecimalValue(financeDataRow.getCell(3)));
+            finance.setRevenueService(getCellBigDecimalValue(financeDataRow.getCell(4)));
+            finance.setRevenueFiscal(getCellBigDecimalValue(financeDataRow.getCell(5)));
+            finance.setRevenueOther(getCellBigDecimalValue(financeDataRow.getCell(6)));
+            finance.setExpenseTotal(getCellBigDecimalValue(financeDataRow.getCell(7)));
+            finance.setExpenseFiscalPayment(getCellBigDecimalValue(financeDataRow.getCell(8)));
+            finance.setExpensePersonnel(getCellBigDecimalValue(financeDataRow.getCell(9)));
+            finance.setExpenseOffice(getCellBigDecimalValue(financeDataRow.getCell(10)));
+            finance.setExpenseMaintenance(getCellBigDecimalValue(financeDataRow.getCell(11)));
+            finance.setExpenseOther(getCellBigDecimalValue(financeDataRow.getCell(12)));
+            finance.setProfit(getCellBigDecimalValue(financeDataRow.getCell(13)));
+            finance.setTax(getCellBigDecimalValue(financeDataRow.getCell(14)));
+            finance.setTaxRate(getCellBigDecimalValue(financeDataRow.getCell(15)));
+            finance.setRemark(getCellStringValue(financeDataRow.getCell(16)));
+
+            // 解析2024年月度游客数据(第12行,用户填写的新数据)
+            finance.setVisitorJan(getCellIntValue(monthlyVisitorDataRow.getCell(1)));
+            finance.setVisitorFeb(getCellIntValue(monthlyVisitorDataRow.getCell(2)));
+            finance.setVisitorMar(getCellIntValue(monthlyVisitorDataRow.getCell(3)));
+            finance.setVisitorApr(getCellIntValue(monthlyVisitorDataRow.getCell(4)));
+            finance.setVisitorMay(getCellIntValue(monthlyVisitorDataRow.getCell(5)));
+            finance.setVisitorJun(getCellIntValue(monthlyVisitorDataRow.getCell(6)));
+            finance.setVisitorJul(getCellIntValue(monthlyVisitorDataRow.getCell(7)));
+            finance.setVisitorAug(getCellIntValue(monthlyVisitorDataRow.getCell(8)));
+            finance.setVisitorSep(getCellIntValue(monthlyVisitorDataRow.getCell(9)));
+            finance.setVisitorOct(getCellIntValue(monthlyVisitorDataRow.getCell(10)));
+            finance.setVisitorNov(getCellIntValue(monthlyVisitorDataRow.getCell(11)));
+            finance.setVisitorDec(getCellIntValue(monthlyVisitorDataRow.getCell(12)));
+            finance.setVisitorTotal(getCellIntValue(monthlyVisitorDataRow.getCell(13)));
+            finance.setVisitorDiscount(getCellIntValue(monthlyVisitorDataRow.getCell(14)));
+            finance.setVisitorFree(getCellIntValue(monthlyVisitorDataRow.getCell(15)));
+            finance.setVisitorFreeRate(getCellBigDecimalValue(monthlyVisitorDataRow.getCell(16)));
+
+            workbook.close();
 
-            return CommonResult.<String>ok().value("成功更新价格记录");
+            // 导入时直接保存,不创建历史记录
+            financeManager.save(finance);
+
+            return CommonResult.<String>ok().message("成功导入价格和收支记录");
 
         } catch (Exception e) {
             return CommonResult.<String>error().message("导入失败: " + e.getMessage());
         }
     }
 
+    /**
+     * 获取单元格整数值
+     */
+    private Integer getCellIntValue(Cell cell) {
+        if (cell == null) {
+            return null;
+        }
+        switch (cell.getCellType()) {
+            case NUMERIC:
+                return (int) cell.getNumericCellValue();
+            case STRING:
+                String str = cell.getStringCellValue().trim();
+                return str.isEmpty() ? null : Integer.parseInt(str);
+            default:
+                return null;
+        }
+    }
 
     /**
-     * 只更新价格字段
+     * 获取单元格BigDecimal值
      */
-    private void updatePriceFieldsFromData(FapReportScenic record, Map<String, Object> data) {
-        Object ticketLow = data.get("ticketLow");
-        if (ticketLow != null) {
-            record.setTicketLow(ticketLow.toString());
+    private java.math.BigDecimal getCellBigDecimalValue(Cell cell) {
+        if (cell == null) {
+            return null;
+        }
+        switch (cell.getCellType()) {
+            case NUMERIC:
+                return new java.math.BigDecimal(cell.getNumericCellValue());
+            case STRING:
+                String str = cell.getStringCellValue().trim();
+                return str.isEmpty() ? null : new java.math.BigDecimal(str);
+            default:
+                return null;
         }
+    }
 
-        Object ticketHigh = data.get("ticketHigh");
-        if (ticketHigh != null) {
-            record.setTicketHigh(ticketHigh.toString());
+    /**
+     * 获取单元格字符串值
+     */
+    private String getCellStringValue(Cell cell) {
+        if (cell == null) {
+            return null;
+        }
+        switch (cell.getCellType()) {
+            case NUMERIC:
+                return String.valueOf(cell.getNumericCellValue());
+            case STRING:
+                return cell.getStringCellValue();
+            default:
+                return null;
         }
+    }
 
-        Object price1 = data.get("price1");
-        if (price1 != null) {
-            record.setPrice1(price1.toString());
+    /**
+     * 获取单元格LocalDate值
+     */
+    private java.time.LocalDate getCellLocalDateValue(Cell cell) {
+        if (cell == null) {
+            return null;
+        }
+        switch (cell.getCellType()) {
+            case NUMERIC:
+                // 如果是日期格式
+                if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) {
+                    return cell.getLocalDateTimeCellValue().toLocalDate();
+                }
+                return null;
+            case STRING:
+                String str = cell.getStringCellValue().trim();
+                if (str.isEmpty()) {
+                    return null;
+                }
+                try {
+                    // 尝试解析常见日期格式
+                    return java.time.LocalDate.parse(str);
+                } catch (Exception e) {
+                    return null;
+                }
+            default:
+                return null;
         }
+    }
+
+    // ==================== 景区收支情况相关接口 ====================
+
 
-        Object price2 = data.get("price2");
-        if (price2 != null) {
-            record.setPrice2(price2.toString());
+    /**
+     * 获取景区收支调整记录列表(所有历史记录)
+     * 返回包含景区价格信息和收支情况的完整列表
+     */
+    @GetMapping("/finance/list")
+    @ApiOperation("获取景区收支调整记录列表")
+    public CommonResult<List<Map<String, Object>>> getFinanceList(
+            @ApiParam(value = "景区ID", required = true) @RequestParam String id) {
+
+        // 获取景区基本信息
+        FapReportScenic scenic = fapReportScenicManager.getById(id);
+        if (scenic == null) {
+            return CommonResult.<List<Map<String, Object>>>error().message("景区不存在");
         }
 
-        Object price3 = data.get("price3");
-        if (price3 != null) {
-            record.setPrice3(price3.toString());
+        // 获取收支调整记录列表
+        LambdaQueryWrapper<FapScenicFinance> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(FapScenicFinance::getScenicId, id);
+        wrapper.orderByDesc(FapScenicFinance::getCreateTime);
+        List<FapScenicFinance> financeList = financeManager.list(wrapper);
+
+        // 组合数据:景区价格信息 + 收支情况
+        List<Map<String, Object>> resultList = new ArrayList<>();
+        for (FapScenicFinance finance : financeList) {
+            Map<String, Object> item = new HashMap<>();
+
+            // 基本信息
+            item.put("id", finance.getId());
+            item.put("scenicId", finance.getScenicId());
+            item.put("scenicName", scenic.getScenicName());
+            item.put("year", finance.getYear());
+            item.put("createTime", finance.getCreateTime());
+
+            // 门票价格(来自景区表)
+            item.put("ticketLow", scenic.getTicketLow());
+            item.put("ticketHigh", scenic.getTicketHigh());
+            item.put("ticketLowAdjust", scenic.getTicketLowAdjust());
+            item.put("ticketHighAdjust", scenic.getTicketHighAdjust());
+
+            // 交通价格(来自景区表)
+            item.put("price1", scenic.getPrice1()); // 景交车
+            item.put("price2", scenic.getPrice2()); // 观光车
+            item.put("price3", scenic.getPrice3()); // 缆车
+            item.put("price4", scenic.getPrice4()); // 索道
+            item.put("price5", scenic.getPrice5()); // 电梯
+            item.put("price6", scenic.getPrice6()); // 游船
+            item.put("price7", scenic.getPrice7()); // 停车场
+            item.put("price8", scenic.getPrice8()); // 其他
+
+            // 收支情况(来自收支表)
+            item.put("employeeCount", finance.getEmployeeCount());
+            item.put("revenueTotal", finance.getRevenueTotal());
+            item.put("expenseTotal", finance.getExpenseTotal());
+            item.put("profit", finance.getProfit());
+            item.put("remark", finance.getRemark());
+
+            resultList.add(item);
         }
 
-        Object price4 = data.get("price4");
-        if (price4 != null) {
-            record.setPrice4(price4.toString());
+        return CommonResult.<List<Map<String, Object>>>ok().value(resultList);
+    }
+
+    /**
+     * 获取收支调整记录详情(包含景区基础信息)
+     */
+    @GetMapping("/finance/detail")
+    @ApiOperation("获取收支调整记录详情")
+    public CommonResult<Map<String, Object>> getFinanceDetail(
+            @ApiParam(value = "调整记录ID", required = true) @RequestParam String id) {
+
+        FapScenicFinance finance = financeManager.getById(id);
+        if (finance == null) {
+            return CommonResult.<Map<String, Object>>error().message("调整记录不存在");
+        }
+
+        // 获取景区基础信息
+        FapReportScenic scenic = fapReportScenicManager.getById(finance.getScenicId());
+        if (scenic == null) {
+            return CommonResult.<Map<String, Object>>error().message("景区不存在");
+        }
+
+        // 组合数据
+        Map<String, Object> result = new HashMap<>();
+
+        // 基本信息
+        result.put("id", finance.getId());
+        result.put("scenicId", finance.getScenicId());
+        result.put("scenicName", scenic.getScenicName());
+        result.put("year", finance.getYear());
+        result.put("createTime", finance.getCreateTime());
+
+        // 填报信息
+        result.put("unitName", finance.getUnitName());
+        result.put("fillDate", finance.getFillDate());
+        result.put("responsiblePerson", finance.getResponsiblePerson());
+        result.put("contactPhone", finance.getContactPhone());
+
+        // 景区基础信息
+        result.put("scenicStatus", scenic.getScenicStatus());
+        result.put("scenicLevel", scenic.getScenicLevel());
+        result.put("resourceType", scenic.getResourceType());
+        result.put("scenicType", scenic.getScenicType());
+        result.put("scenicLocality", scenic.getScenicLocality());
+        result.put("competentOrg", scenic.getCompetentOrg());
+
+        // 字典值转换为中文
+        result.put("operationNature", scenic.getOperationNature());
+        DataDict operationNatureDict = dataDictManager.getByDictKey("1965686172473999360", scenic.getOperationNature());
+        result.put("operationNatureName", operationNatureDict != null ? operationNatureDict.getName() : "");
+
+        result.put("managementAttr", scenic.getManagementAttr());
+        DataDict managementAttrDict = dataDictManager.getByDictKey("1965686172473999360", scenic.getManagementAttr());
+        result.put("managementAttrName", managementAttrDict != null ? managementAttrDict.getName() : "");
+
+        result.put("scenicArea", scenic.getScenicArea());
+        result.put("areaUnit", scenic.getAreaUnit());
+        result.put("priceManageForm", scenic.getPriceManageForm());
+
+        result.put("revenueManage", scenic.getRevenueManage());
+        DataDict revenueManageDict = dataDictManager.getByDictKey("1965686172473999360", scenic.getRevenueManage());
+        result.put("revenueManageName", revenueManageDict != null ? revenueManageDict.getName() : "");
+
+        // 门票价格
+        result.put("ticketLow", scenic.getTicketLow());
+        result.put("ticketHigh", scenic.getTicketHigh());
+        result.put("executeTime", scenic.getExecuteTime());
+        result.put("ticketLowAdjust", scenic.getTicketLowAdjust());
+        result.put("ticketHighAdjust", scenic.getTicketHighAdjust());
+        result.put("executeTimeAdjust", scenic.getExecuteTimeAdjust());
+
+        // 交通价格
+        result.put("price1", scenic.getPrice1()); // 景交车
+        result.put("price2", scenic.getPrice2()); // 观光车
+        result.put("price3", scenic.getPrice3()); // 缆车
+        result.put("price4", scenic.getPrice4()); // 索道
+        result.put("price5", scenic.getPrice5()); // 电梯
+        result.put("price6", scenic.getPrice6()); // 游船
+        result.put("price7", scenic.getPrice7()); // 停车场
+        result.put("price8", scenic.getPrice8()); // 其他
+
+        // 成本监审和定价文件
+        result.put("costAuditResult", scenic.getCostAuditResult());
+        result.put("pricingDocNo", scenic.getPricingDocNo());
+
+        // 收支情况
+        result.put("employeeCount", finance.getEmployeeCount());
+        result.put("revenueTotal", finance.getRevenueTotal());
+        result.put("revenueTicket", finance.getRevenueTicket());
+        result.put("revenueService", finance.getRevenueService());
+        result.put("revenueFiscal", finance.getRevenueFiscal());
+        result.put("revenueOther", finance.getRevenueOther());
+        result.put("expenseTotal", finance.getExpenseTotal());
+        result.put("expenseFiscalPayment", finance.getExpenseFiscalPayment());
+        result.put("expensePersonnel", finance.getExpensePersonnel());
+        result.put("expenseOffice", finance.getExpenseOffice());
+        result.put("expenseMaintenance", finance.getExpenseMaintenance());
+        result.put("expenseOther", finance.getExpenseOther());
+        result.put("profit", finance.getProfit());
+        result.put("tax", finance.getTax());
+        result.put("taxRate", finance.getTaxRate());
+
+        // 游客数据
+        result.put("visitorJan", finance.getVisitorJan());
+        result.put("visitorFeb", finance.getVisitorFeb());
+        result.put("visitorMar", finance.getVisitorMar());
+        result.put("visitorApr", finance.getVisitorApr());
+        result.put("visitorMay", finance.getVisitorMay());
+        result.put("visitorJun", finance.getVisitorJun());
+        result.put("visitorJul", finance.getVisitorJul());
+        result.put("visitorAug", finance.getVisitorAug());
+        result.put("visitorSep", finance.getVisitorSep());
+        result.put("visitorOct", finance.getVisitorOct());
+        result.put("visitorNov", finance.getVisitorNov());
+        result.put("visitorDec", finance.getVisitorDec());
+        result.put("visitorTotal", finance.getVisitorTotal());
+        result.put("visitorDiscount", finance.getVisitorDiscount());
+        result.put("visitorFree", finance.getVisitorFree());
+        result.put("visitorFreeRate", finance.getVisitorFreeRate());
+
+        // 备注
+        result.put("remark", finance.getRemark());
+
+        return CommonResult.<Map<String, Object>>ok().value(result);
+    }
+
+    /**
+     * 导出收支调整记录详情(复杂表格格式)
+     */
+    @GetMapping("/finance/export")
+    @ApiOperation("导出收支调整记录详情")
+    public void exportFinanceDetail(
+            @ApiParam(value = "调整记录ID", required = true) @RequestParam String id,
+            HttpServletResponse response) throws IOException {
+
+        FapScenicFinance finance = financeManager.getById(id);
+        if (finance == null) {
+            throw new IllegalArgumentException("调整记录不存在");
+        }
+
+        // 获取景区信息
+        FapReportScenic scenic = fapReportScenicManager.getById(finance.getScenicId());
+        if (scenic == null) {
+            throw new IllegalArgumentException("景区不存在");
+        }
+
+        // 创建工作簿
+        XSSFWorkbook workbook = new XSSFWorkbook();
+        XSSFSheet sheet = workbook.createSheet("景区价格信息统计表");
+
+        // 创建样式
+        CellStyle titleStyle = createTitleStyle(workbook);
+        CellStyle headerStyle = createHeaderStyle(workbook);
+        CellStyle dataStyle = createDataStyle(workbook);
+        CellStyle centerStyle = createCenterStyle(workbook);
+
+        int rowNum = 0;
+
+        // 第1行:标题
+        Row titleRow = sheet.createRow(rowNum++);
+        titleRow.setHeightInPoints(35);
+        Cell titleCell = titleRow.createCell(0);
+        String title = scenic.getScenicName() + "景区门票价格信息统计表";
+        titleCell.setCellValue(title);
+        titleCell.setCellStyle(titleStyle);
+        addMergedRegionWithBorder(sheet, 0, 0, 0, 19);
+
+        // 第2行:单位和填表时间
+        Row unitRow = sheet.createRow(rowNum++);
+        unitRow.setHeightInPoints(25);
+
+        // 单位标签(第0-1列合并)
+        Cell unitLabelCell = unitRow.createCell(0);
+        unitLabelCell.setCellValue("单位:");
+        unitLabelCell.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 0, 1);
+
+        // 单位值(第2-11列合并,填充实际数据)
+        Cell unitValueCell = unitRow.createCell(2);
+        unitValueCell.setCellValue(finance.getUnitName() != null ? finance.getUnitName() : "");
+        unitValueCell.setCellStyle(dataStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 2, 11);
+
+        // 填表时间标签(第12-13列合并)
+        Cell dateLabelCell = unitRow.createCell(12);
+        dateLabelCell.setCellValue("填表时间:");
+        dateLabelCell.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 12, 13);
+
+        // 填表时间值(第14-19列合并,填充实际数据)
+        Cell dateValueCell = unitRow.createCell(14);
+        dateValueCell.setCellValue(finance.getFillDate() != null ? finance.getFillDate().toString() : "");
+        dateValueCell.setCellStyle(dataStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 14, 19);
+
+        // 第3行:景区基本信息表头
+        Row basicInfoHeaderRow = sheet.createRow(rowNum++);
+        basicInfoHeaderRow.setHeightInPoints(30);
+
+        // 第0列:"基本情况"标签(稍后合并第3-7行)
+        Cell basicSituationLabel = basicInfoHeaderRow.createCell(0);
+        basicSituationLabel.setCellValue("基本情况");
+        basicSituationLabel.setCellStyle(headerStyle);
+
+        basicInfoHeaderRow.createCell(1).setCellValue("景区名称");
+        basicInfoHeaderRow.createCell(2).setCellValue("景区类型");
+        basicInfoHeaderRow.createCell(3).setCellValue("景区等级");
+        basicInfoHeaderRow.createCell(4).setCellValue("价格管理形式");
+        basicInfoHeaderRow.createCell(5).setCellValue("景区面积");
+        basicInfoHeaderRow.createCell(6).setCellValue("主管部门");
+        basicInfoHeaderRow.createCell(7).setCellValue("运营性质");
+        basicInfoHeaderRow.createCell(8).setCellValue("管理属性");
+
+        // 门票收支管理方式(第9-19列合并)
+        Cell revenueManageHeader = basicInfoHeaderRow.createCell(9);
+        revenueManageHeader.setCellValue("门票收支管理方式及财政资金关系");
+        revenueManageHeader.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 9, 19);
+
+        // 设置表头样式
+        for (int i = 1; i <= 8; i++) {
+            basicInfoHeaderRow.getCell(i).setCellStyle(headerStyle);
         }
 
-        Object price5 = data.get("price5");
-        if (price5 != null) {
-            record.setPrice5(price5.toString());
+        // 第4行:景区基本信息数据
+        Row basicInfoDataRow = sheet.createRow(rowNum++);
+        basicInfoDataRow.setHeightInPoints(25);
+
+        basicInfoDataRow.createCell(1).setCellValue(scenic.getScenicName() != null ? scenic.getScenicName() : "");
+        basicInfoDataRow.createCell(2).setCellValue(scenic.getScenicType() != null ? scenic.getScenicType() : "");
+        basicInfoDataRow.createCell(3).setCellValue(scenic.getScenicLevel() != null ? scenic.getScenicLevel() : "");
+        basicInfoDataRow.createCell(4).setCellValue(scenic.getPriceManageForm() != null ? scenic.getPriceManageForm() : "");
+        basicInfoDataRow.createCell(5).setCellValue((scenic.getScenicArea() != null ? scenic.getScenicArea() : "") +
+                                                    (scenic.getAreaUnit() != null ? scenic.getAreaUnit() : ""));
+        basicInfoDataRow.createCell(6).setCellValue(scenic.getCompetentOrg() != null ? scenic.getCompetentOrg() : "");
+        // 运营性质
+        DataDict operationNatureDict = dataDictManager.getByDictKey("1965686172473999360", scenic.getOperationNature());
+        basicInfoDataRow.createCell(7).setCellValue(operationNatureDict != null ? operationNatureDict.getName() : "");
+        DataDict managementAttrDict = dataDictManager.getByDictKey("1965686172473999360", scenic.getManagementAttr());
+        basicInfoDataRow.createCell(8).setCellValue(managementAttrDict != null ? managementAttrDict.getName() : "");
+
+        // 门票收支管理方式(第9-19列合并)
+        Cell revenueManageData = basicInfoDataRow.createCell(9);
+        DataDict revenueManageDict = dataDictManager.getByDictKey("1965686172473999360", scenic.getRevenueManage());
+        revenueManageData.setCellValue(revenueManageDict != null ? revenueManageDict.getName() : "");
+        revenueManageData.setCellStyle(dataStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 9, 19);
+
+        // 设置前8列样式
+        for (int i = 1; i <= 8; i++) {
+            basicInfoDataRow.getCell(i).setCellStyle(dataStyle);
+        }
+
+        // 第5行:门票价格第一行表头
+        Row priceHeaderRow1 = sheet.createRow(rowNum++);
+        priceHeaderRow1.setHeightInPoints(30);
+
+        // 调整前门票价格(合并第5行的1-3列)
+        Cell beforePriceHeader = priceHeaderRow1.createCell(1);
+        beforePriceHeader.setCellValue("调整前门票价格(元/人)");
+        beforePriceHeader.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 1, 3);
+
+        // 调整后门票价格(合并第5行的4-6列)
+        Cell afterPriceHeader = priceHeaderRow1.createCell(4);
+        afterPriceHeader.setCellValue("调整后门票价格(元/人)");
+        afterPriceHeader.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 4, 6);
+
+        // 调整幅度(合并第5行的7-8列)
+        Cell adjustHeader = priceHeaderRow1.createCell(7);
+        adjustHeader.setCellValue("调整幅度(元/人)");
+        adjustHeader.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 7, 8);
+
+        // 景区内交通运输服务价格(合并第5行的9-16列)
+        Cell transportHeader = priceHeaderRow1.createCell(9);
+        transportHeader.setCellValue("景区内交通运输服务价格(元/人)");
+        transportHeader.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 9, 16);
+
+        // 成本监审、成本调查、价格评估结果(合并第5行的17-18列)
+        Cell costAuditHeader5 = priceHeaderRow1.createCell(17);
+        costAuditHeader5.setCellValue("成本监审、成本调查、价格评估结果");
+        costAuditHeader5.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum, 17, 18);
+
+        // 定价文件(合并第5行的19列,跨2行)
+        Cell pricingDocHeader5 = priceHeaderRow1.createCell(19);
+        pricingDocHeader5.setCellValue("定价文件");
+        pricingDocHeader5.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum, 19, 19);
+
+        // 第6行:基本情况第二行表头
+        Row priceHeaderRow2 = sheet.createRow(rowNum++);
+        priceHeaderRow2.setHeightInPoints(25);
+
+        priceHeaderRow2.createCell(1).setCellValue("旺季");
+        priceHeaderRow2.createCell(2).setCellValue("淡季");
+        priceHeaderRow2.createCell(3).setCellValue("执行时间");
+        priceHeaderRow2.createCell(4).setCellValue("旺季");
+        priceHeaderRow2.createCell(5).setCellValue("淡季");
+        priceHeaderRow2.createCell(6).setCellValue("执行时间");
+        priceHeaderRow2.createCell(7).setCellValue("旺季");
+        priceHeaderRow2.createCell(8).setCellValue("淡季");
+
+        // 交通工具子表头
+        String[] transportHeaders = {"景交车", "观光车", "缆车", "索道", "电梯", "游船", "停车场", "其他"};
+        for (int i = 0; i < transportHeaders.length; i++) {
+            priceHeaderRow2.createCell(9 + i).setCellValue(transportHeaders[i]);
         }
 
-        Object price6 = data.get("price6");
-        if (price6 != null) {
-            record.setPrice6(price6.toString());
+        // 设置第6行表头样式
+        for (int i = 1; i <= 16; i++) {
+            if (priceHeaderRow2.getCell(i) != null) {
+                priceHeaderRow2.getCell(i).setCellStyle(headerStyle);
+            }
         }
 
-        Object price7 = data.get("price7");
-        if (price7 != null) {
-            record.setPrice7(price7.toString());
+        // 第7行:门票价格、交通价格、成本监审、定价文件
+        Row priceDataRow = sheet.createRow(rowNum++);
+        priceDataRow.setHeightInPoints(25);
+
+        // 合并"基本情况"标签(第0列,第3-7行,跨5行)
+        addMergedRegionWithBorder(sheet, rowNum - 5, rowNum - 1, 0, 0);
+
+        // 调整前门票价格
+        priceDataRow.createCell(1).setCellValue(scenic.getTicketHigh() != null ? scenic.getTicketHigh() : "");
+        priceDataRow.createCell(2).setCellValue(scenic.getTicketLow() != null ? scenic.getTicketLow() : "");
+        priceDataRow.createCell(3).setCellValue(scenic.getExecuteTime() != null ? scenic.getExecuteTime().toString() : "");
+
+        // 调整后门票价格
+        priceDataRow.createCell(4).setCellValue(scenic.getTicketHighAdjust() != null ? scenic.getTicketHighAdjust() : "");
+        priceDataRow.createCell(5).setCellValue(scenic.getTicketLowAdjust() != null ? scenic.getTicketLowAdjust() : "");
+        priceDataRow.createCell(6).setCellValue(scenic.getExecuteTimeAdjust() != null ? scenic.getExecuteTimeAdjust().toString() : "");
+
+        // 调整幅度(计算百分比)
+        String highAdjust = "";
+        String lowAdjust = "";
+        try {
+            if (scenic.getTicketHigh() != null && scenic.getTicketHighAdjust() != null) {
+                double before = Double.parseDouble(scenic.getTicketHigh());
+                double after = Double.parseDouble(scenic.getTicketHighAdjust());
+                if (before != 0) {
+                    double percentage = ((after - before) / before) * 100;
+                    highAdjust = String.format("%.2f%%", percentage);
+                }
+            }
+            if (scenic.getTicketLow() != null && scenic.getTicketLowAdjust() != null) {
+                double before = Double.parseDouble(scenic.getTicketLow());
+                double after = Double.parseDouble(scenic.getTicketLowAdjust());
+                if (before != 0) {
+                    double percentage = ((after - before) / before) * 100;
+                    lowAdjust = String.format("%.2f%%", percentage);
+                }
+            }
+        } catch (NumberFormatException e) {
+            // 忽略格式错误
         }
+        priceDataRow.createCell(7).setCellValue(highAdjust);
+        priceDataRow.createCell(8).setCellValue(lowAdjust);
+
+        // 交通价格(第9-16列)
+        priceDataRow.createCell(9).setCellValue(scenic.getPrice1() != null ? scenic.getPrice1() : "");
+        priceDataRow.createCell(10).setCellValue(scenic.getPrice2() != null ? scenic.getPrice2() : "");
+        priceDataRow.createCell(11).setCellValue(scenic.getPrice3() != null ? scenic.getPrice3() : "");
+        priceDataRow.createCell(12).setCellValue(scenic.getPrice4() != null ? scenic.getPrice4() : "");
+        priceDataRow.createCell(13).setCellValue(scenic.getPrice5() != null ? scenic.getPrice5() : "");
+        priceDataRow.createCell(14).setCellValue(scenic.getPrice6() != null ? scenic.getPrice6() : "");
+        priceDataRow.createCell(15).setCellValue(scenic.getPrice7() != null ? scenic.getPrice7() : "");
+        priceDataRow.createCell(16).setCellValue(scenic.getPrice8() != null ? scenic.getPrice8() : "");
 
-        Object price8 = data.get("price8");
-        if (price8 != null) {
-            record.setPrice8(price8.toString());
+        // 成本监审、成本调查、价格评估结果(第17-18列合并)
+        Cell costResultCell = priceDataRow.createCell(17);
+        costResultCell.setCellValue(scenic.getCostAuditResult() != null ? scenic.getCostAuditResult() : "");
+        costResultCell.setCellStyle(dataStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 17, 18);
+
+        // 定价文件(第19列)
+        Cell pricingDocCell = priceDataRow.createCell(19);
+        pricingDocCell.setCellValue(scenic.getPricingDocNo() != null ? scenic.getPricingDocNo() : "");
+        pricingDocCell.setCellStyle(dataStyle);
+
+        // 设置数据行样式
+        for (int i = 1; i <= 16; i++) {
+            if (priceDataRow.getCell(i) != null) {
+                priceDataRow.getCell(i).setCellStyle(dataStyle);
+            }
         }
+
+        // 第8-9行:收支情况表头
+        Row financeHeaderRow1 = sheet.createRow(rowNum++);
+        financeHeaderRow1.setHeightInPoints(30);
+        Row financeHeaderRow2 = sheet.createRow(rowNum++);
+        financeHeaderRow2.setHeightInPoints(25);
+
+        // 第一列:收支情况标签(跨3行)
+        Cell financeLabelCell = financeHeaderRow1.createCell(0);
+        financeLabelCell.setCellValue("收支情况");
+        financeLabelCell.setCellStyle(headerStyle);
+
+        Cell fh1 = financeHeaderRow1.createCell(1);
+        fh1.setCellValue("员工人数");
+        fh1.setCellStyle(headerStyle);
+
+        Cell fh2 = financeHeaderRow1.createCell(2);
+        fh2.setCellValue("总收入(万元)");
+        fh2.setCellStyle(headerStyle);
+
+        Cell fh3 = financeHeaderRow1.createCell(7);
+        fh3.setCellValue("总支出(万元)");
+        fh3.setCellStyle(headerStyle);
+
+        Cell fh4 = financeHeaderRow1.createCell(13);
+        fh4.setCellValue("利润(万元)");
+        fh4.setCellStyle(headerStyle);
+
+        Cell fh5 = financeHeaderRow1.createCell(14);
+        fh5.setCellValue("税金(万元)");
+        fh5.setCellStyle(headerStyle);
+
+        Cell fh6 = financeHeaderRow1.createCell(15);
+        fh6.setCellValue("税率(%)");
+        fh6.setCellStyle(headerStyle);
+
+        Cell fh7 = financeHeaderRow1.createCell(16);
+        fh7.setCellValue("备注");
+        fh7.setCellStyle(headerStyle);
+
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 1, 1, 1); // 员工人数
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 2, 2, 6); // 总收入
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 2, 7, 12); // 总支出
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 1, 13, 13); // 利润
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 1, 14, 14); // 税金
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 1, 15, 15); // 税率
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 1, 16, 19); // 备注(合并到第19列)
+
+        String[] revenueHeaders = {"合计", "门票", "经营服务", "财政拨款", "其他"};
+        for (int i = 0; i < revenueHeaders.length; i++) {
+            Cell rhCell = financeHeaderRow2.createCell(i + 2);
+            rhCell.setCellValue(revenueHeaders[i]);
+            rhCell.setCellStyle(headerStyle);
+        }
+
+        String[] expenseHeaders = {"合计", "上缴财政", "人员", "办公", "建设维护", "其他"};
+        for (int i = 0; i < expenseHeaders.length; i++) {
+            Cell ehCell = financeHeaderRow2.createCell(i + 7);
+            ehCell.setCellValue(expenseHeaders[i]);
+            ehCell.setCellStyle(headerStyle);
+        }
+
+        // 第10行:收支情况数据
+        Row financeDataRow1 = sheet.createRow(rowNum++);
+        financeDataRow1.setHeightInPoints(25);
+
+        // 合并收支情况标签列(跨3行)- 第8-10行
+        addMergedRegionWithBorder(sheet, rowNum - 3, rowNum - 1, 0, 0);
+
+        financeDataRow1.createCell(1).setCellValue(finance.getEmployeeCount() != null ? finance.getEmployeeCount() : 0);
+        financeDataRow1.createCell(2).setCellValue(finance.getRevenueTotal() != null ? finance.getRevenueTotal().doubleValue() : 0);
+        financeDataRow1.createCell(3).setCellValue(finance.getRevenueTicket() != null ? finance.getRevenueTicket().doubleValue() : 0);
+        financeDataRow1.createCell(4).setCellValue(finance.getRevenueService() != null ? finance.getRevenueService().doubleValue() : 0);
+        financeDataRow1.createCell(5).setCellValue(finance.getRevenueFiscal() != null ? finance.getRevenueFiscal().doubleValue() : 0);
+        financeDataRow1.createCell(6).setCellValue(finance.getRevenueOther() != null ? finance.getRevenueOther().doubleValue() : 0);
+        financeDataRow1.createCell(7).setCellValue(finance.getExpenseTotal() != null ? finance.getExpenseTotal().doubleValue() : 0);
+        financeDataRow1.createCell(8).setCellValue(finance.getExpenseFiscalPayment() != null ? finance.getExpenseFiscalPayment().doubleValue() : 0);
+        financeDataRow1.createCell(9).setCellValue(finance.getExpensePersonnel() != null ? finance.getExpensePersonnel().doubleValue() : 0);
+        financeDataRow1.createCell(10).setCellValue(finance.getExpenseOffice() != null ? finance.getExpenseOffice().doubleValue() : 0);
+        financeDataRow1.createCell(11).setCellValue(finance.getExpenseMaintenance() != null ? finance.getExpenseMaintenance().doubleValue() : 0);
+        financeDataRow1.createCell(12).setCellValue(finance.getExpenseOther() != null ? finance.getExpenseOther().doubleValue() : 0);
+        financeDataRow1.createCell(13).setCellValue(finance.getProfit() != null ? finance.getProfit().doubleValue() : 0);
+        financeDataRow1.createCell(14).setCellValue(finance.getTax() != null ? finance.getTax().doubleValue() : 0);
+        financeDataRow1.createCell(15).setCellValue(finance.getTaxRate() != null ? finance.getTaxRate().doubleValue() : 0);
+
+        // 备注(第16-19列合并)
+        Cell fd16 = financeDataRow1.createCell(16);
+        fd16.setCellValue(finance.getRemark() != null ? finance.getRemark() : "");
+        fd16.setCellStyle(dataStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 16, 19);
+
+        // 设置数据行样式
+        for (int i = 1; i <= 15; i++) {
+            if (financeDataRow1.getCell(i) != null) {
+                financeDataRow1.getCell(i).setCellStyle(dataStyle);
+            }
+        }
+
+        // ========== 第11-12行:年月度游客人数表格 ==========
+        Row monthlyVisitorHeaderRow = sheet.createRow(rowNum++);
+        monthlyVisitorHeaderRow.setHeightInPoints(25);
+        Row monthlyVisitorDataRow = sheet.createRow(rowNum++);
+        monthlyVisitorDataRow.setHeightInPoints(25);
+
+        // 第一列:标签(跨2行)
+        Cell monthlyLabel = monthlyVisitorHeaderRow.createCell(0);
+        monthlyLabel.setCellValue("年月度游客人数(人)");
+        monthlyLabel.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 1, 0, 0);
+
+        // 第11行:月度表头(1-12月)
+        String[] monthlyHeaders = {"1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"};
+        for (int i = 0; i < monthlyHeaders.length; i++) {
+            Cell mhCell = monthlyVisitorHeaderRow.createCell(i + 1);
+            mhCell.setCellValue(monthlyHeaders[i]);
+            mhCell.setCellStyle(headerStyle);
+        }
+
+        // 本年度人数合计
+        Cell totalHeader = monthlyVisitorHeaderRow.createCell(13);
+        totalHeader.setCellValue("本年度人数合计");
+        totalHeader.setCellStyle(headerStyle);
+
+        // 优惠人数
+        Cell discountHeader = monthlyVisitorHeaderRow.createCell(14);
+        discountHeader.setCellValue("优惠人数");
+        discountHeader.setCellStyle(headerStyle);
+
+        // 其中免票人数
+        Cell freeHeader = monthlyVisitorHeaderRow.createCell(15);
+        freeHeader.setCellValue("其中免票人数");
+        freeHeader.setCellStyle(headerStyle);
+
+        // 免票人数占比(第16-19列合并)
+        Cell monthlyFreeRateHeader = monthlyVisitorHeaderRow.createCell(16);
+        monthlyFreeRateHeader.setCellValue("免票人数占比(%)");
+        monthlyFreeRateHeader.setCellStyle(headerStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 2, rowNum - 2, 16, 19);
+
+        // 第12行:月度游客数据(填充实际数据)
+        monthlyVisitorDataRow.createCell(1).setCellValue(finance.getVisitorJan() != null ? finance.getVisitorJan() : 0);
+        monthlyVisitorDataRow.createCell(2).setCellValue(finance.getVisitorFeb() != null ? finance.getVisitorFeb() : 0);
+        monthlyVisitorDataRow.createCell(3).setCellValue(finance.getVisitorMar() != null ? finance.getVisitorMar() : 0);
+        monthlyVisitorDataRow.createCell(4).setCellValue(finance.getVisitorApr() != null ? finance.getVisitorApr() : 0);
+        monthlyVisitorDataRow.createCell(5).setCellValue(finance.getVisitorMay() != null ? finance.getVisitorMay() : 0);
+        monthlyVisitorDataRow.createCell(6).setCellValue(finance.getVisitorJun() != null ? finance.getVisitorJun() : 0);
+        monthlyVisitorDataRow.createCell(7).setCellValue(finance.getVisitorJul() != null ? finance.getVisitorJul() : 0);
+        monthlyVisitorDataRow.createCell(8).setCellValue(finance.getVisitorAug() != null ? finance.getVisitorAug() : 0);
+        monthlyVisitorDataRow.createCell(9).setCellValue(finance.getVisitorSep() != null ? finance.getVisitorSep() : 0);
+        monthlyVisitorDataRow.createCell(10).setCellValue(finance.getVisitorOct() != null ? finance.getVisitorOct() : 0);
+        monthlyVisitorDataRow.createCell(11).setCellValue(finance.getVisitorNov() != null ? finance.getVisitorNov() : 0);
+        monthlyVisitorDataRow.createCell(12).setCellValue(finance.getVisitorDec() != null ? finance.getVisitorDec() : 0);
+        monthlyVisitorDataRow.createCell(13).setCellValue(finance.getVisitorTotal() != null ? finance.getVisitorTotal() : 0);
+        monthlyVisitorDataRow.createCell(14).setCellValue(finance.getVisitorDiscount() != null ? finance.getVisitorDiscount() : 0);
+        monthlyVisitorDataRow.createCell(15).setCellValue(finance.getVisitorFree() != null ? finance.getVisitorFree() : 0);
+
+        // 免票占比(第16-19列合并)
+        Cell mvdFreeRate = monthlyVisitorDataRow.createCell(16);
+        mvdFreeRate.setCellValue(finance.getVisitorFreeRate() != null ? finance.getVisitorFreeRate().doubleValue() : 0);
+        mvdFreeRate.setCellStyle(dataStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 16, 19);
+
+        // 设置数据行样式
+        for (int i = 1; i <= 15; i++) {
+            if (monthlyVisitorDataRow.getCell(i) != null) {
+                monthlyVisitorDataRow.getCell(i).setCellStyle(dataStyle);
+            }
+        }
+
+        // ========== 第13行:备注说明 ==========
+        Row remarkRow = sheet.createRow(rowNum++);
+        remarkRow.setHeightInPoints(60);
+
+        // 创建左对齐、自动换行样式
+        CellStyle remarkStyle = workbook.createCellStyle();
+        remarkStyle.setAlignment(HorizontalAlignment.LEFT);
+        remarkStyle.setVerticalAlignment(VerticalAlignment.TOP);
+        remarkStyle.setWrapText(true);
+        remarkStyle.setBorderTop(BorderStyle.THIN);
+        remarkStyle.setBorderBottom(BorderStyle.THIN);
+        remarkStyle.setBorderLeft(BorderStyle.THIN);
+        remarkStyle.setBorderRight(BorderStyle.THIN);
+        XSSFFont remarkFont = workbook.createFont();
+        remarkFont.setFontName("宋体");
+        remarkFont.setFontHeightInPoints((short) 10);
+        remarkStyle.setFont(remarkFont);
+
+        // 三条备注合并在一个单元格中
+        Cell remarkCell = remarkRow.createCell(0);
+        remarkCell.setCellValue("备注:1、表格中'现行门票价格'指定价文件中的价格开始执行时间,非临时优惠价格执行时间;\n" +
+                               "      2、景区面积填写'平方公里'或'平方米'、'亩';\n" +
+                               "      3、税率指企业所得税税率。");
+        remarkCell.setCellStyle(remarkStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 0, 19);
+
+        // ========== 第14行:填表信息 ==========
+        Row fillInfoRow = sheet.createRow(rowNum++);
+        fillInfoRow.setHeightInPoints(20);
+
+        // 创建填表信息样式
+        CellStyle fillInfoStyle = workbook.createCellStyle();
+        fillInfoStyle.setAlignment(HorizontalAlignment.LEFT);
+        fillInfoStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        XSSFFont fillInfoFont = workbook.createFont();
+        fillInfoFont.setFontName("宋体");
+        fillInfoFont.setFontHeightInPoints((short) 10);
+        fillInfoStyle.setFont(fillInfoFont);
+
+        // 景区门票价格工作负责人标签(第0列)
+        Cell responsiblePersonLabel = fillInfoRow.createCell(0);
+        responsiblePersonLabel.setCellValue("景区门票价格工作负责人:");
+        responsiblePersonLabel.setCellStyle(fillInfoStyle);
+
+        // 景区门票价格工作负责人值(第1-9列合并,填充实际数据)
+        Cell responsiblePersonValue = fillInfoRow.createCell(1);
+        responsiblePersonValue.setCellValue(finance.getResponsiblePerson() != null ? finance.getResponsiblePerson() : "");
+        responsiblePersonValue.setCellStyle(dataStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 1, 9);
+
+        // 联系电话标签(第10列)
+        Cell contactPhoneLabel = fillInfoRow.createCell(10);
+        contactPhoneLabel.setCellValue("联系电话:");
+        contactPhoneLabel.setCellStyle(fillInfoStyle);
+
+        // 联系电话值(第11-19列合并,填充实际数据)
+        Cell contactPhoneValue = fillInfoRow.createCell(11);
+        contactPhoneValue.setCellValue(finance.getContactPhone() != null ? finance.getContactPhone() : "");
+        contactPhoneValue.setCellStyle(dataStyle);
+        addMergedRegionWithBorder(sheet, rowNum - 1, rowNum - 1, 11, 19);
+
+        // 设置列宽
+        for (int i = 0; i < 20; i++) {
+            sheet.setColumnWidth(i, 4000);
+        }
+
+        // 设置响应头
+        String fileName = scenic.getScenicName() + "_" + finance.getYear() + "年价格信息统计表.xlsx";
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setHeader("Content-Disposition", "attachment; filename=" +
+            new String(fileName.getBytes("UTF-8"), "ISO-8859-1"));
+
+        // 输出到响应流
+        workbook.write(response.getOutputStream());
+        workbook.close();
+    }
+
+    /**
+     * 创建标题样式
+     */
+    private CellStyle createTitleStyle(XSSFWorkbook workbook) {
+        CellStyle style = workbook.createCellStyle();
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        // 添加边框
+        style.setBorderTop(BorderStyle.THIN);
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setBorderRight(BorderStyle.THIN);
+        XSSFFont font = workbook.createFont();
+        font.setBold(true);
+        font.setFontHeightInPoints((short) 16);
+        style.setFont(font);
+        return style;
+    }
+
+    /**
+     * 创建表头样式
+     */
+    private CellStyle createHeaderStyle(XSSFWorkbook workbook) {
+        CellStyle style = workbook.createCellStyle();
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        style.setBorderTop(BorderStyle.THIN);
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setBorderRight(BorderStyle.THIN);
+        XSSFFont font = workbook.createFont();
+        font.setBold(true);
+        style.setFont(font);
+        return style;
     }
 
+    /**
+     * 创建数据样式
+     */
+    private CellStyle createDataStyle(XSSFWorkbook workbook) {
+        CellStyle style = workbook.createCellStyle();
+        style.setAlignment(HorizontalAlignment.LEFT);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        style.setBorderTop(BorderStyle.THIN);
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setBorderRight(BorderStyle.THIN);
+        return style;
+    }
+
+    /**
+     * 创建居中样式
+     */
+    private CellStyle createCenterStyle(XSSFWorkbook workbook) {
+        CellStyle style = workbook.createCellStyle();
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        style.setBorderTop(BorderStyle.THIN);
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setBorderRight(BorderStyle.THIN);
+        return style;
+    }
+
+    /**
+     * 为合并区域设置边框
+     */
+    private void setBorderForMergedRegion(CellRangeAddress region, XSSFSheet sheet) {
+        org.apache.poi.ss.util.RegionUtil.setBorderTop(BorderStyle.THIN, region, sheet);
+        org.apache.poi.ss.util.RegionUtil.setBorderBottom(BorderStyle.THIN, region, sheet);
+        org.apache.poi.ss.util.RegionUtil.setBorderLeft(BorderStyle.THIN, region, sheet);
+        org.apache.poi.ss.util.RegionUtil.setBorderRight(BorderStyle.THIN, region, sheet);
+    }
+
+    /**
+     * 添加合并区域并设置边框
+     */
+    private void addMergedRegionWithBorder(XSSFSheet sheet, int firstRow, int lastRow, int firstCol, int lastCol) {
+        CellRangeAddress region = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol);
+        sheet.addMergedRegion(region);
+        setBorderForMergedRegion(region, sheet);
+    }
+
+
 }

+ 49 - 67
pricing/src/main/java/com/hotent/pricing/controller/report/api/FapReportPortalController.java

@@ -107,7 +107,8 @@ public class FapReportPortalController {
                 }
                 PriceInfoVO waterVO = new PriceInfoVO();
                 waterVO.setName("居民生活用水");
-                waterVO.setPrice1(water.getPrice1());
+                waterVO.setPrice1(water.getPrice1() + "元/吨");
+                waterVO.setImgIcon("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACEAAAAnCAYAAACBvSFyAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAANUSURBVFiFxZi/UxpREMe/u6CYjnSZSQos7c6ZKHRiHWd8UqVTOtHJqF069C/ATEbPTv0H8CyswU5iZkJn6TX2pIo/36bgcODuAQcHyXeG4v24x+d2973ddzFE0HzOLn6YWaq+n1nC3c3F5bDrxKIAELALAARko4DQMA+lc0clQLYNQye1ciE/doh0zj4GsNZjysAgA0GEABgKJHRMNF2A9ZDTrUFiJBSEF4RfQwIAAwZrX4j2XTCowoL0hMgoew2E/WEA2kE+zHz6fXdzcdVjjllz6sAipgqBklEgWtJaz147m3XTGJs6M8pOMfPZqAAAgJnPMspOhYYQwjEA4wMRlBKiM9NAICbSuaMSCJ9HDNAU4Z0pUDtiIqPsrDAqYwFo/1ON6Sun4Lbar+6wVCkpjONxAwCA/0VfISZ5amsMcdBNqfmcXWw1CJ4VJjlxO8rdEEYttzA8K/xrADTdUgQA+l9WAACBNFjTbDzBiTX0ABCROhFXBdIgICUQNSpgAiVfWGfjAC8AYprjvujn/E/nS9XXn/e28kgONEZsmSFi+QcE2K2VC9MGAADAlVOoPuj7WQF2BdKIRCFiUTpnd5pBUK2dFRbDrpFRdkoYxShuigd6CK5xZhd5J1/eUqWdCUwqZl4QEcsH5IKoLlpfEtOyv0SMQ+CC2nwrw/m57uw0AJx4v65Kr9hbHclC4DLQ+eZCwRgZpQzruwyijkKDQMmP6nt2HAAZZWcDcUNU5xf9dO6fHOP46jgghFD095GWc0LzPnHr3/P+dBtVpjJBII0f5Y233GwEg0kYFUuVRnIydi8TaB+tVM4ap/4ABZBK8FRpFBDeOh2WFkjjUd9/Q3tlNZc7WGOwidZ50H/y3hYcSJYqJRP85hgQFRyVnVp5Yx/+8q7HXdMljfyVUzAe4ybNq0NFTAELNEVOrby+8toKkk/96pqYBFUt+vQJj47JMpYqJSd4UrHwKgjdtrn7oO9n258PXH68XFDpmyGleRQD0oAgJZAkEfU76FzSWPTvOuMNrLcvh9bJg77fMVmw5/eJdO5wW4BixCLG1dB71+XNrjml70eSZvk3tQXI9oAwrobei2mu9jv0BvpSk1F2VjMWCGRBxPJlXxeAK4Sq1s+X3Qoik/4CYQle+HvHYPMAAAAASUVORK5CYII="); // 水的图标
                 listData.add(waterVO);
             }
 
@@ -122,7 +123,8 @@ public class FapReportPortalController {
                 if (firstElectricity.isPresent()) {
                     PriceInfoVO electricityVO = new PriceInfoVO();
                     electricityVO.setName("居民用电");
-                    electricityVO.setPrice1(firstElectricity.get().getPriceBelow1kv());
+                    electricityVO.setPrice1(firstElectricity.get().getPriceBelow1kv()+ "元/千瓦时");
+                    electricityVO.setImgIcon("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAuCAYAAABAm7v+AAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAALZSURBVFiFxZg9WttAEIa/WQh2qig30A2ihlglvsHiCwRKQRH7BIYTKClA6eAEtm6ASxsKnBsoN3AqHn6yk8I2j4n1s7OI+Ct3V+v3mZ3Zb7yEmtXqnHcZ6IORKab9cRplku9VnTChTvYAignkEVFgFL5I96gNKNSJbxQPV8cI/HsjQIGOPVa4IpC3Om4MzzYC1KDmEICfMzWV7rX9WpjPnaQPYC9v7hEPooQGAHoNTKtz3gUozptj8Ox6cPRRuqfzke3qs4CBfuEChjg6cD2yUCc+KwwBeEVriEicP3CJUKBjj4mKkngVSVxhcIlQQzVjgIOqdcb8+ekCJIrQoqIObNaSY4SsgUKdHBBwYr+xWw5Zlf3CFm7/vYnLNBlETldKZYSKbKFUjiVvBVRiC8VyLPlKoFbnRwzKt4VyyU11qcKyn9sCd9225bWSD3XiP+HJV9jySMFn0AcCBWD2APhM7ClDvdzEC3WyxwpXbjAAGCMQzZY/BrI+8mwNyKWiatRLoEDHXkM1b8VJXIMYPGPD7RdJ3VDvLzYBAwDK0P5Nejx9BprbAutNwJDB4TiNRgCwhYUtgPBtEzAMnEyG0fdnuF19FpAi2U1cI8z1IDpdHdtWSpU2Wv8TBgAUgzdR3pd5MACwTcApg+e9MSMj0AyEjIGMjfnFMBlBzR7xkO2o5ldJC5IrxmgyjA6LpkUtQquTXNg2aPkwNL3nu/Y07RV6nahjZK5uXUuUVcGIgQSetAZDBpUwIqBAx57j1ZCRQdv2Wcb6X8c77Iijs/Cn/Zv02LqDlByZOH+W/iT6xnqhkh3Xqj+9CRBAn+zXcm+cRpdSGCmQVYQYOJkMjpyN2h7I4g4q8qe3Aaq+gwr9qXagUCdVLx3pZFDsT7UDmbLXDqbpvbmrBcYaqKRFsfKn2oGU2soreWt/qh0I6xES+ZNEdl7G8Jedk4s/SWSX1Gx6PH9AyNhwW+pPEv0FQKAncJRNJaEAAAAASUVORK5CYII="); // 电的图标
                     listData.add(electricityVO);
                     orgNameSet.add(firstElectricity.get().getOrgName());
                 }
@@ -138,7 +140,8 @@ public class FapReportPortalController {
                 }
                 PriceInfoVO gasVO = new PriceInfoVO();
                 gasVO.setName("民用管道天然气");
-                gasVO.setPrice1(gas.getSalesPriceResidential());
+                gasVO.setPrice1(gas.getSalesPriceResidential() + "元/立方米");
+                gasVO.setImgIcon("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAwCAYAAABnjuimAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAQKSURBVFiFrVlNctNIFP7eywxOdj6CboBZJNYO5wTTeA6QZIehpoATxD6BzSIRO8wFHHGCmJ0NVGFu4LmBl4Eq92MhydG/WlJ/K1er9d6n999tggX0h95HAba/9cP7jf9uZ0NmGmxLEAHjDh//cJU3sCUzDmtEQzjCuO8PP0wty7VONIS87b/w7ntq2rUl0QpREellFgmDDp1YI2vHogQnf116HT75aENFa6Ku8hwClVhN1NnQu26rpzVRYVFVewgYn6nbyn1laO96oX9MthHTtE28tiJ6qm56IJjWTecJH79JL579e3th8nIrosRsZM3DfmDsKi+ReCRsFMONiQZulLd13xNGgpRANgSMq8g2JvqEj9+UZ3shLuNWFa3/h0HCNSLa1JoR9qxz45qYpunQiFBItD+8LSTS4eNpQ2uGSvngZuajp7FHjjDujYkGX0WZDA2fDQBcNiUZETpVN0HbzbZfJy9ec4nuoQcAnHTM9NS0Kww7LZFp4CpvkFfe8qpDLlEmvkAQM4ny0+GTa6Cgr9eECC40y13h81R1yBDtqWlXKHSHPJIKXN48gdIgol5FnCeqQ4ZovOxEhF3lOWVfn4ZArBxH4lZNEE2XnUfCdFcnywk8F2BsgevBqgmieUW8P/xwB8oZjEsh3a+L0YQ0rtoyFQ6qz4FocRGvHuOKsPJH8/ZkA/0Hoi1aYlY0sI1+r/xR2zBwTtVNj2GhJaYR9e8IXxejCYB5U3nMNGAA6HDn0pY1Q2zSC6QxQczS9cDPA9cbTukFSFhKILtv/usM0ZU/2mroSRMFItphV3lOjSk9KQCyS1uKQH7R/m+L1/NGViU4bHI4K36f5yt/tAWwPKxpfCp7p4lVCdRlgJ4a7M0XoOVz8Et+hkvblT9alr3T1KoMaeZ2CJYRqb3eb1DLWvK+nirZsZA0ynYSJEkJlqG1KqG1lFo9q4t23Kgsxaz5KMy8A4VVwdz9hOVfNSlGbkiQ+u7/V8tCofYNIIazrfxkSL3AJmASZnpL6C/GOjX5XMsFQpv14tWsKbU4ogSs1onlyh9tGRSVmGqQyLs25BKywEbDdZS0TLq4kySQk0BtcGRCNKaTw3irLCtCsEbSFPESyHicbErBGsbBbwc0i3uQEU42BsOthUw3hGC5XrxM5MNhwg+GW7KS0SbQKDyHbX/Jw4v0YuJwF35FbrzuoW0O1iDOvcjYksZ53r9/mXP9ejG6yrOsWCYK8POkAtqQxnlRM8m90gksm6yZR/x3m1NABoL41SPN1ncvn5V1vMJrx/Xi1Yw0zqMkEsm/02wCV3mDcBjaauirdOLUIoqgGix/6YdnAM2IqFd0yVoXQrgGMCeNc9PR0Biu8gau8trei8JVnnM6vKkt5w9gfpr1oxjwuAAAAABJRU5ErkJggg=="); // 气的图标
                 listData.add(gasVO);
             }
 
@@ -151,7 +154,8 @@ public class FapReportPortalController {
                 }
                 PriceInfoVO heatVO = new PriceInfoVO();
                 heatVO.setName("居民用热(日单价)");
-                heatVO.setPrice1(heat.getAreaChargeResidentialUsable());
+                heatVO.setPrice1(heat.getAreaChargeResidentialUsable() + "元/m2");
+                heatVO.setImgIcon("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACsAAAArCAYAAADhXXHAAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAOgSURBVFiF7ZlfbiJHEMa/qliGt8yeIHADVoozfgs+weK5QPBbcBTFnMD2CTCKnNm3sBdgxydY/Ga8kcKewOQGvAWsuL48eAx4oGHAHu+uxE9CoO6u6Y/u6j9VI1jATuW8RJi3qM0ytrDVv4pq/VVsdoLzqkKPARRAdEg2r6PDSOY1/iEIjwEeCeRJQqfo01i/jg6jZQ394I8jQBrJcoMdzIj1g/BPANVnEjnT4cf2Ly1XfanS8LY1dzNvkEj2dLpgtxKWsxIKAPHUOskj77lmU0RKj8RS5c1zC0xQ+L7ye9lVOcRw4LQk+pooeS4fdSLQgquuF9UHgJzNN2RT51Z8Rkb27+msYDnrtg/Pvjixvag+EGNzuszs7hMAbK3wnBaBVPulUCoQllYVuoy0Ylvddu0g7UNLlUZzW+ZvQU8hlRukHdEHelF9IJTe2qocpBIrgHMFz6NUaXj8jG5Q9YMQZna5rKGqeCR+cm7uKg1/P5wcDoJOWhdbZYFVVTXV6SZzbxxxHcSDYPqPVHeC88tFx/ADX8TWpdAf07X7inhxsQRO7uy/vXVsX16s2T9/Rb921rHduEFWbMRmxUZsVmzEZsVGbFa4r4jE/ZEoKM+UyyRyIFkSkemLdh+QKK4szdhP7HoTu3Thj3NkjfbOaO/mlps1zeySxotbjvYInMTVrW67VqTZpdndpxGH+1N1jxCZhD2ElXcr4Tgaocpv022/gXaw4uV70pHqG4lF5JjvjGy4v625o1sb1f398AMEZYEgh/xxt10r+kFYRSI0ovFCVKqIL+Sm/NsPwghEAeBkNojOQxby6T4rvLjPUWknj7yXmPaCH4RMCgWAOKM4jg7iMKiadBshTh9+ryX21oZNMRRHNnwlJhEVMwk3MRS77Zp02zUZ+3+CkQ3rWBAFi+HgKqqNbdcSm9NclYIP25q7oeIGQJW0QjxdfQCg4iYeVSe9qD7ovv/5NYETkj0AIDgA0BJD8SqqPYrLFvqsQAbg1MqX+8QdId+KoCCYRIYiUtqthGUYTik4ptBLmzu4btdOgcl0u3COrEI8hfSE2Bt/oC2FOHOopnxPpTfi8PV1+/CV0ep+8Lbh2r5WxT2yKg0CiXQ5AZX77znEf6KR03zDD8KJzTPxVZ1ggvjtiEC/yyr7l+gyIthTw+X0Sk9l6QdvGwCPshPnZtkLkSRKWGYvPJZ2njITM24vEPdLh4xZNZWqNNbjjfil6d/asJmi3Zj/AeHseFelBVtoAAAAAElFTkSuQmCC"); // 热的图标
                 listData.add(heatVO);
             }
 
@@ -159,7 +163,7 @@ public class FapReportPortalController {
             LambdaQueryWrapper<FapReportScenic> wrapper = new LambdaQueryWrapper<>();
             wrapper.eq(FapReportScenic::getIsHistory, "0");
             if (regionId != null && !regionId.trim().isEmpty()) {
-                wrapper.eq(FapReportScenic::getScenicLocality, regionId);
+                wrapper.in(FapReportScenic::getScenicLocality, getCityAndCountyIds(regionId));
             }
             wrapper.orderByDesc(FapReportScenic::getExecuteTime);
             List<FapReportScenic> scenicList = scenicManager.list(wrapper);
@@ -175,8 +179,9 @@ public class FapReportPortalController {
                 }
                 PriceInfoVO scenicVO = new PriceInfoVO();
                 scenicVO.setName(scenic.getScenicName());
-                scenicVO.setPrice1(scenic.getTicketHigh());
-                scenicVO.setPrice2(scenic.getTicketLow());
+                scenicVO.setPrice1(scenic.getTicketHigh()+"元");
+                scenicVO.setPrice2(scenic.getTicketLow()+"元");
+                scenicVO.setImgIcon(scenic.getImgIcon());
                 listData.add(scenicVO);
             }
 
@@ -208,6 +213,7 @@ public class FapReportPortalController {
                     PriceInfoVO travelVO = new PriceInfoVO();
                     travelVO.setName(projectName);
                     travelVO.setPrice1(standard.getFeeStandardContent());
+                    travelVO.setImgIcon(IconManager.getIconByName(projectName));
                     listData.add(travelVO);
                 }
             }
@@ -227,7 +233,7 @@ public class FapReportPortalController {
     @ApiOperation("获取燃气价格查询")
     public CommonResult<FapReportGas> getGasList(
             @ApiParam(value = "地区ID") @RequestParam(required = true) String regionId) {
-        List<String> regionIds = Collections.singletonList(regionId);
+        List<String> regionIds = getCityAndCountyIds(regionId);
         List<FapReportGas> records = gasDao.getGasReportApiList(regionIds);
         if (records == null || records.isEmpty()){
             return CommonResult.<FapReportGas>ok().value(null);
@@ -304,14 +310,14 @@ public class FapReportPortalController {
     @GetMapping("/scenic/list")
     @ApiOperation("获取景区价格查询")
     public CommonResult<Map<String, Object>> getScenicList(
-            @ApiParam(value = "景区属地(地区ID)") @RequestParam(required = false) String region,
+            @ApiParam(value = "景区属地(地区ID)") @RequestParam(required = true) String regionId,
             @ApiParam(value = "景区名称") @RequestParam(required = false) String scenicName,
             @ApiParam(value = "景区等级(5A/4A/3A)") @RequestParam(required = false) String scenicLevel) {
         Map<String, Object> result = new HashMap<>();
         LambdaQueryWrapper<FapReportScenic> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(FapReportScenic::getIsHistory, "0");
-        if (region != null && !region.trim().isEmpty()) {
-            wrapper.eq(FapReportScenic::getScenicLocality, region);
+        if (regionId != null && !regionId.trim().isEmpty()) {
+            wrapper.in(FapReportScenic::getScenicLocality, getCityAndCountyIds(regionId));
         }
         if (scenicName != null && !scenicName.trim().isEmpty()) {
             wrapper.like(FapReportScenic::getScenicName, scenicName.trim());
@@ -335,6 +341,7 @@ public class FapReportPortalController {
                 priceInfoVO.setName(scenic.getScenicName());
                 priceInfoVO.setPrice1(scenic.getPrice1());
                 priceInfoVO.setPrice2(scenic.getPrice2());
+                priceInfoVO.setImgIcon(scenic.getImgIcon());
                 listData.add(priceInfoVO);
             }
         }
@@ -581,75 +588,50 @@ public class FapReportPortalController {
     }
 
     @GetMapping("/getRegion")
-    @ApiOperation("获取下拉树")
+    @ApiOperation("获取下拉树(只返回市级)")
     public CommonResult<List<Map<String, Object>>> getChildrenOrgs() {
         LambdaQueryWrapper<Org> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(Org::getOrgType, 0);
+        wrapper.eq(Org::getRegionLevel, 2);
+        wrapper.orderByAsc(Org::getRegionCode);
         List<Org> orgs = orgService.list(wrapper);
-        return CommonResult.<List<Map<String, Object>>>ok().value( buildRegionTree(orgs, null));
+
+        // 转换为返回格式
+        List<Map<String, Object>> cityList = new ArrayList<>();
+        for (Org org : orgs) {
+            Map<String, Object> cityMap = new HashMap<>();
+            cityMap.put("id", org.getId());
+            cityMap.put("name", org.getName());
+            cityMap.put("regionLevel", org.getRegionLevel());
+            cityList.add(cityMap);
+        }
+
+        return CommonResult.<List<Map<String, Object>>>ok().value(cityList);
     }
 
     /**
-     * 构建地区树结构
-     *
-     * @param orgs    组织列表
-     * @param rootOrg 根节点组织,如果为null则使用默认根节点判断逻辑
+     * 获取市及其下所有县的ID列表
+     * @param cityId 市ID
+     * @return 包含市ID和所有县ID的列表
      */
-    private List<Map<String, Object>> buildRegionTree(List<Org> orgs, Org rootOrg) {
+    private List<String> getCityAndCountyIds(String cityId) {
+        List<String> regionIds = new ArrayList<>();
+        regionIds.add(cityId); // 添加市本身
 
-        // 转换为Map格式,便于查找
-        Map<String, Map<String, Object>> orgMap = new HashMap<>();
-        for (Org org : orgs) {
-            Map<String, Object> region = new HashMap<>();
-            region.put("id", org.getId());
-            region.put("name", org.getName());
-            region.put("parentId", org.getParentId());
-            region.put("children", new ArrayList<Map<String, Object>>());
-
-            // 设置regionPath
-            Org parentOrganization = orgHierarchyUtil.getParentOrganization(org.getId());
-            if (parentOrganization != null) {
-                region.put("regionPath", parentOrganization.getName());
-            }
-
-            orgMap.put(org.getId(), region);
-        }
+        // 查询该市下的所有县
+        LambdaQueryWrapper<Org> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(Org::getOrgType, 0);
+        wrapper.eq(Org::getParentId, cityId);
+        List<Org> counties = orgService.list(wrapper);
 
-        // 构建树结构
-        List<Map<String, Object>> rootNodes = new ArrayList<>();
-        for (Map<String, Object> region : orgMap.values()) {
-            String parentId = (String) region.get("parentId");
-
-            if (rootOrg != null) {
-                // 如果是指定的根节点组织
-                if (rootOrg.getId().equals(region.get("id"))) {
-                    rootNodes.add(region);
-                } else {
-                    // 其他节点按照parentId关系添加到对应的父节点下
-                    Map<String, Object> parent = orgMap.get(parentId);
-                    if (parent != null) {
-                        @SuppressWarnings("unchecked")
-                        List<Map<String, Object>> children = (List<Map<String, Object>>) parent.get("children");
-                        children.add(region);
-                    }
-                }
-            } else {
-                // 管理员用户:使用默认的根节点判断逻辑
-                if (parentId == null || parentId.isEmpty() || "0".equals(parentId)) {
-                    rootNodes.add(region);
-                } else {
-                    Map<String, Object> parent = orgMap.get(parentId);
-                    if (parent != null) {
-                        @SuppressWarnings("unchecked")
-                        List<Map<String, Object>> children = (List<Map<String, Object>>) parent.get("children");
-                        children.add(region);
-                    }
-                }
-            }
+        for (Org county : counties) {
+            regionIds.add(county.getId());
         }
 
-        return rootNodes;
+        return regionIds;
     }
 
+
+
 }
 

+ 25 - 0
pricing/src/main/java/com/hotent/pricing/controller/report/api/IconManager.java

@@ -0,0 +1,25 @@
+package com.hotent.pricing.controller.report.api;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class IconManager {
+
+    private static final Map<String, String> iconMap = new HashMap<>();
+
+    static {
+        iconMap.put("出租车", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAmCAYAAAC29NkdAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAISSURBVFiF7ZixUttAEIb/3YBNaTpmaEiZzo3HlH6AFBq9AGpNJmP8BvYTGArjkuQFPGrSOx2QFO4ooaEnnQ3DbQqDrDvrCJJ8gszo77wn731a7f13EiGjmv5I0lx/MW5Tlnk4y5+KVAmYV9a+aPjDgIQ6RFR3CSAiUyE5+TX+8i1p/ENSsOmfHhF4REQ7LuEAgIh2COTtfvqM26sfP1fGzUDdG9QqXL0mUM013AqMwsfzsH0Tj630YIW3Om8BBwDC4pmxjfiPujeoAXKkF1a6F+PDYxdADX8YMPhsGaEOAG0urYKbXPHi1RPI3VzNE5t3HXpQ9yGA+CPda/jDwArIwgd6Cjqeht07V4BPuScvMUSA+96oBUJLG1T47gruWaTQ1wNoNbxhZG0RoDB1tAsFE3NFudBiDgrjMWaOWBjLxaGtIBLjzhzqUT2cGKFg3xvtRYAV3tKqJyLT87A9QUH6HX6dGIslshxeWstSRO57z5SCMp7YouW4aGuxyWY5XLS12GSzHEp7Mi5a7/48WALm1T8ApTtXs+25mm0L0FvftK/Pu2EbEKB3qR+z+k3/9A9Ag1xoKfNaK3ivZub2g3X4Y9q8/28PVrkamDFzz86itHlfNGoBes+PZJE4X/9lyVvuJHlVAuZVCZhX1q0u6xfRrLK5ybuvIJtvU3h6L3kDlkQOJoW+AXTDirrFsi2+MCRx/AVvpt6PXT+mFAAAAABJRU5ErkJggg==");
+        iconMap.put("公共汽车", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAlCAYAAAAwYKuzAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAJeSURBVFiF7ZhPchJBFMa/11ClOznCcALNQoZlcoNhLiDuJCkrcoLACSALMy7HC8DkBI67IFYlnkC8AVmZWPKeC0Lx703Rwx+ZSuXbdfN1969e9zSvHyFBZS9wxOBMIB6BCkm+TSSQIcGE9/y7eRPVh5rHJA4m6gKo7goOAMZzy4dn5vl1kkcFLHkXHkhe7QpMkfPa/1jVftAjaOjlromWQXLqmolb/P8l6lHKEKCuzAPmrVwsdYao18C6MqACDLVW+awACRT1o+PBVsgeVPYCR4DVgG4l+DIlkcte57i9TZC0KvnBGQkOJ+08aNoAaKtRWkcEOLNMmf9IngA31RPgpsoDCCcNYnzdL86YQWbClu91am/3CbSoq6gWzgYt81uceUCr/2Ix1HL9YKvJgozT/ZU+u2wG4m2OtDSnlSvzW/yYAKVOjCIxigBF6ZeiiJkPiFEUoGE7yu4jARrf5vPEiusHPwE4VqsI4l73XWWmp+n6F7fA6ozaKoKG8VmBDnW3wkeIF/uYZalPXdvGNAIvPQnTVBw0bw7GarxdBI05nW2P3xOsVgJ0iVf2grnjIIZOk/1TWd6DqLqVwAHJpYBeSPqajcNGrkt+0CbIrQjeAHalFVtAjN8JdEgpqOaHUwFAAyBQikke0z24H9lv8USCeCR/m+ssZijXIqKEs0dqMqICCvgXJQRXCPH37nurO2xRrv8pTvo4mEc/tH6Vot85CQFoj/jBH747XwcOAIjlXJ1X6KYfnagXf+IZvOe7A4DaMi0ahcQ4Sqol2+gqqg2IcQRBLJDhw9whiVSSxvwDXpC9pKMtIXsAAAAASUVORK5CYII=");
+        iconMap.put("轨道交通", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACcAAAAoCAYAAAB99ePgAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAOISURBVFiF7ZhBdtpIEIb/Koghq9ENhrmBeC8YdoYTpM0FHC81Wdg+geEEOG8elndmLkCUE9hLxVmEnCA6grwKzEuqZiHkABbQEHnMYv4VtLrVn7q7qquKsIVqpu8yUxPgA6g6ACogVB46KCKFxkQ0AvQLCQVh4EWbzkO2HRvGryirAegEmAGxlSJSwuAfGb8bBWdxLnAJFM4BvNkYKJNRY4AubCBXwtXa/TcMPt9qpSwgCeh+HP55sTFcvX3VA/Q0b6gMhIuJfOtmreIjONf0nBK9vAGp+/RgUymNJvqttQjIzw4GAKRuiV7eLDbPwZW43PvPwVKRuslRmmlKf9Tbl6cA9TJGBQod5c+CJgjNxXaBHH8avh08wLmm5+xx6SuBnLSTQmMWOgwD7zZvsFQN4zeVsbidEQlaYeBFRQDY4/IJAc58H7pIwVzTc8ooLzzfXmOM41FwFoeBd7vf9jsEdGYeV4RxBKBbTP7r6aLhsuDv2a/TvMgAlFBGw/itMPBuVeQDMc/CgRKH3+WG8Zuz27koYRzkyPXovQVw1i1R2TeXhpXp9VNM/qsi5gNWlUcWsxNSdXnVlj6nlNTluThsh0Qghy36PZv+h9tWOw1XXNeBoPdQ2jg5Wfte0vt1fdbCTcPopaH0U2qnt3Wn4dZuayrX9JwXvGcI/LuKfPgUvLUOQBvGrwjjiKD3E5kMbPNW65UrUfk9g68J6BDTTcP4VjdLzfRdYf2cxGzUy8oVfgmuZvrubEhNIEeZTqwm4MLR3P1N6r4yf1kFG1ZwWTFXkrlvpyKKVq7JCi4MvEjnQ+kojZTXiUTfAXiAUaBjW9SxPnN3Q68rItUf8r31cej9YTtBGHjRRMZVEamKSPVu6HVt51xqrUlFad75bmKhs5paZ+bYxIo3hAOot9/2f4PoF8GPmMBxClczfVch1kHqsrEFfvF6VT1mpZ8joAMmFFAECY4BjH7mmpv57zTbKoBdZb5OWlfndFYzTA/xIHGm+n4jqvQdjOuG8Sth4A0WjGupbG6IQXqIxxjHZSlXt4FLx2NqXPX2lbOuxEb1tr98bZeUpvJS/fDq86rC0aqVi0j1cBScxXkXEhXo3A29LqkeKuFmWeWUVR9XkBQap8WUpPqUb4WTgM5+2z8PAy8iQSvztlHcMisdQjFfSRIch4EXNYzfzC6L5QRoLk0YeBEST/CTS3VEiuN/Ad+tk6YAZKSeAAAAAElFTkSuQmCC");
+    }
+
+    // 根据名称检查并返回对应的图标
+    public static String getIconByName(String name) {
+        for (Map.Entry<String, String> entry : iconMap.entrySet()) {
+            if (name.contains(entry.getKey())) {
+                return entry.getValue();
+            }
+        }
+        return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAoCAYAAAB0HkOaAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAMESURBVFiF7ZhPVhpBEMa/KnhPsiM3mJwgs4jCDjxBBi6g7OJzoycQT6C+PJ3sQi4g5AQhuxgX6gmcG4Sdo3GqsmD+9oAiMLws/FbQ3VT96Krp6hrCDKo7riVQm5gbACyoVgFYIFjpdQodkdJIoSMi9gB4IsFNCTz8NdjxnvNDTwIwtgjYBrJO55LCU0LvQfyT68H+aCYY2zmqrvGbA0D3FgaYKjq+l7tDEyoDMwapXE3YCQ/AcG7fSjZIbdMmCTbT4SunZ9eo0p8UEvNHL9W6c2oz8ZUxbCnjAEAn9hN9CHflz2RzdBzI3+/zwpS4vIVx7uV0L/7bKFzxzlRQsXWqOd0rcbmQHCqjbEcpwNGgMBpFOHtOzOXYb7wzpGhm05kGgE58BBeSwgKhmfELHGZglNSmiEYxvOh/ai0dJFSt/aUPqDOmScAYYbYTqBqvJp07WWeT/Ex/++B8bsYwzNRMT5LQdZEogQQZ+yUu2UgSmN6nJ334hcI84tGwz40ERpO4QeFNqx3LUmg/PkQVMg5T3XGz1ZeKDVGixA+BquvOqc1BSJVQ6kpgcn4YNjOXMvkC0ZtVwJh+GNxgszAyVhMmhXrGgMXQbGlfpDq/RJeDXfNPW6yk1fRIrX1W4KXqCT8Ei2ptd3qxXrF4hjUr0yvMNE2C6aloS0Q60KU/5p5AOiraAtAzJzMXcgW6v893DtNgtbZ7u6S+aXjR39lMjQw22q5HQDca4GStjh7EP8nZEN1fGASAquZsG388G6Yiq7UgmGhbU1fbGIZA1Q3nzDEXE9PHZcCE7UpGdcdtpm+Y5qHnkaDjw7+uoFINe+2uaWR+JW3tevt0m8EH6Xx8PYGn6RVmmsqBPG4CAIEtZv5qLlDVa9Fg4bOGqXREROZrEahoK3rs44a27riWMm5zVvIn51yqtdwf6e4xEgneRRe6/ypMMYwPf/LpS1RoD5X2G8OMSwENzMWCYCl9t5B8yw0qhukSlAnTvdx1oBiG9cJToHt5vpsr9fNobEf3U7WoR5q8QgOAf5caQeBiMNFoAAAAAElFTkSuQmCC";  // 如果没有匹配项,返回默认图标
+    }
+}

+ 14 - 0
pricing/src/main/java/com/hotent/pricing/dao/FapScenicFinanceDao.java

@@ -0,0 +1,14 @@
+package com.hotent.pricing.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hotent.pricing.model.entity.report.FapScenicFinance;
+
+/**
+ * 景区收支情况表 Mapper 接口
+ *
+ * @author 超级管理员
+ * @since 2025-11-28
+ */
+public interface FapScenicFinanceDao extends BaseMapper<FapScenicFinance> {
+
+}

+ 15 - 1
pricing/src/main/java/com/hotent/pricing/manager/DataMiddlePlatformManager.java

@@ -5,6 +5,7 @@ package com.hotent.pricing.manager;/**
  * @create: 2025-11-06 11:36
  */
 
+import com.hotent.base.model.CommonResult;
 import org.springframework.http.ResponseEntity;
 
 /**
@@ -15,5 +16,18 @@ public interface DataMiddlePlatformManager {
 
     void sync() throws Exception;
 
-    ResponseEntity<?> ssoDataMiddlePlatform(String code) throws Exception;
+
+    /**
+     * mvue端单点登录
+     * @param code 授权码
+     * @return 登录结果
+     */
+    CommonResult<?> ssoForMvue(String code);
+
+    /**
+     * front端单点登录
+     * @param code 授权码
+     * @return 登录结果
+     */
+    ResponseEntity<?> ssoForFront(String code);
 }

+ 183 - 122
pricing/src/main/java/com/hotent/pricing/manager/impl/DataMiddlePlatformManagerImpl.java

@@ -7,12 +7,14 @@ import com.hotent.base.conf.SaaSConfig;
 import com.hotent.base.feign.UCFeignService;
 import com.hotent.base.jwt.JwtAuthenticationResponse;
 import com.hotent.base.jwt.JwtTokenHandler;
+import com.hotent.base.model.CommonResult;
 import com.hotent.base.util.*;
 import com.hotent.pricing.manager.DataMiddlePlatformManager;
 import com.hotent.uc.api.model.IUser;
 import com.hotent.uc.manager.OrgManager;
 import com.hotent.uc.manager.RoleManager;
 import com.hotent.uc.manager.UserManager;
+import com.hotent.uc.model.User;
 import com.hotent.uc.params.org.OrgVo;
 import com.hotent.uc.params.user.UserVo;
 import com.integration.oauth2.sdk.OAuth2Client;
@@ -46,17 +48,37 @@ import java.util.stream.Collectors;
 @Service
 public class DataMiddlePlatformManagerImpl implements DataMiddlePlatformManager {
 
-    @Value("${data.middle.platform.base-url:http://10.7.14.236:8280/stage-api/}")
-    private String baseUrl;
+    // mvue端
+    @Value("${oauth2.mvue.base-url:}")
+    private String mvueBaseUrl;
 
-    @Value("${data.middle.platform.client-id:cbjsxt}")
-    private String clientId;
+    @Value("${oauth2.mvue.client-id:}")
+    private String mvueClientId;
 
-    @Value("${data.middle.platform.client-secret:cbjsxt}")
-    private String clientSecret;
+    @Value("${oauth2.mvue.client-secret:}")
+    private String mvueClientSecret;
 
-    @Value("${data.middle.platform.public-key:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCMnhQ99yP-eEU2jXdQWc6j-wWbqNLqOLinEGBY11WJUCmzHiEycDXPc6-3YMOvrdAiHZcjkMCzU_eRnBLUqkcNw9nhQrCak-sTpEVlAV21LskD6KMf-6PsfttUvpXeCO5g3Hg48F_vbLKxb8s_lcvQgCpKBIpsUdYRcp_PgSg8BQIDAQAB}")
-    private String publicKey;
+    @Value("${oauth2.mvue.public-key:}")
+    private String mvuePublicKey;
+
+    @Value("${oauth2.mvue.redirect-uri:}")
+    private String mvueRedirectUri;
+
+    // front端
+    @Value("${oauth2.front.base-url:}")
+    private String frontBaseUrl;
+
+    @Value("${oauth2.front.client-id:}")
+    private String frontClientId;
+
+    @Value("${oauth2.front.client-secret:}")
+    private String frontClientSecret;
+
+    @Value("${oauth2.front.public-key:}")
+    private String frontPublicKey;
+
+    @Value("${oauth2.front.redirect-uri:}")
+    private String frontRedirectUri;
 
 
     @Autowired
@@ -75,24 +97,18 @@ public class DataMiddlePlatformManagerImpl implements DataMiddlePlatformManager
     JwtConfig jwtConfig;
 
     @Autowired
-    SaaSConfig saasConfig;
-
-    @Autowired
-    UCFeignService uCFeignService;
-
-    @Autowired
     RoleManager roleService;
 
     @Override
     public void sync() throws Exception {
         try {
-            OAuth2Client oAuth2Client = OAuth2ToMethod.OAuth2Method.initClient(baseUrl,
-                    clientId,
-                    clientSecret,
-                    publicKey
+            OAuth2Client oAuth2Client = OAuth2ToMethod.OAuth2Method.initClient(mvueBaseUrl,
+                    mvueClientId,
+                    mvueClientSecret,
+                    mvuePublicKey
                     , 30);
             System.err.println("oAuth2Client:"+ JSON.toJSONString(oAuth2Client));
-            String openApiToken = OAuth2ToMethod.getOpenApiToken("");
+            String openApiToken = OAuth2ToMethod.getOpenApiToken("admin");
             System.err.println("openApiToken:"+ JSON.toJSONString(openApiToken));
             //1.拉去中台组织数据,2.组装组织数据,3.并批量添加
             List<Org> orgsAll = OAuth2ToMethod.getOrgsAll(openApiToken, "");
@@ -103,51 +119,47 @@ public class DataMiddlePlatformManagerImpl implements DataMiddlePlatformManager
             }).collect(Collectors.toList());
             orgManager.addOrgs(orgVoList);
             //1.拉取中台用户数据,2.组装用户数据,3.批量添加
-            int j=1;
-            List<UserDetail> usersAll =new ArrayList<>();
-            for (int i = 1; i==j ; i++) {
-                usersAll = OAuth2ToMethod.getUsersAll(openApiToken, Integer.toString(i), "50");
+            List<UserDetail> usersAll = OAuth2ToMethod.getUsersAll(openApiToken, "1", "100000");
+            System.out.println("userList: " + usersAll.size());
+            if (usersAll != null && !usersAll.isEmpty()) {
                 List<UserVo> userVos = usersAll.stream().map(u -> {
                     UserVo userVo = new UserVo();
-                    BeanUtils.copyProperties(u,userVo);
+                    BeanUtils.copyProperties(u, userVo);
                     userVo.setOrgId(u.getDeptId());
                     return userVo;
                 }).collect(Collectors.toList());
                 userService.addUsers(userVos);
-                if (usersAll.size()==50) {
-                    j++;
-                }else {
-                    j=1;
-                }
             }
 
             //角色数据
-
-            List<com.hotent.uc.model.Role> roleList = new ArrayList<>();
-            for (int i = 1; i==j ; i++) {
-                List<Role> roles = OAuth2ToMethod.getRolesAll(openApiToken, "1", "50");
+            List<Role> roles = OAuth2ToMethod.getRolesAll(openApiToken, "1", "100000");
+            if (roles != null && !roles.isEmpty()) {
                 List<String> codes = roles.stream().map(Role::getCode).collect(Collectors.toList());
-                List<com.hotent.uc.model.Role> ro=roleService.list(
-                        new QueryWrapper<com.hotent.uc.model.Role>().in("CODE_",codes)
+                List<com.hotent.uc.model.Role> existingRoles = roleService.list(
+                        new QueryWrapper<com.hotent.uc.model.Role>().in("CODE_", codes)
                 );
+
+                List<com.hotent.uc.model.Role> roleList = new ArrayList<>();
                 for (Role role : roles) {
-                    com.hotent.uc.model.Role ored = ro.stream().filter(rol -> rol.getCode().equals(role.getCode())).findAny().orElse(null);
-                    if (ored==null) {
-                        com.hotent.uc.model.Role r = new com.hotent.uc.model.Role();
-                        r.setId(UniqueIdUtil.getSuid());
-                        r.setName(role.getName());
-                        r.setCode(role.getCode());
-                        r.setCreateTime(LocalDateTime.now());
-                        roleList.add(r);
+                    com.hotent.uc.model.Role existingRole = existingRoles.stream()
+                            .filter(r -> r.getCode().equals(role.getCode()))
+                            .findAny()
+                            .orElse(null);
+
+                    if (existingRole == null) {
+                        com.hotent.uc.model.Role newRole = new com.hotent.uc.model.Role();
+                        newRole.setId(UniqueIdUtil.getSuid());
+                        newRole.setName(role.getName());
+                        newRole.setCode(role.getCode());
+                        newRole.setCreateTime(LocalDateTime.now());
+                        roleList.add(newRole);
                     }
                 }
-                if (usersAll.size()==50) {
-                    j++;
-                }else {
-                    j=1;
+
+                if (!roleList.isEmpty()) {
+                    roleService.saveBatch(roleList);
                 }
             }
-            roleService.saveBatch(roleList);
 
 
         } catch (Exception e) {
@@ -155,88 +167,137 @@ public class DataMiddlePlatformManagerImpl implements DataMiddlePlatformManager
         }
     }
 
+
+
+
     @Override
-    public ResponseEntity<?> ssoDataMiddlePlatform(String code) throws Exception {
-        OAuth2Client oAuth2Client = OAuth2ToMethod.OAuth2Method.initClient(baseUrl,
-                clientId,
-                clientSecret,
-                publicKey
-                , 30);
-        AccessTokenResponse accessTokenWithCode = OAuth2ToMethod.getAccessTokenWithCode(code, "");
-        System.err.println("免登录:"+JSON.toJSON(accessTokenWithCode));
-        String accessToken = accessTokenWithCode.getValue().getAccess_token();
-        UserInfo userInfo = OAuth2ToMethod.getUserInfo(accessToken);
-        System.err.println("用户信息:"+JSON.toJSON(userInfo));
-        CacheEvictUtil.deleteUserDetailsCache(userInfo.getName());
-        // 当前切中的方法
-        HttpServletRequest request = HttpUtil.getRequest();
-        boolean isMobile = HttpUtil.isMobile(request);
-        //final UserDetails userDetails = userDetailsService.loadUserByUsername("zhangming0030");
-        final UserDetails userDetails = userDetailsService.loadUserByUsername(userInfo.getName());
-        final String token = jwtTokenHandler.generateToken(userDetails);
-        String userName = userDetails.getUsername();
-        String account = "";
-        String userId = "";
-        Map<String, Object> userAttrs = new HashMap<String, Object>();
-        if (userDetails instanceof IUser) {
-            IUser user = ((IUser) userDetails);
-            userName = user.getFullname();
-            account = user.getAccount();
-            userId = user.getUserId();
-            request.setAttribute("loginUser", String.format("%s[%s]", userName, account));
-            userAttrs.put("tenantId", user.getTenantId());
+    public CommonResult<?> ssoForMvue(String code) {
+        Map<String, Object> result = new HashMap<>();
+        if (StringUtil.isEmpty(code)) {
+            return new CommonResult<>(false, "参数错误");
         }
-        //获取超时时间
-        //logger.debug("通过单点认证登录成功。");
-        //处理单用户登录
-        //if (!(code.isPresent() && SsoConfig.MODE_JWT.equals(mode))) {
-            handleSingleLogin(isMobile, MapUtil.getString(userAttrs, "tenantId"), account, token);
-        //}
-        // Return the token
-        return ResponseEntity.ok(new JwtAuthenticationResponse(token, userName, account, userId, jwtConfig.getExpirationLong(), userAttrs));
-
-       /* User user = userService.getByAccount(userInfo.getName());
-        CacheEvictUtil.deleteUserDetailsCache(user.getAccount());*/
-        //return null;
-    }
 
+        // 使用mvue端的OAuth2配置
+        OAuth2ToMethod.OAuth2Method.initClient(mvueBaseUrl,
+                mvueClientId,
+                mvueClientSecret,
+                mvuePublicKey, 30);
+
+        try {
+            OAuth2Client oauth2Client = OAuth2Client.getInstance(mvueBaseUrl, mvueClientId, mvueClientSecret, mvuePublicKey);
+            oauth2Client.setTimeout(30);
+            AccessTokenResponse accessTokenResponse = oauth2Client.getAccessToken(code, mvueRedirectUri);
+
+            if (accessTokenResponse.getState()) {
+                String accessToken = accessTokenResponse.getValue().getAccess_token();
+                UserInfo userInfo = OAuth2ToMethod.getUserInfo(accessToken);
 
-    /**
-     * 处理单用户登录
-     *
-     * @param isMobile
-     * @param username
-     * @param token
-     */
-    private Map<String,Object> handleSingleLogin(boolean isMobile, String tenantId, String username, String token) {
-        Map<String,Object> result=new HashMap<>();
-        //如果是单用户登录
-        if (jwtConfig.isSingle()) {
-            String userAgent = isMobile ? "mobile" : "pc";
-            // 非SaaS模式
-            if (StringUtil.isEmpty(tenantId) && !saasConfig.isEnable()) {
-                tenantId = "-1";
+                if (userInfo != null) {
+                    User userDb = userService.getByAccount(userInfo.getName());
+                    if (userDb != null) {
+                        jwtTokenHandler.removeShortCache(code);
+                        CacheEvictUtil.deleteUserDetailsCache(userDb.getAccount());
+
+                        HttpServletRequest request = HttpUtil.getRequest();
+                        final UserDetails userDetails = userDetailsService.loadUserByUsername(userDb.getAccount());
+                        final String token = jwtTokenHandler.generateToken(userDetails);
+                        String userName = userDetails.getUsername();
+                        String account = "";
+                        String userId = "";
+                        boolean loginStatus = true;
+                        Map<String, Object> userAttrs = new HashMap<>();
+
+                        if (userDetails instanceof IUser) {
+                            IUser user = ((IUser) userDetails);
+                            userName = user.getFullname();
+                            account = user.getAccount();
+                            userId = user.getUserId();
+                            request.setAttribute("loginUser", String.format("%s[%s]", userName, account));
+                            userAttrs.put("tenantId", user.getTenantId());
+                            userAttrs.put("isAdmin", user.isAdmin());
+                        }
+
+                        result.put("token", token);
+                        result.put("username", account);
+                        result.put("userId", userId);
+                        result.put("expiration", jwtConfig.getExpirationLong());
+                        result.put("loginStatus", loginStatus);
+                        result.put("userAttrs", userAttrs);
+                        return new CommonResult<>(true, "单点登录成功", result);
+                    } else {
+                        throw new RuntimeException("当前系统查无此人!");
+                    }
+                } else {
+                    throw new RuntimeException("获取userInfo失败!");
+                }
+            } else {
+                throw new RuntimeException("获取accessToken失败!");
             }
-            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
-            // 从缓存中获取token
-            String oldToken = jwtTokenHandler.getTokenFromCache(userAgent, tenantId, username, jwtConfig.getExpiration());
-            if(StringUtil.isNotEmpty(oldToken)) {
-                if(jwtTokenHandler.validateToken(oldToken, userDetails) && !oldToken.equals(token)){
-//                    throw new BaseException(ResponseErrorEnums.KICK_OFF_BY_ANOTHER);
-                    System.out.println(username+":"+userAgent+"端重复登录了");
-                    result.put("flag",true);
-                    result.put("msg","当前账号已在另一地方登录,若不是本人操作,请注意账号安全");
+        } catch (Exception e) {
+            throw new RuntimeException("获取accessToken异常:" + e.getMessage());
+        }
+    }
+
+    @Override
+    public ResponseEntity<?> ssoForFront(String code) {
+        if (StringUtil.isEmpty(code)) {
+            throw new RuntimeException("code无效!");
+        }
+
+        // 使用front端的OAuth2配置
+        OAuth2ToMethod.OAuth2Method.initClient(frontBaseUrl,
+                frontClientId,
+                frontClientSecret,
+                frontPublicKey, 30);
+
+        try {
+            OAuth2Client oauth2Client = OAuth2Client.getInstance(frontBaseUrl, frontClientId, frontClientSecret, frontPublicKey);
+            oauth2Client.setTimeout(30);
+            AccessTokenResponse accessTokenResponse = oauth2Client.getAccessToken(code, frontRedirectUri);
+
+            if (accessTokenResponse.getState()) {
+                String accessToken = accessTokenResponse.getValue().getAccess_token();
+                UserInfo userInfo = OAuth2ToMethod.getUserInfo(accessToken);
+
+                if (userInfo != null) {
+                    User userDb = userService.getByAccount(userInfo.getName());
+                    if (userDb != null) {
+                        jwtTokenHandler.removeShortCache(code);
+                        CacheEvictUtil.deleteUserDetailsCache(userDb.getAccount());
+
+                        HttpServletRequest request = HttpUtil.getRequest();
+                        boolean isMobile = HttpUtil.isMobile(request);
+                        final UserDetails userDetails = userDetailsService.loadUserByUsername(userDb.getAccount());
+                        final String token = jwtTokenHandler.generateToken(userDetails);
+                        String userName = userDetails.getUsername();
+                        String account = "";
+                        String userId = "";
+                        boolean loginStatus = true;
+                        Map<String, Object> userAttrs = new HashMap<>();
+
+                        if (userDetails instanceof IUser) {
+                            IUser user = ((IUser) userDetails);
+                            userName = user.getFullname();
+                            account = user.getAccount();
+                            userId = user.getUserId();
+                            request.setAttribute("loginUser", String.format("%s[%s]", userName, account));
+                            userAttrs.put("tenantId", user.getTenantId());
+                            userAttrs.put("isAdmin", user.isAdmin());
+                        }
+
+                        return ResponseEntity.ok(new JwtAuthenticationResponse(token, userName, account, userId, jwtConfig.getExpirationLong(), loginStatus, userAttrs));
+                    } else {
+                        throw new RuntimeException("当前系统查无此人!");
+                    }
+                } else {
+                    throw new RuntimeException("获取userInfo失败!");
                 }
-            }else{
-                result.put("flag",false);
-                result.put("msg","无多处登录");
+            } else {
+                throw new RuntimeException("获取accessToken失败!");
             }
-            // 以当前登录设备、租户ID、用户账号为key将token存放到缓存中
-            jwtTokenHandler.putTokenInCache(userAgent, tenantId, username, jwtConfig.getExpiration(), token);
+        } catch (Exception e) {
+            throw new RuntimeException("获取accessToken异常:" + e.getMessage());
         }
-        //处理用户登录日志
-        uCFeignService.loginLog(username, isMobile ? "mobile" : "pc");
-        return result;
     }
 
 }

+ 89 - 0
pricing/src/main/java/com/hotent/pricing/manager/impl/FapScenicFinanceManagerImpl.java

@@ -0,0 +1,89 @@
+package com.hotent.pricing.manager.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.hotent.base.manager.impl.BaseManagerImpl;
+import com.hotent.base.util.AuthenticationUtil;
+import com.hotent.pricing.dao.FapScenicFinanceDao;
+import com.hotent.pricing.manager.FapScenicFinanceManager;
+import com.hotent.pricing.model.entity.report.FapScenicFinance;
+import com.hotent.uc.model.User;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 景区收支情况表 服务实现类
+ *
+ * @author 超级管理员
+ * @since 2025-11-28
+ */
+@Service
+public class FapScenicFinanceManagerImpl extends BaseManagerImpl<FapScenicFinanceDao, FapScenicFinance>
+        implements FapScenicFinanceManager {
+
+    @Override
+    public FapScenicFinance getDetail(String id) {
+        FapScenicFinance finance = this.getById(id);
+        if (finance != null && "0".equals(finance.getIsHistory())) {
+            // 查询历史记录
+            LambdaQueryWrapper<FapScenicFinance> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(FapScenicFinance::getRefId, id);
+            wrapper.eq(FapScenicFinance::getIsHistory, "1");
+            wrapper.orderByDesc(FapScenicFinance::getUpdateTime);
+            List<FapScenicFinance> historyList = this.list(wrapper);
+            finance.setHistoryList(historyList);
+        }
+        return finance;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void create(FapScenicFinance finance) {
+        // 获取当前用户信息
+        finance.setOrgId(AuthenticationUtil.getCurrentUserMainOrgId());
+
+        // 设置为当前记录
+        finance.setIsHistory("0");
+        finance.setRefId(null);
+
+        // 保存新增记录
+        this.save(finance);
+
+        // 新增后立即创建历史记录,记录初始状态
+        FapScenicFinance historyRecord = new FapScenicFinance();
+        BeanUtils.copyProperties(finance, historyRecord);
+        historyRecord.setId(null);
+        historyRecord.setIsHistory("1");
+        historyRecord.setRefId(finance.getId());
+        this.save(historyRecord);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void update(FapScenicFinance finance) {
+        // 查询原记录
+        FapScenicFinance currentRecord = this.getById(finance.getId());
+        if (currentRecord == null) {
+            throw new RuntimeException("记录不存在");
+        }
+
+        // 保存历史记录
+        if ("0".equals(currentRecord.getIsHistory())) {
+            FapScenicFinance historyRecord = new FapScenicFinance();
+            BeanUtils.copyProperties(currentRecord, historyRecord);
+            historyRecord.setId(null);
+            historyRecord.setIsHistory("1");
+            historyRecord.setRefId(currentRecord.getId());
+            this.save(historyRecord);
+        }
+
+        // 更新当前记录
+        finance.setIsHistory("0");
+        finance.setRefId(null);
+
+        this.updateById(finance);
+    }
+}

+ 7 - 0
pricing/src/main/java/com/hotent/pricing/model/entity/report/FapReportScenic.java

@@ -115,6 +115,11 @@ public class FapReportScenic extends CommonModel<FapReportScenic> {
     @JsonProperty("executeTime")
     private LocalDate executeTime;
 
+    @ApiModelProperty(value = "调整后执行时间")
+    @TableField("execute_time_adjust")
+    @JsonProperty("executeTimeAdjust")
+    private LocalDate executeTimeAdjust;
+
     @ApiModelProperty(value = "是否属于景区目录管理范围(是/否)")
     @TableField("in_catalog_scope")
     @JsonProperty("inCatalogScope")
@@ -288,6 +293,8 @@ public class FapReportScenic extends CommonModel<FapReportScenic> {
     @JsonProperty("refId")
     private Long refId;
 
+    private String imgIcon;
+
     @ApiModelProperty(value = "历史记录")
     @TableField(exist = false)
     @JsonProperty("historyList")

+ 271 - 0
pricing/src/main/java/com/hotent/pricing/model/entity/report/FapScenicFinance.java

@@ -0,0 +1,271 @@
+package com.hotent.pricing.model.entity.report;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.hotent.pricing.model.CommonModel;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 景区收支情况表
+ *
+ * @author 超级管理员
+ * @since 2025-11-28
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("fap_scenic_finance")
+@ApiModel("景区收支情况")
+public class FapScenicFinance extends CommonModel<FapScenicFinance> {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("主键ID")
+    @TableId("id")
+    @JsonProperty("id")
+    private String id;
+
+    @ApiModelProperty("景区ID")
+    @TableField("scenic_id")
+    @JsonProperty("scenicId")
+    private String scenicId;
+
+    @ApiModelProperty("景区名称")
+    @TableField(exist = false)
+    @JsonProperty("scenicName")
+    private String scenicName;
+
+    @ApiModelProperty("年份")
+    @TableField("year")
+    @JsonProperty("year")
+    private Integer year;
+
+    @ApiModelProperty("员工人数")
+    @TableField("employee_count")
+    @JsonProperty("employeeCount")
+    private Integer employeeCount;
+
+    // ==================== 总收入(万元) ====================
+
+    @ApiModelProperty("总收入-合计(万元)")
+    @TableField("revenue_total")
+    @JsonProperty("revenueTotal")
+    private BigDecimal revenueTotal;
+
+    @ApiModelProperty("总收入-门票(万元)")
+    @TableField("revenue_ticket")
+    @JsonProperty("revenueTicket")
+    private BigDecimal revenueTicket;
+
+    @ApiModelProperty("总收入-经营服务(万元)")
+    @TableField("revenue_service")
+    @JsonProperty("revenueService")
+    private BigDecimal revenueService;
+
+    @ApiModelProperty("总收入-财政拨款(万元)")
+    @TableField("revenue_fiscal")
+    @JsonProperty("revenueFiscal")
+    private BigDecimal revenueFiscal;
+
+    @ApiModelProperty("总收入-其他(万元)")
+    @TableField("revenue_other")
+    @JsonProperty("revenueOther")
+    private BigDecimal revenueOther;
+
+    // ==================== 总支出(万元) ====================
+
+    @ApiModelProperty("总支出-合计(万元)")
+    @TableField("expense_total")
+    @JsonProperty("expenseTotal")
+    private BigDecimal expenseTotal;
+
+    @ApiModelProperty("总支出-上缴财政(万元)")
+    @TableField("expense_fiscal_payment")
+    @JsonProperty("expenseFiscalPayment")
+    private BigDecimal expenseFiscalPayment;
+
+    @ApiModelProperty("总支出-人员(万元)")
+    @TableField("expense_personnel")
+    @JsonProperty("expensePersonnel")
+    private BigDecimal expensePersonnel;
+
+    @ApiModelProperty("总支出-办公(万元)")
+    @TableField("expense_office")
+    @JsonProperty("expenseOffice")
+    private BigDecimal expenseOffice;
+
+    @ApiModelProperty("总支出-建设维护(万元)")
+    @TableField("expense_maintenance")
+    @JsonProperty("expenseMaintenance")
+    private BigDecimal expenseMaintenance;
+
+    @ApiModelProperty("总支出-其他(万元)")
+    @TableField("expense_other")
+    @JsonProperty("expenseOther")
+    private BigDecimal expenseOther;
+
+    // ==================== 财务指标 ====================
+
+    @ApiModelProperty("利润(万元)")
+    @TableField("profit")
+    @JsonProperty("profit")
+    private BigDecimal profit;
+
+    @ApiModelProperty("税金(万元)")
+    @TableField("tax")
+    @JsonProperty("tax")
+    private BigDecimal tax;
+
+    @ApiModelProperty("税率(%)")
+    @TableField("tax_rate")
+    @JsonProperty("taxRate")
+    private BigDecimal taxRate;
+
+    // ==================== 月度游客数据(人) ====================
+
+    @ApiModelProperty("1月游客数(人)")
+    @TableField("visitor_jan")
+    @JsonProperty("visitorJan")
+    private Integer visitorJan;
+
+    @ApiModelProperty("2月游客数(人)")
+    @TableField("visitor_feb")
+    @JsonProperty("visitorFeb")
+    private Integer visitorFeb;
+
+    @ApiModelProperty("3月游客数(人)")
+    @TableField("visitor_mar")
+    @JsonProperty("visitorMar")
+    private Integer visitorMar;
+
+    @ApiModelProperty("4月游客数(人)")
+    @TableField("visitor_apr")
+    @JsonProperty("visitorApr")
+    private Integer visitorApr;
+
+    @ApiModelProperty("5月游客数(人)")
+    @TableField("visitor_may")
+    @JsonProperty("visitorMay")
+    private Integer visitorMay;
+
+    @ApiModelProperty("6月游客数(人)")
+    @TableField("visitor_jun")
+    @JsonProperty("visitorJun")
+    private Integer visitorJun;
+
+    @ApiModelProperty("7月游客数(人)")
+    @TableField("visitor_jul")
+    @JsonProperty("visitorJul")
+    private Integer visitorJul;
+
+    @ApiModelProperty("8月游客数(人)")
+    @TableField("visitor_aug")
+    @JsonProperty("visitorAug")
+    private Integer visitorAug;
+
+    @ApiModelProperty("9月游客数(人)")
+    @TableField("visitor_sep")
+    @JsonProperty("visitorSep")
+    private Integer visitorSep;
+
+    @ApiModelProperty("10月游客数(人)")
+    @TableField("visitor_oct")
+    @JsonProperty("visitorOct")
+    private Integer visitorOct;
+
+    @ApiModelProperty("11月游客数(人)")
+    @TableField("visitor_nov")
+    @JsonProperty("visitorNov")
+    private Integer visitorNov;
+
+    @ApiModelProperty("12月游客数(人)")
+    @TableField("visitor_dec")
+    @JsonProperty("visitorDec")
+    private Integer visitorDec;
+
+    // ==================== 游客统计汇总 ====================
+
+    @ApiModelProperty("本年度游客人数合计(人)")
+    @TableField("visitor_total")
+    @JsonProperty("visitorTotal")
+    private Integer visitorTotal;
+
+    @ApiModelProperty("优惠人数(人)")
+    @TableField("visitor_discount")
+    @JsonProperty("visitorDiscount")
+    private Integer visitorDiscount;
+
+    @ApiModelProperty("其中免票人数(人)")
+    @TableField("visitor_free")
+    @JsonProperty("visitorFree")
+    private Integer visitorFree;
+
+    @ApiModelProperty("免票人数占比(%)")
+    @TableField("visitor_free_rate")
+    @JsonProperty("visitorFreeRate")
+    private BigDecimal visitorFreeRate;
+
+    // ==================== 填报信息 ====================
+
+    @ApiModelProperty("单位名称")
+    @TableField("unit_name")
+    @JsonProperty("unitName")
+    private String unitName;
+
+    @ApiModelProperty("填表时间")
+    @TableField("fill_date")
+    @JsonProperty("fillDate")
+    private java.time.LocalDate fillDate;
+
+    @ApiModelProperty("景区门票价格工作负责人")
+    @TableField("responsible_person")
+    @JsonProperty("responsiblePerson")
+    private String responsiblePerson;
+
+    @ApiModelProperty("联系电话")
+    @TableField("contact_phone")
+    @JsonProperty("contactPhone")
+    private String contactPhone;
+
+    // ==================== 历史记录标识 ====================
+
+    @ApiModelProperty("是否历史记录(0-当前记录,1-历史记录)")
+    @TableField("is_history")
+    @JsonProperty("isHistory")
+    private String isHistory;
+
+    @ApiModelProperty("关联的当前记录ID")
+    @TableField("ref_id")
+    @JsonProperty("refId")
+    private String refId;
+
+
+    // ==================== 组织信息 ====================
+
+    @ApiModelProperty("所属组织ID")
+    @TableField("org_id")
+    @JsonProperty("orgId")
+    private String orgId;
+
+    @ApiModelProperty("所属组织名称")
+    @TableField("org_name")
+    @JsonProperty("orgName")
+    private String orgName;
+
+
+
+    // ==================== 历史记录列表(不存数据库) ====================
+
+    @ApiModelProperty("历史记录列表")
+    @TableField(exist = false)
+    @JsonProperty("historyList")
+    private List<FapScenicFinance> historyList;
+}

+ 2 - 0
pricing/src/main/java/com/hotent/pricing/model/vo/report/PriceInfoVO.java

@@ -31,5 +31,7 @@ public class PriceInfoVO implements Serializable {
     @ApiModelProperty(value = "价格2")
     @com.fasterxml.jackson.annotation.JsonProperty("price2")
     private String price2;
+
+    private String imgIcon;
 }
 

+ 10 - 2
pricing/src/main/java/com/hotent/pricing/util/FileUploadUtil.java

@@ -196,10 +196,18 @@ public class FileUploadUtil {
         // 统一为正斜杠,折叠重复的分隔符
         String unified = path.replace('\\', '/');
         unified = unified.replaceAll("/+", "/");
-        // 去掉路径开头多余的斜杠(避免拼接时产生双分隔符)
-        if (unified.startsWith("/")) {
+
+        // 保留绝对路径标识:
+        // - Linux: 以 / 开头
+        // - Windows: 以盘符开头(如 C:/ 或 D:/)
+        // 只去掉非绝对路径开头的多余斜杠
+        boolean isAbsolutePath = unified.startsWith("/") || unified.matches("^[a-zA-Z]:/.*");
+
+        if (!isAbsolutePath && unified.startsWith("/")) {
+            // 只有在不是绝对路径的情况下才去掉开头的斜杠
             unified = unified.replaceFirst("^/+", "");
         }
+
         // 转换为当前系统分隔符
         return unified.replace('/', File.separatorChar);
     }

+ 2 - 20
uc/src/main/java/com/hotent/uc/manager/impl/UserManagerImpl.java

@@ -3990,32 +3990,14 @@ public class UserManagerImpl extends BaseManagerImpl<UserDao, User> implements U
                         throw new RuntimeException("添加用户失败,帐号【" + user.getAccount() + "】已存在,请重新输入!");
                     }
 
-                /*User u = this.getByNumber(user.getUserNumber());
-                if(BeanUtils.isNotEmpty(u)){
-                    throw new RuntimeException("添加用户失败,工号【"+user.getUserNumber()+"】已存在,请重新输入!");
-                }
-
-                if(StringUtil.isNotEmpty(user.getMobile())){
-                    u = this.getByMobile(user.getMobile());
-                    if(BeanUtils.isNotEmpty(u)){
-                        throw new RuntimeException("添加用户失败,手机号【"+user.getMobile()+"】已存在,请重新输入!");
-                    }
-                }*/
-                /*if (!checkEmail(user.getEmail()) && !StringUtil.isEmpty(user.getEmail())){
-                    throw new RuntimeException("添加用户失败,邮箱格式不正确!");
-                }*/
-
                     User newUser = null;
                     newUser = UserVo.parser(user);
                     newUser.setStatus(User.STATUS_NORMAL);
                     newUser.setId(UniqueIdUtil.getSuid());
                     newUser.setCreateTime(LocalDateTime.now());
                     newUser.setFrom(User.FROM_RESTFUL);
-                    String password = user.getPassword();
-                    if (StringUtil.isEmpty(user.getPassword())) {
-                        password = pwdStrategyManager.getDefault().getInitPwd();
-                    }
-                    newUser.setPassword(passwordEncoder.encode(password));
+                    String password = pwdStrategyManager.getDefault().getInitPwd();
+                    newUser.setPassword(password);
                     newUser.setPwdCreateTime(LocalDateTime.now());
                     Integer status = BeanUtils.isNotEmpty(user.getStatus()) ? user.getStatus() : 1;
                     if (status != 1 && status != -1 && status != -2 && status != 0) {