package com.ruoyi.web.controller.system;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.jarvis.domain.GroupRebateExcelUpload;
import com.ruoyi.jarvis.domain.OrderRows;
import com.ruoyi.jarvis.service.IGroupRebateExcelUploadService;
import com.ruoyi.jarvis.service.impl.GroupRebateExcelImportService;
import com.ruoyi.jarvis.service.IOrderRowsService;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.http.HttpUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.jarvis.domain.JDOrder;
import com.ruoyi.jarvis.domain.dto.JDOrderSimpleDTO;
import com.ruoyi.jarvis.domain.dto.QuickRecordModelOption;
import com.ruoyi.jarvis.domain.dto.QuickRecordModelShopOption;
import com.ruoyi.jarvis.util.QuickRecordModelShopOptionUtil;
import com.ruoyi.jarvis.service.IJDOrderModelShopService;
import com.ruoyi.jarvis.service.IJDOrderProfitService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.jarvis.service.IJDOrderService;
import com.ruoyi.jarvis.service.IInstructionService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;

/**
 * JD订单列表Controller
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/system/jdorder")
public class JDOrderListController extends BaseController
{

    private static final String JARVIS_JAVA_SKEY = "2192057370ef8140c201079969c956a3";

    @Value("${jarvis.server.jarvis-java.base-url:http://127.0.0.1:6666}")
    private String jarvisJavaBaseUrl;

    @Value("${jarvis.server.jarvis-java.jd-api-path:/jd}")
    private String jdApiPath;

    private final IJDOrderService jdOrderService;
    private final IJDOrderProfitService jdOrderProfitService;
    private final IOrderRowsService orderRowsService;
    private final IInstructionService instructionService;
    private final GroupRebateExcelImportService groupRebateExcelImportService;
    private final IGroupRebateExcelUploadService groupRebateExcelUploadService;

    private final ObjectMapper objectMapper;
    private final ISysConfigService sysConfigService;
    private final IJDOrderModelShopService jdOrderModelShopService;

    public JDOrderListController(IJDOrderService jdOrderService, IJDOrderProfitService jdOrderProfitService,
                                 IOrderRowsService orderRowsService,
                                 IInstructionService instructionService,
                                 GroupRebateExcelImportService groupRebateExcelImportService,
                                 IGroupRebateExcelUploadService groupRebateExcelUploadService,
                                 ObjectMapper objectMapper,
                                 ISysConfigService sysConfigService,
                                 IJDOrderModelShopService jdOrderModelShopService) {
        this.jdOrderService = jdOrderService;
        this.jdOrderProfitService = jdOrderProfitService;
        this.orderRowsService = orderRowsService;
        this.instructionService = instructionService;
        this.groupRebateExcelImportService = groupRebateExcelImportService;
        this.groupRebateExcelUploadService = groupRebateExcelUploadService;
        this.objectMapper = objectMapper;
        this.sysConfigService = sysConfigService;
        this.jdOrderModelShopService = jdOrderModelShopService;
    }

    /**
     * 查询JD订单列表
     */
    @org.springframework.web.bind.annotation.GetMapping("/list")
    public TableDataInfo list(JDOrder query, HttpServletRequest request) {
        startPage();
        
        // 处理排序参数
        String orderBy = request.getParameter("orderBy");
        String isAsc = request.getParameter("isAsc");
        
        // 处理时间筛选参数
        String beginTimeStr = request.getParameter("beginTime");
        String endTimeStr = request.getParameter("endTime");
        
        if (beginTimeStr != null && !beginTimeStr.isEmpty()) {
            query.getParams().put("beginTime", beginTimeStr);
        }
        if (endTimeStr != null && !endTimeStr.isEmpty()) {
            query.getParams().put("endTime", endTimeStr);
        }
        
        // 处理完成日期筛选参数
        String hasFinishTime = request.getParameter("hasFinishTime");
        if (hasFinishTime != null && "true".equalsIgnoreCase(hasFinishTime)) {
            query.getParams().put("hasFinishTime", true);
        }
        
        // 处理混合搜索参数（订单号/第三方单号/分销标识）
        String orderSearch = request.getParameter("orderSearch");
        if (orderSearch != null && !orderSearch.trim().isEmpty()) {
            query.getParams().put("orderSearch", orderSearch.trim());
        }

        String rebateRemarkAbnormal = request.getParameter("rebateRemarkHasAbnormal");
        if (rebateRemarkAbnormal != null && !rebateRemarkAbnormal.isEmpty()) {
            query.setRebateRemarkHasAbnormal(Integer.valueOf(rebateRemarkAbnormal));
        }
        if ("true".equalsIgnoreCase(request.getParameter("hasRebateRemark"))) {
            query.getParams().put("hasRebateRemark", true);
        }
        if ("true".equalsIgnoreCase(request.getParameter("rebateWithoutUploadLink"))) {
            query.getParams().put("rebateWithoutUploadLink", true);
        }

        applyModelNumberExcludeFilter(query);

        java.util.List<JDOrder> list;
        if (orderBy != null && !orderBy.isEmpty()) {
            // 设置排序参数
            query.getParams().put("orderBy", orderBy);
            query.getParams().put("isAsc", isAsc);
            list = jdOrderService.selectJDOrderListWithSort(query);
        } else {
            list = jdOrderService.selectJDOrderList(query);
        }
        
        TableDataInfo dataTable = getDataTable(list);
        List<JDOrder> rows = (List<JDOrder>) dataTable.getRows();
        
        // 如果需要筛选完成日期不为空，先过滤后再关联查询
        if (hasFinishTime != null && "true".equalsIgnoreCase(hasFinishTime)) {
            // 先关联查询所有订单的完成时间
            for (JDOrder jdOrder : rows) {
                OrderRows orderRows = orderRowsService.selectOrderRowsByOrderId(jdOrder.getOrderId());
                if (orderRows != null) {
                    jdOrder.setProPriceAmount(orderRows.getProPriceAmount());
                    jdOrder.setFinishTime(orderRows.getFinishTime());
                    jdOrder.setOrderStatus(orderRows.getValidCode());
                    jdOrder.setExpressStatus(orderRows.getExpressStatus());
                } else {
                    jdOrder.setProPriceAmount(0.0);
                    jdOrder.setFinishTime(null);
                    jdOrder.setOrderStatus(null);
                    jdOrder.setExpressStatus(null);
                }
            }
            // 过滤掉完成时间为空的订单
            rows.removeIf(jdOrder -> jdOrder.getFinishTime() == null);
            // 更新总数
            dataTable.setTotal(rows.size());
        } else {
            // 正常关联查询
            for (JDOrder jdOrder : rows) {
                OrderRows orderRows = orderRowsService.selectOrderRowsByOrderId(jdOrder.getOrderId());
                if (orderRows != null) {
                    jdOrder.setProPriceAmount(orderRows.getProPriceAmount());
                    jdOrder.setFinishTime(orderRows.getFinishTime());
                    jdOrder.setOrderStatus(orderRows.getValidCode());
                    jdOrder.setExpressStatus(orderRows.getExpressStatus());
                } else {
                    jdOrder.setProPriceAmount(0.0);
                    jdOrder.setOrderStatus(null);
                    jdOrder.setExpressStatus(null);
                }
            }
        }

        return dataTable;
    }

    /**
     * 快捷录单页：型号下拉数据；每型号取 jd_order 主键最大的一条的付款与后返（通常即最近落库单）
     */
    @GetMapping("/quickRecord/modelOptions")
    public AjaxResult quickRecordModelOptions() {
        List<QuickRecordModelOption> options = jdOrderService.selectQuickRecordModelOptions();
        return AjaxResult.success(options);
    }

    /**
     * 快捷录单页：型号后缀店铺下拉（系统参数 quickRecord.modelShopOptions，每行格式：短前缀（完整店名））
     */
    @GetMapping("/quickRecord/shopOptions")
    public AjaxResult quickRecordShopOptions() {
        String raw = sysConfigService.selectConfigByKey(QuickRecordModelShopOptionUtil.CONFIG_KEY);
        List<QuickRecordModelShopOption> options = QuickRecordModelShopOptionUtil.parseOptions(raw);
        return AjaxResult.success(options);
    }

    /**
     * 一键迁移：将 model_number 末尾匹配的店铺前缀写入 model_shop，并从 model_number 截掉。
     * 需先配置 quickRecord.modelShopOptions。
     */
    @Log(title = "JD订单型号店铺拆分迁移", businessType = BusinessType.UPDATE)
    @PostMapping("/migrateModelShop")
    public AjaxResult migrateModelShop() {
        return AjaxResult.success(jdOrderService.migrateModelShopSplit());
    }

    /**
     * 导入跟团返现类 Excel：按「单号/订单号」匹配系统订单，将「是否返现」「总共返现」等写入后返备注（可多次导入累加）；文件落盘并记上传记录。
     */
    @Log(title = "JD订单后返表导入", businessType = BusinessType.IMPORT)
    @PostMapping("/importGroupRebateExcel")
    public AjaxResult importGroupRebateExcel(@RequestParam("file") MultipartFile file,
                                             @RequestParam(value = "documentTitle", required = false) String documentTitle) {
        try {
            Map<String, Object> data = groupRebateExcelImportService.importExcel(file, documentTitle);
            return AjaxResult.success(data);
        } catch (Exception e) {
            return AjaxResult.error("导入失败: " + e.getMessage());
        }
    }

    /**
     * 批量导入后返表：请求内按顺序处理多个文件，前端仅需一次提交；结果概要返回，明细见上传记录。
     */
    @Log(title = "JD订单后返表批量导入", businessType = BusinessType.IMPORT)
    @PostMapping("/importGroupRebateExcelBatch")
    public AjaxResult importGroupRebateExcelBatch(@RequestParam("files") MultipartFile[] files,
                                                  @RequestParam(value = "documentTitle", required = false) String documentTitle) {
        try {
            Map<String, Object> data = groupRebateExcelImportService.importExcelBatch(files, documentTitle);
            if (Boolean.FALSE.equals(data.get("success"))) {
                return AjaxResult.error((String) data.get("message"));
            }
            return AjaxResult.success(data);
        } catch (Exception e) {
            return AjaxResult.error("批量导入失败: " + e.getMessage());
        }
    }

    /**
     * 后返表上传记录（分页）
     */
    @GetMapping("/groupRebateUpload/list")
    public TableDataInfo groupRebateUploadList(GroupRebateExcelUpload query, HttpServletRequest request) {
        startPage();
        String beginTimeStr = request.getParameter("beginTime");
        String endTimeStr = request.getParameter("endTime");
        if (beginTimeStr != null && !beginTimeStr.isEmpty()) {
            query.getParams().put("beginTime", beginTimeStr);
        }
        if (endTimeStr != null && !endTimeStr.isEmpty()) {
            query.getParams().put("endTime", endTimeStr);
        }
        List<GroupRebateExcelUpload> list = groupRebateExcelUploadService.selectGroupRebateExcelUploadList(query);
        return getDataTable(list);
    }

    /**
     * 删除一条后返表上传记录，并撤销写入订单的后返备注（依赖 uploadRecordId / affectedOrderIds；历史导入可能仅删记录）
     */
    @Log(title = "后返表上传记录", businessType = BusinessType.DELETE)
    @DeleteMapping("/groupRebateUpload/{id}")
    public AjaxResult deleteGroupRebateUpload(@PathVariable("id") Long id) {
        return AjaxResult.success(groupRebateExcelImportService.deleteUploadRecord(id));
    }

    /**
     * 重新下载已上传的后返表原件（与通用 download 一致，使用 POST + blob）
     */
    @Log(title = "后返表上传记录下载", businessType = BusinessType.EXPORT)
    @PostMapping("/groupRebateUpload/download/{id}")
    public void downloadGroupRebateUpload(@PathVariable("id") Long id, HttpServletResponse response) throws Exception {
        GroupRebateExcelUpload rec = groupRebateExcelUploadService.selectGroupRebateExcelUploadById(id);
        if (rec == null || StringUtils.isEmpty(rec.getFilePath())) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        File file = GroupRebateExcelImportService.resolveDiskFile(rec.getFilePath());
        if (!file.isFile()) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        String downloadName = StringUtils.isNotEmpty(rec.getOriginalFilename()) ? rec.getOriginalFilename() : file.getName();
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        FileUtils.setAttachmentResponseHeader(response, downloadName);
        FileUtils.writeBytes(file.getAbsolutePath(), response.getOutputStream());
    }

    /**
     * 导出JD订单列表
     */
    @Log(title = "JD订单", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, JDOrder jdOrder) throws IOException
    {
        String fileName = "JD订单数据";

        applyModelNumberExcludeFilter(jdOrder);

        List<JDOrder> list = jdOrderService.selectJDOrderList(jdOrder);

        // 设置响应头，指定文件名
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String encodedFileName = java.net.URLEncoder.encode(fileName + ".xlsx", "UTF-8");
        response.setHeader("Content-Disposition", "attachment; filename=" + encodedFileName);
        // 添加download-filename响应头，以支持前端工具类
        response.setHeader("download-filename", encodedFileName);

        ExcelUtil<JDOrder> util = new ExcelUtil<JDOrder>(JDOrder.class);
        util.exportExcel(response, list, fileName);
    }

    /**
     * 获取JD订单详细信息
     */
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id)
    {
        return success(jdOrderService.selectJDOrderById(id));
    }

    /**
     * 新增JD订单
     */
    @Log(title = "JD订单", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody JDOrder jdOrder)
    {
        return toAjax(jdOrderService.insertJDOrder(jdOrder));
    }

    /**
     * 修改JD订单（PUT body 使用 JsonNode：extraCost / extra_cost 从原始 JSON 显式写入实体，
     * 避免个别 Jackson + Lombok 场景下 treeToValue 未映射导致重算利润仍按 0 计算）
     */
    @Log(title = "JD订单", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody JsonNode root) throws JsonProcessingException {
        JDOrder jdOrder = objectMapper.treeToValue(root, JDOrder.class);
        applyExtraCostFromPayload(root, jdOrder);
        jdOrderModelShopService.normalizeOrder(jdOrder);
        jdOrderProfitService.recalculate(jdOrder);
        jdOrder.getParams().put("applyProfitFields", Boolean.TRUE);
        int rows = jdOrderService.updateJDOrder(jdOrder);
        if (rows <= 0) {
            return AjaxResult.error("更新失败或订单不存在");
        }
        return AjaxResult.success(jdOrderService.selectJDOrderById(jdOrder.getId()));
    }

    private static void applyExtraCostFromPayload(JsonNode root, JDOrder order) {
        if (root == null || order == null) {
            return;
        }
        JsonNode n = root.get("extraCost");
        if (readExtraCostNumber(n, order)) {
            return;
        }
        n = root.get("extra_cost");
        readExtraCostNumber(n, order);
    }

    /** @return true 已从节点解析并写入 order */
    private static boolean readExtraCostNumber(JsonNode n, JDOrder order) {
        if (n == null || n.isNull()) {
            return false;
        }
        if (n.isNumber()) {
            order.setExtraCost(n.asDouble());
            return true;
        }
        if (n.isTextual()) {
            try {
                order.setExtraCost(Double.parseDouble(n.asText().trim()));
                return true;
            } catch (NumberFormatException ignored) {
                return false;
            }
        }
        return false;
    }

    /**
     * 删除JD订单
     */
    @Log(title = "JD订单", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable Long[] ids)
    {
        return toAjax(jdOrderService.deleteJDOrderByIds(ids));
    }

    /**
     * 订单搜索工具接口（返回简易字段）
     */
    @Anonymous
    @GetMapping("/tools/search")
    public TableDataInfo searchOrders(
            @RequestParam(required = false) String orderSearch,
            @RequestParam(required = false) String address,
            HttpServletRequest request)
    {
        // startPage会从request中读取pageNum和pageSize参数
        startPage();
        
        JDOrder query = new JDOrder();
        
        // 处理单号搜索（过滤TF、H、F、PDD等关键词）
        if (orderSearch != null && !orderSearch.trim().isEmpty()) {
            String searchKeyword = orderSearch.trim().toUpperCase();
            // 过滤掉TF、H、F、PDD等关键词
            if (searchKeyword.contains("TF") || searchKeyword.contains("H") || 
                searchKeyword.contains("F") || searchKeyword.contains("PDD")) {
                // 如果包含过滤关键词，返回空结果
                return getDataTable(new java.util.ArrayList<>());
            }
            // 至少5个字符
            if (searchKeyword.length() >= 5) {
                query.getParams().put("orderSearch", orderSearch.trim());
            }
        }
        
        // 处理地址搜索（至少3个字符）
        if (address != null && !address.trim().isEmpty()) {
            if (address.trim().length() >= 3) {
                query.setAddress(address.trim());
            }
        }
        
        // 如果没有有效的搜索条件，返回空结果
        if ((orderSearch == null || orderSearch.trim().isEmpty() || orderSearch.trim().length() < 5) &&
            (address == null || address.trim().isEmpty() || address.trim().length() < 3)) {
            return getDataTable(new java.util.ArrayList<>());
        }
        
        java.util.List<JDOrder> list = jdOrderService.selectJDOrderList(query);
        
        // 转换为简易DTO，只返回前端需要的字段，其他字段脱敏
        java.util.List<JDOrderSimpleDTO> simpleList = new java.util.ArrayList<>();
        for (JDOrder jdOrder : list) {
            JDOrderSimpleDTO dto = new JDOrderSimpleDTO();
            
            // 只设置前端需要的字段
            dto.setRemark(jdOrder.getRemark());
            dto.setOrderId(jdOrder.getOrderId());
            dto.setThirdPartyOrderNo(jdOrder.getThirdPartyOrderNo());
            dto.setModelNumber(jdOrder.getModelNumber());
            dto.setAddress(jdOrder.getAddress());
            dto.setIsRefunded(jdOrder.getIsRefunded() != null ? jdOrder.getIsRefunded() : 0);
            dto.setIsRebateReceived(jdOrder.getIsRebateReceived() != null ? jdOrder.getIsRebateReceived() : 0);
            dto.setStatus(jdOrder.getStatus());
            dto.setCreateTime(jdOrder.getCreateTime());
            
            // 关联查询订单状态和赔付金额
            OrderRows orderRows = orderRowsService.selectOrderRowsByOrderId(jdOrder.getOrderId());
            if (orderRows != null) {
                dto.setProPriceAmount(orderRows.getProPriceAmount());
                dto.setOrderStatus(orderRows.getValidCode());
            } else {
                dto.setProPriceAmount(0.0);
                dto.setOrderStatus(null);
            }
            
            simpleList.add(dto);
        }
        
        return getDataTable(simpleList);
    }

    /**
     * 列表刷新后：对利润未手动的订单按规则重算，仅结果变化时落库（不解除售价/利润锁定）
     */
    @Log(title = "JD订单同步自动利润", businessType = BusinessType.UPDATE)
    @PostMapping("/tools/sync-auto-profit")
    @SuppressWarnings("unchecked")
    public AjaxResult syncAutoProfit(@RequestBody(required = false) Map<String, Object> body) {
        if (body == null || !body.containsKey("ids")) {
            return AjaxResult.error("请传入 ids 数组");
        }
        Object raw = body.get("ids");
        if (!(raw instanceof List)) {
            return AjaxResult.error("ids 须为数组");
        }
        List<?> idList = (List<?>) raw;
        if (idList.isEmpty()) {
            Map<String, Object> empty = new HashMap<>(2);
            empty.put("updated", 0);
            return AjaxResult.success(empty);
        }
        List<Long> ids = new ArrayList<>(idList.size());
        for (Object o : idList) {
            if (o == null) {
                continue;
            }
            ids.add(((Number) o).longValue());
        }
        int n = jdOrderProfitService.syncAutoProfitIfChanged(ids);
        Map<String, Object> data = new HashMap<>(2);
        data.put("updated", n);
        return AjaxResult.success(data);
    }

    /**
     * 按 ID 批量重算售价（自动从型号配置回填）与利润（清除手动锁定后按规则计算）
     */
    @Log(title = "JD订单批量重算利润", businessType = BusinessType.UPDATE)
    @PostMapping("/tools/recalc-profit")
    @SuppressWarnings("unchecked")
    public AjaxResult recalcProfitBatch(@RequestBody(required = false) Map<String, Object> body) {
        if (body == null || !body.containsKey("ids")) {
            return AjaxResult.error("请传入 ids 数组");
        }
        Object raw = body.get("ids");
        if (!(raw instanceof List)) {
            return AjaxResult.error("ids 须为数组");
        }
        List<?> idList = (List<?>) raw;
        if (idList.isEmpty()) {
            return AjaxResult.error("ids 不能为空");
        }
        int affected = 0;
        for (Object o : idList) {
            if (o == null) {
                continue;
            }
            long id = ((Number) o).longValue();
            JDOrder order = jdOrderService.selectJDOrderById(id);
            if (order == null) {
                continue;
            }
            order.setProfitManual(0);
            order.setSellingPriceManual(0);
            jdOrderProfitService.recalculate(order);
            order.getParams().put("applyProfitFields", Boolean.TRUE);
            affected += jdOrderService.updateJDOrder(order);
        }
        return AjaxResult.success("已更新 " + affected + " 条订单的售价/利润字段");
    }

    @Log(title = "批量标记后返到账", businessType = BusinessType.UPDATE)
    @RequestMapping(value = "/tools/batch-mark-rebate-received", method = {RequestMethod.POST, RequestMethod.GET})
    public AjaxResult batchMarkRebateReceivedForCompensation() {
        try {
            // 调用批量更新方法
            if (instructionService instanceof com.ruoyi.jarvis.service.impl.InstructionServiceImpl) {
                ((com.ruoyi.jarvis.service.impl.InstructionServiceImpl) instructionService)
                    .batchMarkRebateReceivedForCompensation();
                return AjaxResult.success("批量标记后返到账完成，请查看控制台日志");
            } else {
                return AjaxResult.error("无法执行批量更新操作");
            }
        } catch (Exception e) {
            return AjaxResult.error("批量标记失败: " + e.getMessage());
        }
    }

    /**
     * 生成录单格式文本（Excel可粘贴格式）
     * 根据当前查询条件生成Tab分隔的文本，可以直接粘贴到Excel
     */
    @GetMapping("/generateExcelText")
    public AjaxResult generateExcelText(JDOrder query, HttpServletRequest request) {
        try {
            // 处理时间筛选参数
            String beginTimeStr = request.getParameter("beginTime");
            String endTimeStr = request.getParameter("endTime");
            
            if (beginTimeStr != null && !beginTimeStr.isEmpty()) {
                query.getParams().put("beginTime", beginTimeStr);
            }
            if (endTimeStr != null && !endTimeStr.isEmpty()) {
                query.getParams().put("endTime", endTimeStr);
            }
            
            // 处理混合搜索参数
            String orderSearch = request.getParameter("orderSearch");
            if (orderSearch != null && !orderSearch.trim().isEmpty()) {
                query.getParams().put("orderSearch", orderSearch.trim());
            }

            String rebateRemarkAbnormal = request.getParameter("rebateRemarkHasAbnormal");
            if (rebateRemarkAbnormal != null && !rebateRemarkAbnormal.isEmpty()) {
                query.setRebateRemarkHasAbnormal(Integer.valueOf(rebateRemarkAbnormal));
            }
            if ("true".equalsIgnoreCase(request.getParameter("hasRebateRemark"))) {
                query.getParams().put("hasRebateRemark", true);
            }
            if ("true".equalsIgnoreCase(request.getParameter("rebateWithoutUploadLink"))) {
                query.getParams().put("rebateWithoutUploadLink", true);
            }

            // 处理其他查询参数
            if (query.getRemark() != null && !query.getRemark().trim().isEmpty()) {
                query.setRemark(query.getRemark().trim());
            }
            if (query.getDistributionMark() != null && !query.getDistributionMark().trim().isEmpty()) {
                query.setDistributionMark(query.getDistributionMark().trim());
            }
            if (query.getModelNumber() != null && !query.getModelNumber().trim().isEmpty()) {
                query.setModelNumber(query.getModelNumber().trim());
            }
            applyModelNumberExcludeFilter(query);
            if (query.getBuyer() != null && !query.getBuyer().trim().isEmpty()) {
                query.setBuyer(query.getBuyer().trim());
            }
            if (query.getAddress() != null && !query.getAddress().trim().isEmpty()) {
                query.setAddress(query.getAddress().trim());
            }
            if (query.getStatus() != null && !query.getStatus().trim().isEmpty()) {
                query.setStatus(query.getStatus().trim());
            }
            
            // 获取订单列表（不分页，获取所有符合条件的订单）
            List<JDOrder> list = jdOrderService.selectJDOrderList(query);
            
            if (list == null || list.isEmpty()) {
                return AjaxResult.success("暂无订单数据");
            }
            
            // 关联查询订单状态和赔付金额
            for (JDOrder jdOrder : list) {
                OrderRows orderRows = orderRowsService.selectOrderRowsByOrderId(jdOrder.getOrderId());
                if (orderRows != null) {
                    jdOrder.setProPriceAmount(orderRows.getProPriceAmount());
                    // estimateCosPrice 是京粉实际价格
                    if (orderRows.getEstimateCosPrice() != null) {
                        jdOrder.setJingfenActualPrice(orderRows.getEstimateCosPrice());
                    }
                }
            }
            
            // 按 remark 排序
            list.sort((o1, o2) -> {
                String r1 = o1.getRemark() != null ? o1.getRemark() : "";
                String r2 = o2.getRemark() != null ? o2.getRemark() : "";
                return r1.compareTo(r2);
            });
            
            // 生成Excel格式文本（Tab分隔）
            StringBuilder sb = new StringBuilder();
            for (JDOrder o : list) {
                // 日期（格式：yyyy/MM/dd）
                String dateStr = "";
                if (o.getOrderTime() != null) {
                    java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy/MM/dd");
                    dateStr = sdf.format(o.getOrderTime());
                }
                
                // 多多单号（第三方单号，如果没有则使用内部单号）
                String duoduoOrderNo = o.getThirdPartyOrderNo() != null && !o.getThirdPartyOrderNo().trim().isEmpty() 
                    ? o.getThirdPartyOrderNo() : (o.getRemark() != null ? o.getRemark() : "");
                
                // 型号（对外完整型号 = 本体 + 店铺前缀）
                String modelNumber = QuickRecordModelShopOptionUtil.fullModelNumber(o);
                
                // 数量（固定为1）
                String quantity = "1";
                
                // 地址
                String address = o.getAddress() != null ? o.getAddress() : "";
                
                // 姓名（从地址中提取，地址格式通常是"姓名 电话 详细地址"）
                String buyer = "";
                if (address != null && !address.trim().isEmpty()) {
                    String[] addressParts = address.trim().split("\\s+");
                    if (addressParts.length > 0) {
                        buyer = addressParts[0];
                    }
                }
                
                // 售价（固定为0）
                String sellingPriceStr = "0";
                
                // 成本（售价是0，成本也设为空）
                String costStr = "";
                
                // 利润（后返金额）
                Double rebate = o.getRebateAmount() != null ? o.getRebateAmount() : 0.0;
                String profitStr = rebate > 0 
                    ? String.format(java.util.Locale.ROOT, "%.2f", rebate) : "";
                
                // 京东单号
                String orderId = o.getOrderId() != null ? o.getOrderId() : "";
                
                // 物流链接
                String logisticsLink = o.getLogisticsLink() != null ? o.getLogisticsLink() : "";
                
                // 下单付款
                String paymentAmountStr = o.getPaymentAmount() != null 
                    ? String.format(java.util.Locale.ROOT, "%.2f", o.getPaymentAmount()) : "";
                
                // 后返
                String rebateAmountStr = o.getRebateAmount() != null 
                    ? String.format(java.util.Locale.ROOT, "%.2f", o.getRebateAmount()) : "";
                
                // 按顺序拼接：日期、多多单号、型号、数量、姓名、地址、售价、成本、利润、京东单号、物流、下单付款、后返
                sb.append(dateStr).append('\t')
                  .append(duoduoOrderNo).append('\t')
                  .append(modelNumber).append('\t')
                  .append(quantity).append('\t')
                  .append(buyer).append('\t')
                  .append(address).append('\t')
                  .append(sellingPriceStr).append('\t')
                  .append(costStr).append('\t')
                  .append(profitStr).append('\t')
                  .append(orderId).append('\t')
                  .append(logisticsLink).append('\t')
                  .append(paymentAmountStr).append('\t')
                  .append(rebateAmountStr).append('\n');
            }
            
            return AjaxResult.success(sb.toString());
        } catch (Exception e) {
            return AjaxResult.error("生成失败: " + e.getMessage());
        }
    }

    /**
     * 型号不含：支持逗号、中文逗号、空格、换行分隔的多个关键词，型号不得包含任一关键词。
     */
    private void applyModelNumberExcludeFilter(JDOrder query) {
        if (query == null) {
            return;
        }
        List<String> terms = parseModelNumberExcludeTerms(query.getModelNumberExclude());
        if (!terms.isEmpty()) {
            query.getParams().put("modelNumberExcludeList", terms);
        }
        query.setModelNumberExclude(null);
    }

    private static List<String> parseModelNumberExcludeTerms(String raw) {
        List<String> out = new ArrayList<>();
        if (StringUtils.isEmpty(raw)) {
            return out;
        }
        for (String part : raw.split("[,，\\s\\n]+")) {
            String t = part.trim();
            if (!t.isEmpty() && !out.contains(t)) {
                out.add(t);
            }
        }
        return out;
    }

    /**
     * 回填京粉订单 goodsInfo（店铺名、商品图）
     * Body: { "orderIds": "352543902480387,3525433002460987" }
     * 或: { "missing": true, "limit": 100 }
     */
    @Log(title = "京粉订单goodsInfo回填", businessType = BusinessType.OTHER)
    @PostMapping("/orderRows/backfillGoodsInfo")
    public AjaxResult backfillGoodsInfo(@RequestBody Map<String, Object> body) {
        try {
            JSONObject param = new JSONObject();
            param.put("skey", JARVIS_JAVA_SKEY);
            if (body != null) {
                if (body.get("orderIds") != null) {
                    param.put("orderIds", body.get("orderIds"));
                }
                if (body.get("missing") != null) {
                    param.put("missing", body.get("missing"));
                }
                if (body.get("limit") != null) {
                    param.put("limit", body.get("limit"));
                }
            }
            String url = jarvisJavaBaseUrl + jdApiPath + "/backfillGoodsInfo";
            String resp = HttpUtils.sendJsonPost(url, param.toJSONString());
            if (StringUtils.isEmpty(resp)) {
                return AjaxResult.error("Jarvis 服务无响应，请检查 jarvis.server.jarvis-java.base-url");
            }
            Object parsed = JSON.parse(resp);
            if (parsed instanceof JSONObject) {
                JSONObject obj = (JSONObject) parsed;
                if (obj.containsKey("error")) {
                    return AjaxResult.error(String.valueOf(obj.get("error")));
                }
            }
            return AjaxResult.success(parsed);
        } catch (Exception e) {
            return AjaxResult.error("回填失败: " + e.getMessage());
        }
    }
}
