using BLW_Log.Models;
using Common;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NPOI.HSSF.Record;
using Org.BouncyCastle.Asn1.Ocsp;
using System.Data;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;

namespace BLW_Log.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class PanelSelectionController : ControllerBase
    {
        public readonly IMemoryCache _memoryCache;
        private readonly IWebHostEnvironment _webHostEnvironment;

        public PanelSelectionController(IMemoryCache memoryCache, IWebHostEnvironment webHostEnvironment)
        {
            this._memoryCache = memoryCache;
            this._webHostEnvironment = webHostEnvironment;
        }

        #region 图纸操作
        /// <summary>
        /// 查看预览图
        /// </summary>
        [HttpPost]
        public ReturnInfo SendToCDR([FromBody] RedisInfoNode request)
        {
            try
            {
                // 记录日志
                LogOperation(request.User, "SendToCDR", request.ProjectName, request.PictureNum, request);

                bool success = PublishToRedis(new RedisInfo
                {
                    Type = "CellCorelDRAW",
                    ObjToRedis = new CellCorelDRAW() { MsgType = 1, MsgNode = request }
                });
                if (!success)
                {
                    return new ReturnInfo
                    {
                        isok = false,
                        message = "发送到Redis失败",
                        status = 500,
                        response = null
                    };
                }

                DateTime startTime = DateTime.Now;
                Dictionary<string, string> pcn = new()
                {
                    { request.PictureNum, request.Guid }
                };

                CSRedisCacheHelper.LPush("PicNumList", pcn);

                return new ReturnInfo
                {
                    isok = true,
                    message = "",
                    status = 200,
                    response = "图片已经加入队列，请稍后查看"
                };
            }
            catch (Exception ex)
            {
                return new ReturnInfo
                {
                    isok = false,
                    message = ex.Message,
                    status = 500,
                    response = null
                };
            }
        }


        #endregion

        #region 查询图纸数据表
        [HttpPost]
        public ReturnInfo ClearCache(string sql)
        {
            _memoryCache.Remove("SeriesQuery");
            _memoryCache.Remove("MaterialQuery");
            _memoryCache.Remove("IconLibraryQuery");
            _memoryCache.Remove("ModelQuery");
            _memoryCache.Remove("PatternQuery");
            _memoryCache.Remove("TrenchQuery");

            _memoryCache.Remove("LocationQuery");
            _memoryCache.Remove("IconQuery");
            _memoryCache.Remove("TxtQuery");
            _memoryCache.Remove("TxtEnQuery");
            _memoryCache.Remove("LogoEnQuery");
            _memoryCache.Remove("FontQuery");
            return new ReturnInfo
            {
                isok = true,
                message = "清除成功",
                status = 200,
                response = "OK"
            };
        }

        /// <summary>
        /// 执行系列查询
        /// </summary>
        [HttpPost]
        public ReturnInfo SeriesQuery()
        {
            string key = "SeriesQuery";
            var R = _memoryCache.Get<ReturnInfo>(key);
            if (R != null)
            {
                return R;
            }
            else
            {
                var rs = ExecuteQuery("SELECT * FROM `cdr_library`.`tbl_cdr_file`");
                _memoryCache.Set(key, rs, TimeSpan.FromMinutes(2));
                return rs;
            }
        }

        public ReturnInfo MaterialQuery()
        {
            string key = "MaterialQuery";
            var R = _memoryCache.Get<ReturnInfo>(key);
            if (R != null)
            {
                return R;
            }
            else
            {
                var rs = ExecuteQuery("SELECT * FROM `cdr_library`.`tbl_cdr_file` WHERE PreviewPath IS NULL");
                _memoryCache.Set(key, rs, TimeSpan.FromMinutes(2));
                return rs;
            }

        }

        /// <summary>
        /// 获取所有包含图标的素材文件
        /// </summary>
        [HttpPost]
        public ReturnInfo IconLibraryQuery()
        {
            string key = "IconLibraryQuery";
            var R = _memoryCache.Get<ReturnInfo>(key);
            if (R != null)
            {
                return R;
            }
            else
            {
                var rs = ExecuteQuery($@"SELECT DISTINCT f.* FROM `cdr_library`.`tbl_cdr_file` f INNER JOIN `cdr_library`.`tbl_iconlibrary` i ON f.ID = i.FID WHERE i.IconType = 1 AND f.PreviewPath IS NULL");
                _memoryCache.Set(key, rs, TimeSpan.FromMinutes(2));
                return rs;
            }


        }
        /// <summary>
        /// 执行model查询
        /// </summary>
        [HttpPost]
        public ReturnInfo ModelQuery([FromBody] QueryInfo Info)
        {
            return ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_model` WHERE FID = {Info.FID}");
            string key = "ModelQuery";
            var R = _memoryCache.Get<ReturnInfo>(key);
            if (R != null)
            {
                return R;
            }
            else
            {
                var rs = ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_model` WHERE FID = {Info.FID}");
                _memoryCache.Set(key, rs, TimeSpan.FromMinutes(2));
                return rs;
            }
        }
        [HttpPost]
        public ReturnInfo IdModelQuery([FromBody] QueryInfo Info)
        {
            return ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_model` WHERE ID = {Info.PID}");
        }

        /// <summary>
        /// 执行Pattern查询
        /// </summary>
        [HttpPost]
        public ReturnInfo PatternQuery([FromBody] QueryInfo Info)
        {
            return ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_pattern` WHERE FID = {Info.FID}");
            string key = "PatternQuery";
            var R = _memoryCache.Get<ReturnInfo>(key);
            if (R != null)
            {
                return R;
            }
            else
            {
                var rs = ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_pattern` WHERE FID = {Info.FID}");
                _memoryCache.Set(key, rs, TimeSpan.FromMinutes(2));
                return rs;
            }
        }
        [HttpPost]
        public ReturnInfo IdPatternQuery([FromBody] QueryInfo Info)
        {
            return ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_pattern` WHERE ID = {Info.FID}");

        }

        /// <summary>
        /// 执行Trench查询
        /// </summary>
        [HttpPost]
        public ReturnInfo TrenchQuery([FromBody] QueryInfo Info)
        {
            return ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_trench` WHERE MID = {Info.MID}");
            string key = "TrenchQuery";
            var R = _memoryCache.Get<ReturnInfo>(key);
            if (R != null)
            {
                return R;
            }
            else
            {
                var rs = ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_trench` WHERE MID = {Info.MID}");
                _memoryCache.Set(key, rs, TimeSpan.FromMinutes(2));
                return rs;
            }
        }

        /// <summary>
        /// 执行Location查询
        /// </summary>
        [HttpPost]
        public ReturnInfo LocationQuery([FromBody] QueryInfo Info)
        {
            return ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_location` WHERE PID = {Info.PID}");
            string key = "LocationQuery";
            var R = _memoryCache.Get<ReturnInfo>(key);
            if (R != null)
            {
                return R;
            }
            else
            {
                var rs = ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_location` WHERE PID = {Info.PID}");
                _memoryCache.Set(key, rs, TimeSpan.FromMinutes(2));
                return rs;
            }
        }

        /// <summary>
        /// 执行Icon查询
        /// </summary>
        [HttpPost]
        public ReturnInfo IconQuery([FromBody] QueryInfo Info)
        {
            return ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_iconlibrary` WHERE IconType = 1 AND FID = {Info.FID}");
            string key = "IconQuery";
            var R = _memoryCache.Get<ReturnInfo>(key);
            if (R != null)
            {
                return R;
            }
            else
            {
                var rs = ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_iconlibrary` WHERE IconType = 1");
                _memoryCache.Set(key, rs, TimeSpan.FromMinutes(2));
                return rs;
            }
        }

        /// <summary>
        /// 执行txt查询
        /// </summary>
        [HttpPost]
        public ReturnInfo TxtQuery()
        {
            string key = "TxtQuery";
            var R = _memoryCache.Get<ReturnInfo>(key);
            if (R != null)
            {
                return R;
            }
            else
            {
                var rs = ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_iconlibrary` WHERE IconType = 2");
                _memoryCache.Set(key, rs, TimeSpan.FromMinutes(2));
                return rs;
            }
        }

        /// <summary>
        /// 执行txten查询
        /// </summary>
        [HttpPost]
        public ReturnInfo TxtEnQuery()
        {
            string key = "TxtEnQuery";
            var R = _memoryCache.Get<ReturnInfo>(key);
            if (R != null)
            {
                return R;
            }
            else
            {
                var rs = ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_iconlibrary` WHERE IconType = 3");
                _memoryCache.Set(key, rs, TimeSpan.FromMinutes(2));
                return rs;
            }
        }

        /// <summary>
        /// 执行logo查询
        /// </summary>
        [HttpPost]
        public ReturnInfo LogoEnQuery()
        {
            string key = "LogoEnQuery";
            var R = _memoryCache.Get<ReturnInfo>(key);
            if (R != null)
            {
                return R;
            }
            else
            {
                var rs = ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_iconlibrary` WHERE IconType = 4");
                _memoryCache.Set(key, rs, TimeSpan.FromMinutes(2));
                return rs;
            }
        }

        /// <summary>
        /// 执行font查询
        /// </summary>
        [HttpPost]
        public ReturnInfo FontQuery()
        {
            string key = "FontQuery";
            var R = _memoryCache.Get<ReturnInfo>(key);
            if (R != null)
            {
                return R;
            }
            else
            {
                var rs = ExecuteQuery($"SELECT * FROM `cdr_library`.`tbl_font`");
                _memoryCache.Set(key, rs, TimeSpan.FromMinutes(2));
                return rs;
            }
        }


        #endregion

        #region tbl_web_log 增删改查
        [HttpPost]
        public ReturnInfo WebLogQuery() => ExecuteQuery("SELECT * FROM `cdr_library`.`tbl_web_log` WHERE is_valid = 1");

        [HttpPost]
        public ReturnInfo WebLogAdd([FromBody] TblWebLog log)
        {
            string sql = @"
                INSERT INTO `cdr_library`.`tbl_web_log` 
                (username, action_time, action_type, hotel_name, target_type, method, target_id, user_ip, user_location, user_agent, is_valid, operation) 
                VALUES (@username, @action_time, @action_type, @hotel_name, @target_type, @method, @target_id, @user_ip, @user_location, @user_agent, 1, @operation)";
                
            var parameters = new[]
            {
                new MySqlParameter("@username", log.username ?? (object)DBNull.Value),
                new MySqlParameter("@action_time", log.action_time ?? DateTime.Now),
                new MySqlParameter("@action_type", log.action_type ?? (object)DBNull.Value),
                new MySqlParameter("@hotel_name", log.hotel_name ?? (object)DBNull.Value),
                new MySqlParameter("@target_type", log.target_type ?? (object)DBNull.Value),
                new MySqlParameter("@method", log.method ?? (object)DBNull.Value),
                new MySqlParameter("@target_id", log.target_id ?? (object)DBNull.Value),
                new MySqlParameter("@user_ip", log.user_ip ?? (object)DBNull.Value),
                new MySqlParameter("@user_location", log.user_location ?? (object)DBNull.Value),
                new MySqlParameter("@user_agent", log.user_agent ?? (object)DBNull.Value),
                new MySqlParameter("@operation", log.operation ?? (object)DBNull.Value)
            };

            return ExecuteNonQueryWithGuid(sql, parameters);
        }

        [HttpPost]
        public ReturnInfo WebLogUpdate([FromBody] TblWebLog log)
        {
            string sql = @"
                UPDATE `cdr_library`.`tbl_web_log` SET 
                username=@username, action_time=@action_time, action_type=@action_type, 
                hotel_name=@hotel_name, target_type=@target_type, method=@method, 
                target_id=@target_id, user_ip=@user_ip, user_location=@user_location, user_agent=@user_agent, operation=@operation 
                    WHERE id=@id";

            var parameters = new[]
            {
                new MySqlParameter("@username", log.username ?? (object)DBNull.Value),
                new MySqlParameter("@action_time", log.action_time ?? DateTime.Now),
                new MySqlParameter("@action_type", log.action_type ?? (object)DBNull.Value),
                new MySqlParameter("@hotel_name", log.hotel_name ?? (object)DBNull.Value),
                new MySqlParameter("@target_type", log.target_type ?? (object)DBNull.Value),
                new MySqlParameter("@method", log.method ?? (object)DBNull.Value),
                new MySqlParameter("@target_id", log.target_id ?? (object)DBNull.Value),
                new MySqlParameter("@user_ip", log.user_ip ?? (object)DBNull.Value),
                new MySqlParameter("@user_location", log.user_location ?? (object)DBNull.Value),
                new MySqlParameter("@user_agent", log.user_agent ?? (object)DBNull.Value),
                new MySqlParameter("@operation", log.operation ?? (object)DBNull.Value)
            };

            return ExecuteNonQueryWithGuid(sql, parameters, log.id.ToString());
        }

        [HttpPost]
        public ReturnInfo WebLogDelete([FromBody] string id)
        {
            // tbl_web_log identifies rows by `id` (bigint), not `guid`.
            if (!long.TryParse(id, out long idVal))
            {
                return new ReturnInfo { isok = false, message = "Invalid id parameter", status = 400, response = null };
            }
            string sql = "UPDATE `cdr_library`.`tbl_web_log` SET is_valid=0 WHERE id=@id";
            var parameter = new MySqlParameter("@id", idVal);
            return ExecuteNonQueryWithGuid(sql, new[] { parameter }, id);
        }
        [HttpPost]
        public ReturnInfo WebPanelListQuery() => ExecuteQuery("SELECT * FROM `cdr_library`.`tbl_web_panel_list` WHERE is_valid = 1");

        [HttpPost]
        public ReturnInfo WebPanelListAdd([FromBody] TblWebPanelList panel)
        {
            string sql = @"
                INSERT INTO `cdr_library`.`tbl_web_panel_list` 
                (panel_list_name, gang_series, model_type, gang_material_id, panel_count, 
                 carving_quantity, position, carving_filename, design_status, panel_info_json, logo_json, cdr_filename, thumbnail_large,  
                 thumbnail_small, erp_part_number, remarks, created_at, is_valid, guid)  
                VALUES (@panel_list_name, @gang_series, @model_type, @gang_material_id, @panel_count, 
                       @carving_quantity, @position, @carving_filename, @design_status, @panel_info_json, @logo_json, @cdr_filename, @thumbnail_large, 
                       @thumbnail_small, @erp_part_number, @remarks, @created_at, 1, @guid)";

            var parameters = new[]
            {
                new MySqlParameter("@panel_list_name", panel.panel_list_name),
                new MySqlParameter("@gang_series", panel.gang_series ?? (object)DBNull.Value),
                new MySqlParameter("@model_type", panel.model_type ?? (object)DBNull.Value),
                new MySqlParameter("@gang_material_id", panel.gang_material_id ?? (object)DBNull.Value),
                new MySqlParameter("@panel_count", panel.panel_count ?? (object)DBNull.Value),
                new MySqlParameter("@carving_quantity", panel.carving_quantity ?? (object)DBNull.Value),
                new MySqlParameter("@position", panel.position ?? (object)DBNull.Value),
                new MySqlParameter("@carving_filename", panel.carving_filename ?? (object)DBNull.Value),
                new MySqlParameter("@design_status", panel.design_status ?? (object)DBNull.Value),
                new MySqlParameter("@panel_info_json", panel.panel_info_json ?? (object)DBNull.Value),
                new MySqlParameter("@logo_json", panel.logo_json ?? (object)DBNull.Value),
                new MySqlParameter("@cdr_filename", panel.cdr_filename ?? (object)DBNull.Value),
                new MySqlParameter("@thumbnail_large", panel.thumbnail_large ?? (object)DBNull.Value),
                new MySqlParameter("@thumbnail_small", panel.thumbnail_small ?? (object)DBNull.Value),
                new MySqlParameter("@erp_part_number", panel.erp_part_number ?? (object)DBNull.Value),
                new MySqlParameter("@remarks", panel.remarks ?? (object)DBNull.Value),
                new MySqlParameter("@created_at", panel.created_at ?? DateTime.Now),
                new MySqlParameter("@guid", Guid.NewGuid().ToString("N"))
            };

            return ExecuteNonQueryWithGuid(sql, parameters);
        }

        [HttpPost]
        public ReturnInfo WebPanelListUpdate([FromBody] TblWebPanelList panel)
        {
            string sql = @"
                UPDATE `cdr_library`.`tbl_web_panel_list` SET  
                panel_list_name=@panel_list_name, gang_series=@gang_series, 
                model_type=@model_type, gang_material_id=@gang_material_id,  
                panel_count=@panel_count, carving_quantity=@carving_quantity, position=@position,
                carving_filename=@carving_filename, design_status=@design_status, panel_info_json=@panel_info_json, logo_json=@logo_json, cdr_filename=@cdr_filename,  
                thumbnail_large=@thumbnail_large, thumbnail_small=@thumbnail_small,  
                erp_part_number=@erp_part_number, remarks=@remarks  
                WHERE guid=@guid;";

            var parameters = new[]
            {
                new MySqlParameter("@panel_list_name", panel.panel_list_name),
                new MySqlParameter("@gang_series", panel.gang_series ?? (object)DBNull.Value),
                new MySqlParameter("@model_type", panel.model_type ?? (object)DBNull.Value),
                new MySqlParameter("@gang_material_id", panel.gang_material_id ?? (object)DBNull.Value),
                new MySqlParameter("@panel_count", panel.panel_count ?? (object)DBNull.Value),
                new MySqlParameter("@carving_quantity", panel.carving_quantity ?? (object)DBNull.Value),
                new MySqlParameter("@position", panel.position ?? (object)DBNull.Value),
                new MySqlParameter("@carving_filename", panel.carving_filename ?? (object)DBNull.Value),
                new MySqlParameter("@design_status", panel.design_status ?? (object)DBNull.Value),
                new MySqlParameter("@panel_info_json", panel.panel_info_json ?? (object)DBNull.Value),
                new MySqlParameter("@logo_json", panel.logo_json ?? (object)DBNull.Value),
                new MySqlParameter("@cdr_filename", panel.cdr_filename ?? (object)DBNull.Value),
                new MySqlParameter("@thumbnail_large", panel.thumbnail_large ?? (object)DBNull.Value),
                new MySqlParameter("@thumbnail_small", panel.thumbnail_small ?? (object)DBNull.Value),
                new MySqlParameter("@erp_part_number", panel.erp_part_number ?? (object)DBNull.Value),
                new MySqlParameter("@remarks", panel.remarks ?? (object)DBNull.Value),
                new MySqlParameter("@guid", panel.guid ?? (object)DBNull.Value)
            };

            return ExecuteNonQueryWithGuid(sql, parameters, panel.guid);
        }

        [HttpPost]
        public ReturnInfo WebPanelListDelete([FromBody] string guid)
        {
            string sql = "UPDATE `cdr_library`.`tbl_web_panel_list` SET is_valid=0 WHERE guid=@guid";
            var parameter = new MySqlParameter("@guid", guid ?? (object)DBNull.Value);
            return ExecuteNonQueryWithGuid(sql, new[] { parameter }, guid);
        }

        [HttpPost]
        public ReturnInfo WebPanelListQueryByGuid([FromBody] QueryInfo Info)
        {
            string sql = "SELECT * FROM `cdr_library`.`tbl_web_panel_list` WHERE guid = @guid AND is_valid = 1";
            var parameter = new MySqlParameter("@guid", Info.GUID ?? (object)DBNull.Value);
            return ExecuteQuery(sql, new[] { parameter });
        }
        #endregion

        #region tbl_web_panel_unit 增删改查
        [HttpPost]
        public ReturnInfo WebPanelUnitQuery() => ExecuteQuery("SELECT * FROM `cdr_library`.`tbl_web_panel_unit` WHERE is_valid = 1");

        [HttpPost]
        public ReturnInfo WebPanelUnitAdd([FromBody] TblWebPanelUnit unit)
        {
            string sql = @"
                INSERT INTO `cdr_library`.`tbl_web_panel_unit` 
                (panel_name, product_type, element_count, elements_json, cdr_filename, 
                 thumbnail_large, thumbnail_small, remarks, erp_part_number, design_status, 
                 owner, created_at, published_at, share_type, is_valid, guid, project_guid,
                 series_id, pattern_id, direction, color)
                VALUES (@panel_name, @product_type, @element_count, @elements_json, @cdr_filename, 
                       @thumbnail_large, @thumbnail_small, @remarks, @erp_part_number, @design_status, 
                       @owner, @created_at, @published_at, @share_type, 1, @guid, @project_guid,
                       @series_id, @pattern_id, @direction, @color)";

            var parameters = new[]
            {
                new MySqlParameter("@panel_name", unit.panel_name),
                new MySqlParameter("@product_type", unit.product_type ?? (object)DBNull.Value),
                new MySqlParameter("@element_count", unit.element_count ?? (object)DBNull.Value),
                new MySqlParameter("@elements_json", unit.elements_json ?? (object)DBNull.Value),
                new MySqlParameter("@cdr_filename", unit.cdr_filename ?? (object)DBNull.Value),
                new MySqlParameter("@thumbnail_large", unit.thumbnail_large ?? (object)DBNull.Value),
                new MySqlParameter("@thumbnail_small", unit.thumbnail_small ?? (object)DBNull.Value),
                new MySqlParameter("@remarks", unit.remarks ?? (object)DBNull.Value),
                new MySqlParameter("@erp_part_number", unit.erp_part_number ?? (object)DBNull.Value),
                new MySqlParameter("@design_status", unit.design_status ?? (object)DBNull.Value),
                new MySqlParameter("@owner", unit.owner),
                new MySqlParameter("@created_at", unit.created_at),
                new MySqlParameter("@published_at", unit.published_at),
                new MySqlParameter("@share_type", unit.share_type ?? (object)DBNull.Value),
                new MySqlParameter("@guid", Guid.NewGuid().ToString("N")),
                new MySqlParameter("@project_guid", unit.project_guid ?? (object)DBNull.Value),
                new MySqlParameter("@series_id", unit.series_id ?? (object)DBNull.Value),
                new MySqlParameter("@pattern_id", unit.pattern_id ?? (object)DBNull.Value),
                new MySqlParameter("@direction", unit.direction ?? (object)DBNull.Value),
                new MySqlParameter("@color", unit.color ?? (object)DBNull.Value)
            };

            return ExecuteNonQueryWithGuid(sql, parameters);
        }

        [HttpPost]
        public async Task<ReturnInfo> WebPanelUnitUpdate([FromBody] TblWebPanelUnit unit)
        {
            string sql = @"
                UPDATE `cdr_library`.`tbl_web_panel_unit` SET 
                panel_name=@panel_name, product_type=@product_type, element_count=@element_count, 
                elements_json=@elements_json, cdr_filename=@cdr_filename, thumbnail_large=@thumbnail_large, 
                thumbnail_small=@thumbnail_small, remarks=@remarks, erp_part_number=@erp_part_number, 
                design_status=@design_status, owner=@owner, published_at=@published_at, 
                share_type=@share_type, project_guid=@project_guid, series_id=@series_id, 
                pattern_id=@pattern_id, direction=@direction, color=@color
                WHERE guid=@guid";

            var parameters = new[]
            {
                new MySqlParameter("@panel_name", unit.panel_name),
                new MySqlParameter("@product_type", unit.product_type ?? (object)DBNull.Value),
                new MySqlParameter("@element_count", unit.element_count ?? (object)DBNull.Value),
                new MySqlParameter("@elements_json", unit.elements_json ?? (object)DBNull.Value),
                new MySqlParameter("@cdr_filename", unit.cdr_filename ?? (object)DBNull.Value),
                new MySqlParameter("@thumbnail_large", unit.thumbnail_large ?? (object)DBNull.Value),
                new MySqlParameter("@thumbnail_small", unit.thumbnail_small ?? (object)DBNull.Value),
                new MySqlParameter("@remarks", unit.remarks ?? (object)DBNull.Value),
                new MySqlParameter("@erp_part_number", unit.erp_part_number ?? (object)DBNull.Value),
                new MySqlParameter("@design_status", unit.design_status ?? (object)DBNull.Value),
                new MySqlParameter("@owner", unit.owner),
                new MySqlParameter("@published_at", unit.published_at),
                new MySqlParameter("@share_type", unit.share_type ?? (object)DBNull.Value),
                new MySqlParameter("@project_guid", unit.project_guid ?? (object)DBNull.Value),
                new MySqlParameter("@series_id", unit.series_id ?? (object)DBNull.Value),
                new MySqlParameter("@pattern_id", unit.pattern_id ?? (object)DBNull.Value),
                new MySqlParameter("@direction", unit.direction ?? (object)DBNull.Value),
                new MySqlParameter("@color", unit.color ?? (object)DBNull.Value),
                new MySqlParameter("@guid", unit.guid ?? (object)DBNull.Value)
            };

            // 执行原始更新操作
            var result = ExecuteNonQueryWithGuid(sql, parameters, unit.guid);

            // 如果原始操作成功，则执行额外的数据库操作
            if (result.isok && unit.design_status != "生成中")
            {
                try
                {
                    // 查询所有gang_material_id字段值包含当前unit guid的记录
                    string querySql = "SELECT * FROM `cdr_library`.`tbl_web_panel_list` WHERE panel_info_json LIKE @guid_pattern AND is_valid = 1";
                    var queryParameters = new[]
                    {
                        new MySqlParameter("@guid_pattern", $"%{unit.guid}%")
                    };

                    var queryResult = ExecuteQuery(querySql, queryParameters);

                    // 如果查询到记录，则更新这些记录的cdr_filename、thumbnail_large和remarks字段为空字符串
                    if (queryResult.isok && queryResult.response != null)
                    {
                        string updateSql = @"
                            UPDATE `cdr_library`.`tbl_web_panel_list` SET 
                            cdr_filename = '', thumbnail_large = '', remarks = '' 
                            WHERE panel_info_json LIKE @guid_pattern AND is_valid = 1";

                        var updateParameters = new[]
                        {
                            new MySqlParameter("@guid_pattern", $"%{unit.guid}%")
                        };
                        SetProjectUpdateCdr(new QueryInfo { GUID = unit.project_guid });
                        ExecuteNonQueryWithGuid(updateSql, updateParameters);
                    }
                }
                catch (Exception ex)
                {
                    // 记录错误但不影响原始操作的返回结果
                    System.Diagnostics.Debug.WriteLine($"执行额外数据库操作时出错: {ex.Message}");
                }
            }

            return result;
        }

        [HttpPost]
        public ReturnInfo WebPanelUnitDelete([FromBody] string guid)
        {
            string sql = "UPDATE `cdr_library`.`tbl_web_panel_unit` SET is_valid=0 WHERE guid=@guid";
            var parameter = new MySqlParameter("@guid", guid ?? (object)DBNull.Value);
            return ExecuteNonQueryWithGuid(sql, new[] { parameter }, guid);
        }

        [HttpPost]
        public ReturnInfo WebPanelUnitQueryByProject([FromBody] QueryInfo info)
        {
            string sql = "SELECT * FROM `cdr_library`.`tbl_web_panel_unit` WHERE is_valid = 1 AND project_guid = @project_guid";
            var parameter = new MySqlParameter("@project_guid", info.GUID ?? (object)DBNull.Value);
            return ExecuteQuery(sql, new[] { parameter });
        }

        [HttpPost]
        public ReturnInfo WebPanelUnitQueryByGuid([FromBody] QueryInfo Info)
        {
            string sql = "SELECT * FROM `cdr_library`.`tbl_web_panel_unit` WHERE guid = @guid AND is_valid = 1";
            var parameter = new MySqlParameter("@guid", Info.GUID ?? (object)DBNull.Value);
            return ExecuteQuery(sql, new[] { parameter });
        }

        /// <summary>
        /// 根据指定的tbl_web_panel_unit表中的GUID值，查询并获取对应行的完整数据记录，
        /// 然后基于该记录创建一条新记录，并修改特定字段
        /// </summary>
        /// <param name="request">包含原始GUID、新owner和project_guid的请求对象</param>
        /// <returns>返回操作结果和新记录的GUID</returns>
        [HttpPost]
        public ReturnInfo CopyPanelUnitWithNewFields([FromBody] CopyPanelUnitRequest request)
        {
            try
            {
                // 验证输入参数
                if (string.IsNullOrEmpty(request.OriginalGuid))
                {
                    return new ReturnInfo
                    {
                        isok = false,
                        message = "原始GUID不能为空",
                        status = 400,
                        response = null
                    };
                }

                if (string.IsNullOrEmpty(request.NewOwner))
                {
                    return new ReturnInfo
                    {
                        isok = false,
                        message = "新所有者不能为空",
                        status = 400,
                        response = null
                    };
                }

                // 1. 根据原始GUID查询记录
                string querySql = "SELECT * FROM `cdr_library`.`tbl_web_panel_unit` WHERE guid = @originalGuid AND is_valid = 1";
                var queryParameters = new[]
                {
                    new MySqlParameter("@originalGuid", request.OriginalGuid)
                };

                var queryResult = ExecuteQuery(querySql, queryParameters);

                if (!queryResult.isok || queryResult.response == null)
                {
                    return new ReturnInfo
                    {
                        isok = false,
                        message = "查询原始记录失败",
                        status = 500,
                        response = null
                    };
                }

                // 解析查询结果
                var dataTable = JsonConvert.DeserializeObject<DataTable>(queryResult.response.ToString());
                if (dataTable == null || dataTable.Rows.Count == 0)
                {
                    return new ReturnInfo
                    {
                        isok = false,
                        message = "未找到有效的原始记录",
                        status = 404,
                        response = null
                    };
                }

                var originalRow = dataTable.Rows[0];
                var newGuid = Guid.NewGuid().ToString("N");
                var currentTime = DateTime.Now;

                // 2. 创建新记录，修改指定字段
                string insertSql = @"
                    INSERT INTO `cdr_library`.`tbl_web_panel_unit` 
                    (panel_name, product_type, element_count, elements_json, cdr_filename, 
                     thumbnail_large, thumbnail_small, remarks, erp_part_number, design_status, 
                     owner, created_at, published_at, share_type, is_valid, guid, project_guid,
                     series_id, pattern_id, direction, color)
                    VALUES (@panel_name, @product_type, @element_count, @elements_json, @cdr_filename, 
                           @thumbnail_large, @thumbnail_small, @remarks, @erp_part_number, @design_status, 
                           @owner, @created_at, @published_at, @share_type, 1, @guid, @project_guid,
                           @series_id, @pattern_id, @direction, @color)";

                var insertParameters = new[]
                {
                    // 修改的字段
                    new MySqlParameter("@owner", request.NewOwner),
                    new MySqlParameter("@created_at", currentTime),
                    new MySqlParameter("@published_at", currentTime),
                    new MySqlParameter("@share_type", originalRow["share_type"] ?? (object)DBNull.Value),
                    new MySqlParameter("@guid", newGuid),
                    new MySqlParameter("@project_guid", request.NewProjectGuid ?? (object)DBNull.Value),
                    // 其他字段保持原值
                    new MySqlParameter("@panel_name", originalRow["panel_name"]),
                    new MySqlParameter("@product_type", originalRow["product_type"] ?? (object)DBNull.Value),
                    new MySqlParameter("@element_count", originalRow["element_count"] ?? (object)DBNull.Value),
                    new MySqlParameter("@elements_json", originalRow["elements_json"] ?? (object)DBNull.Value),
                    new MySqlParameter("@cdr_filename", originalRow["cdr_filename"] ?? (object)DBNull.Value),
                    new MySqlParameter("@thumbnail_large", originalRow["thumbnail_large"] ?? (object)DBNull.Value),
                    new MySqlParameter("@thumbnail_small", originalRow["thumbnail_small"] ?? (object)DBNull.Value),
                    new MySqlParameter("@remarks", originalRow["remarks"] ?? (object)DBNull.Value),
                    new MySqlParameter("@erp_part_number", originalRow["erp_part_number"] ?? (object)DBNull.Value),
                    new MySqlParameter("@design_status", originalRow["design_status"] ?? (object)DBNull.Value),
                    new MySqlParameter("@series_id", originalRow["series_id"] ?? (object)DBNull.Value),
                    new MySqlParameter("@pattern_id", originalRow["pattern_id"] ?? (object)DBNull.Value),
                    new MySqlParameter("@direction", originalRow["direction"] ?? (object)DBNull.Value),
                    new MySqlParameter("@color", originalRow["color"] ?? (object)DBNull.Value)
                };

                return ExecuteNonQueryWithGuid(insertSql, insertParameters, newGuid);
            }
            catch (Exception ex)
            {
                return new ReturnInfo
                {
                    isok = false,
                    message = $"复制面板单元失败: {ex.Message}",
                    status = 500,
                    response = null
                };
            }
        }

        /// <summary>
        /// 复制面板组：根据原始面板组 GUID 查询记录，创建一条新面板组记录，并将新面板组挂载到目标房型
        /// 关联方式：tbl_web_room_type.panel_group_count（JSON数组字符串）
        /// </summary>
        [HttpPost]
        public ReturnInfo CopyPanelListWithNewFields([FromBody] CopyPanelListRequest request)
        {
            try
            {
                if (request == null)
                {
                    return new ReturnInfo { isok = false, message = "请求参数不能为空", status = 400, response = null };
                }

                if (string.IsNullOrWhiteSpace(request.OriginalGuid))
                {
                    return new ReturnInfo { isok = false, message = "原始GUID不能为空", status = 400, response = null };
                }

                if (string.IsNullOrWhiteSpace(request.TargetRoomGuid))
                {
                    return new ReturnInfo { isok = false, message = "目标房型GUID不能为空", status = 400, response = null };
                }

                // 使用事务保证：插入面板组 + 更新目标房型的 panel_group_count 原子性
                using var conn = new MySqlConnection(ReadConfig.Instance.MySQLConnectionString);
                conn.Open();
                using var tx = conn.BeginTransaction();

                // 1) 查询原始面板组记录
                DataTable panelTable = new();
                using (var queryPanelCmd = new MySqlCommand(
                    "SELECT * FROM `cdr_library`.`tbl_web_panel_list` WHERE guid = @guid AND is_valid = 1",
                    conn,
                    tx))
                {
                    queryPanelCmd.Parameters.AddWithValue("@guid", request.OriginalGuid);
                    using var adapter = new MySqlDataAdapter(queryPanelCmd);
                    adapter.Fill(panelTable);
                }

                if (panelTable.Rows.Count == 0)
                {
                    tx.Rollback();
                    return new ReturnInfo { isok = false, message = "未找到有效的原始面板组", status = 404, response = null };
                }

                // 2) 锁定读取目标房型的 panel_group_count
                string roomPanelGroupCountRaw = null;
                using (var queryRoomCmd = new MySqlCommand(
                    "SELECT panel_group_count FROM `cdr_library`.`tbl_web_room_type` WHERE guid = @guid AND is_valid = 1 FOR UPDATE",
                    conn,
                    tx))
                {
                    queryRoomCmd.Parameters.AddWithValue("@guid", request.TargetRoomGuid);
                    var scalar = queryRoomCmd.ExecuteScalar();
                    if (scalar == null || scalar == DBNull.Value)
                    {
                        roomPanelGroupCountRaw = null;
                    }
                    else
                    {
                        roomPanelGroupCountRaw = scalar.ToString();
                    }
                }

                // 如果目标房型不存在
                // 注意：ExecuteScalar 在无行时返回 null
                using (var existsCmd = new MySqlCommand(
                    "SELECT COUNT(1) FROM `cdr_library`.`tbl_web_room_type` WHERE guid = @guid AND is_valid = 1",
                    conn,
                    tx))
                {
                    existsCmd.Parameters.AddWithValue("@guid", request.TargetRoomGuid);
                    var cnt = Convert.ToInt32(existsCmd.ExecuteScalar() ?? 0);
                    if (cnt <= 0)
                    {
                        tx.Rollback();
                        return new ReturnInfo { isok = false, message = "未找到有效的目标房型", status = 404, response = null };
                    }
                }

                var originalRow = panelTable.Rows[0];
                var newGuid = Guid.NewGuid().ToString("N");
                var currentTime = DateTime.Now;

                // 3) 插入新面板组记录
                using (var insertCmd = new MySqlCommand(@"
                    INSERT INTO `cdr_library`.`tbl_web_panel_list`
                    (panel_list_name, gang_series, model_type, gang_material_id, panel_count,
                     carving_quantity, position, carving_filename, design_status, panel_info_json, logo_json, cdr_filename, thumbnail_large,
                     thumbnail_small, erp_part_number, remarks, created_at, is_valid, guid)
                    VALUES (@panel_list_name, @gang_series, @model_type, @gang_material_id, @panel_count,
                            @carving_quantity, @position, @carving_filename, @design_status, @panel_info_json, @logo_json, @cdr_filename, @thumbnail_large,
                            @thumbnail_small, @erp_part_number, @remarks, @created_at, 1, @guid)", conn, tx))
                {
                    insertCmd.Parameters.AddWithValue("@panel_list_name", originalRow["panel_list_name"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@gang_series", originalRow["gang_series"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@model_type", originalRow["model_type"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@gang_material_id", originalRow["gang_material_id"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@panel_count", originalRow["panel_count"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@carving_quantity", originalRow["carving_quantity"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@position", originalRow["position"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@carving_filename", originalRow["carving_filename"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@design_status", originalRow["design_status"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@panel_info_json", originalRow["panel_info_json"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@logo_json", originalRow["logo_json"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@cdr_filename", originalRow["cdr_filename"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@thumbnail_large", originalRow["thumbnail_large"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@thumbnail_small", originalRow["thumbnail_small"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@erp_part_number", originalRow["erp_part_number"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@remarks", originalRow["remarks"] ?? (object)DBNull.Value);
                    insertCmd.Parameters.AddWithValue("@created_at", currentTime);
                    insertCmd.Parameters.AddWithValue("@guid", newGuid);

                    var affected = insertCmd.ExecuteNonQuery();
                    if (affected <= 0)
                    {
                        tx.Rollback();
                        return new ReturnInfo { isok = false, message = "新增面板组记录失败（未影响任何数据）", status = 500, response = null };
                    }
                }

                // 4) 更新目标房型的 panel_group_count
                List<string> panelGroups;
                try
                {
                    panelGroups = JsonConvert.DeserializeObject<List<string>>(string.IsNullOrWhiteSpace(roomPanelGroupCountRaw) ? "[]" : roomPanelGroupCountRaw)
                                 ?? new List<string>();
                }
                catch
                {
                    panelGroups = new List<string>();
                }

                if (!panelGroups.Contains(newGuid)) panelGroups.Add(newGuid);
                var newPanelGroupCountJson = JsonConvert.SerializeObject(panelGroups);

                using (var updateRoomCmd = new MySqlCommand(
                    "UPDATE `cdr_library`.`tbl_web_room_type` SET panel_group_count=@panel_group_count WHERE guid=@guid",
                    conn,
                    tx))
                {
                    updateRoomCmd.Parameters.AddWithValue("@panel_group_count", newPanelGroupCountJson);
                    updateRoomCmd.Parameters.AddWithValue("@guid", request.TargetRoomGuid);
                    var affected = updateRoomCmd.ExecuteNonQuery();
                    if (affected <= 0)
                    {
                        tx.Rollback();
                        return new ReturnInfo { isok = false, message = "更新目标房型面板组列表失败（未影响任何数据）", status = 500, response = null };
                    }
                }

                tx.Commit();
                return new ReturnInfo { isok = true, message = "复制成功", status = 200, response = newGuid };
            }
            catch (Exception ex)
            {
                return new ReturnInfo
                {
                    isok = false,
                    message = $"复制面板组失败: {ex.Message}",
                    status = 500,
                    response = null
                };
            }
        }
        #endregion

        #region tbl_web_project 增删改查
        [HttpPost]
        public ReturnInfo WebProjectQuery() => ExecuteQuery("SELECT * FROM `cdr_library`.`tbl_web_project` WHERE is_valid = 1");

        [HttpPost]
        public ReturnInfo WebProjectAdd([FromBody] TblWebProject project)
        {
            string sql = @"
                INSERT INTO `cdr_library`.`tbl_web_project` 
                (report_id, hotel_name, room_type_count, room_data_json, remarks, creator, created_at, is_valid, guid, edit_status, cdr_filename,
                 protocol, project_name, switch_series, socket_series, cn_font, cn_font_size, en_font, en_font_size) 
                VALUES (@report_id, @hotel_name, @room_type_count, @room_data_json, @remarks, @creator, @created_at, 1, @guid, @edit_status, @cdr_filename,
                        @protocol, @project_name, @switch_series, @socket_series, @cn_font, @cn_font_size, @en_font, @en_font_size)";

            var parameters = new[]
            {
                new MySqlParameter("@report_id", project.report_id),
                new MySqlParameter("@hotel_name", project.hotel_name),
                new MySqlParameter("@room_type_count", project.room_type_count),
                new MySqlParameter("@room_data_json", project.room_data_json),
                new MySqlParameter("@remarks", project.remarks ?? (object)DBNull.Value),
                new MySqlParameter("@creator", project.creator),
                new MySqlParameter("@created_at", project.created_at),
                new MySqlParameter("@guid", Guid.NewGuid().ToString("N")),
                new MySqlParameter("@edit_status", project.edit_status ?? (object)DBNull.Value),
                new MySqlParameter("@cdr_filename", project.cdr_filename ?? (object)DBNull.Value),
                new MySqlParameter("@protocol", project.protocol ?? (object)DBNull.Value),
                new MySqlParameter("@project_name", project.project_name ?? (object)DBNull.Value),
                new MySqlParameter("@switch_series", project.switch_series ?? (object)DBNull.Value),
                new MySqlParameter("@socket_series", project.socket_series ?? (object)DBNull.Value),
                new MySqlParameter("@cn_font", project.cn_font ?? (object)DBNull.Value),
                new MySqlParameter("@cn_font_size", project.cn_font_size ?? (object)DBNull.Value),
                new MySqlParameter("@en_font", project.en_font ?? (object)DBNull.Value),
                new MySqlParameter("@en_font_size", project.en_font_size ?? (object)DBNull.Value)
            };

            return ExecuteNonQueryWithGuid(sql, parameters);
        }
        /// <summary>
        /// 清空项目的编辑状态和cdr文件名
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        [HttpPost]
        public ReturnInfo SetProjectUpdateCdr([FromBody] QueryInfo info)
        {
            string sql = @"
                UPDATE `cdr_library`.`tbl_web_project` SET 
                edit_status='', cdr_filename=''
                WHERE guid=@guid";

            var parameters = new[]
            {
                new MySqlParameter("@guid", info.GUID ?? (object)DBNull.Value)
            };

            return ExecuteNonQueryWithGuid(sql, parameters, info.GUID);
        }
        [HttpPost]
        public ReturnInfo WebProjectUpdate([FromBody] TblWebProject project)
        {
            string sql = @"
                UPDATE `cdr_library`.`tbl_web_project` SET 
                report_id=@report_id, hotel_name=@hotel_name, room_type_count=@room_type_count, 
                room_data_json=@room_data_json, remarks=@remarks, creator=@creator,
                edit_status=@edit_status, cdr_filename=@cdr_filename,
                protocol=@protocol, project_name=@project_name, switch_series=@switch_series, socket_series=@socket_series,
                cn_font=@cn_font, cn_font_size=@cn_font_size, en_font=@en_font, en_font_size=@en_font_size
                WHERE guid=@guid";

            var parameters = new[]
            {
                new MySqlParameter("@report_id", project.report_id),
                new MySqlParameter("@hotel_name", project.hotel_name),
                new MySqlParameter("@room_type_count", project.room_type_count),
                new MySqlParameter("@room_data_json", project.room_data_json),
                new MySqlParameter("@remarks", project.remarks ?? (object)DBNull.Value),
                new MySqlParameter("@creator", project.creator),
                new MySqlParameter("@edit_status", project.edit_status ?? (object)DBNull.Value),
                new MySqlParameter("@cdr_filename", project.cdr_filename ?? (object)DBNull.Value),
                new MySqlParameter("@protocol", project.protocol ?? (object)DBNull.Value),
                new MySqlParameter("@project_name", project.project_name ?? (object)DBNull.Value),
                new MySqlParameter("@switch_series", project.switch_series ?? (object)DBNull.Value),
                new MySqlParameter("@socket_series", project.socket_series ?? (object)DBNull.Value),
                new MySqlParameter("@cn_font", project.cn_font ?? (object)DBNull.Value),
                new MySqlParameter("@cn_font_size", project.cn_font_size ?? (object)DBNull.Value),
                new MySqlParameter("@en_font", project.en_font ?? (object)DBNull.Value),
                new MySqlParameter("@en_font_size", project.en_font_size ?? (object)DBNull.Value),
                new MySqlParameter("@guid", project.guid ?? (object)DBNull.Value)
            };

            return ExecuteNonQueryWithGuid(sql, parameters, project.guid);
        }

        [HttpPost]
        public ReturnInfo WebProjectDelete([FromBody] string guid)
        {
            string sql = "UPDATE `cdr_library`.`tbl_web_project` SET is_valid=0 WHERE guid=@guid";
            var parameter = new MySqlParameter("@guid", guid ?? (object)DBNull.Value);
            return ExecuteNonQueryWithGuid(sql, new[] { parameter }, guid);
        }
        [HttpPost]
        public ReturnInfo GetWebProject([FromBody] QueryInfo info)
        {
            string sql = "SELECT * FROM `cdr_library`.`tbl_web_project` WHERE guid=@Guid;";
            var parameter = new MySqlParameter("@guid", info.GUID ?? (object)DBNull.Value);
            return ExecuteQuery(sql, new[] { parameter });
        }

        /// <summary>
        /// 发送CDR文件的路径表单到Redis生成队列
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        [HttpPost]
        public ReturnInfo GetAllProjectCDR([FromBody] GetAllProjectCdrRequest request)
        {
            ApplyProjectCdrDefaults(request);
            EnsureEnumValueExists("protocol", request.Protocol, DefaultProtocolOptions);
            EnsureEnumValueExists("switch_series", request.SwitchSeries, DefaultSwitchSeriesOptions);

            string sql = @"SELECT DISTINCT p.hotel_name, rt.room_name, pnl.model_type, pnl.gang_series, 
                    pnl.panel_list_name,pnl.cdr_filename,pnl.position,pnl.carving_quantity,pnl.thumbnail_large,pnl.carving_filename 
                    FROM `cdr_library`.`tbl_web_project` p
                    JOIN `cdr_library`.`tbl_web_room_type` rt ON p.room_data_json LIKE CONCAT('%', rt.guid, '%')
                    JOIN `cdr_library`.`tbl_web_panel_list` pnl ON rt.panel_group_count LIKE CONCAT('%', pnl.guid, '%')
                WHERE p.guid=@guid ORDER BY rt.room_name;";
            var parameter = new MySqlParameter("@guid", request.GUID ?? (object)DBNull.Value);
            // 将当前项目状态置为生成中，避免重复触发或并发问题
            string updateSql = @"UPDATE `cdr_library`.`tbl_web_project` SET 
                    edit_status='生成中',
                    protocol=@protocol,
                    project_name=@project_name,
                    switch_series=@switch_series,
                    socket_series=@socket_series,
                    cn_font=@cn_font,
                    cn_font_size=@cn_font_size,
                    en_font=@en_font,
                    en_font_size=@en_font_size
                WHERE guid=@guid";

            var updateParameters = new[]
            {
                parameter,
                new MySqlParameter("@protocol", request.Protocol ?? (object)DBNull.Value),
                new MySqlParameter("@project_name", request.ProjectName ?? (object)DBNull.Value),
                new MySqlParameter("@switch_series", request.SwitchSeries ?? (object)DBNull.Value),
                new MySqlParameter("@socket_series", request.SocketSeries ?? (object)DBNull.Value),
                new MySqlParameter("@cn_font", request.ChineseFont ?? (object)DBNull.Value),
                new MySqlParameter("@cn_font_size", request.ChineseFontSize ?? (object)DBNull.Value),
                new MySqlParameter("@en_font", request.EnglishFont ?? (object)DBNull.Value),
                new MySqlParameter("@en_font_size", request.EnglishFontSize ?? (object)DBNull.Value)
            };
            ExecuteNonQueryWithGuid(updateSql, updateParameters, request.GUID);

            var eq = ExecuteQuery(sql, new[] { parameter });

            // 获取tbl_web_project的整行数据
            string projectSql = "SELECT * FROM `cdr_library`.`tbl_web_project` WHERE guid = @guid";
            var projectQueryResult = ExecuteQuery(projectSql, new[] { parameter });
            string hotel_name = JsonConvert.DeserializeObject<DataTable>(projectQueryResult.response.ToString()).Rows[0]["hotel_name"].ToString();
            // 解析两个查询结果（如果有）为JToken，以便合并发送到Redis
            JToken? panelsToken = null;
            JToken? projectToken = null;
            try
            {
                if (eq != null && eq.response != null) panelsToken = JToken.Parse(eq.response!.ToString());
            }
            catch { panelsToken = null; }
            try
            {
                if (projectQueryResult != null && projectQueryResult.response != null) projectToken = JToken.Parse(projectQueryResult.response!.ToString());
            }
            catch { projectToken = null; }

            // 在推送到Redis前，为项目数据补充固定尺寸说明字段
            string switchSizeText = "单体：108*108_二连体：194*108_三连体：280*108_四连体：365*108";
            string socketSizeText = request.SocketSeries == "玻璃"
                ? "单体：108*108_二连体：194*108_三连体：280*108_四连体：365*108"
                : "单体：86*86_二连体：172*86_三连体：258*86_四连体：344*86";

            // 确保 projectToken 不为 null，且能新增字段
            if (projectToken == null)
            {
                projectToken = new JObject();
            }

            try
            {
                if (projectToken is JObject projectObj)
                {
                    projectObj["SwitchSizeText"] = switchSizeText;
                    projectObj["SocketSizeText"] = socketSizeText;
                }
                else if (projectToken is JArray projectArr)
                {
                    if (projectArr.Count == 0)
                    {
                        projectToken = new JObject
                        {
                            ["SwitchSizeText"] = switchSizeText,
                            ["SocketSizeText"] = socketSizeText
                        };
                    }
                    else
                    {
                        foreach (var item in projectArr)
                        {
                            if (item is JObject row)
                            {
                                row["SwitchSizeText"] = switchSizeText;
                                row["SocketSizeText"] = socketSizeText;
                            }
                        }
                    }
                }
            }
            catch
            {
                // ignore
            }
            string picNum = Guid.NewGuid().ToString("N");
            var combinedPayload = new
            {
                project = projectToken,
                panels = panelsToken,
                RecordDate = DateTime.Now.ToString("yyyy-MM-dd"),
                picNum
            };

            bool success = PublishToRedis(new RedisInfo
            {
                Type = "CellCorelDRAW",
                ObjToRedis = new CellCorelDRAW() { MsgType = 2, MsgNode = combinedPayload }
            });
            Dictionary<string, string> pcn = new()
                {
                    { picNum, request.GUID ?? string.Empty }
                };

            CSRedisCacheHelper.LPush("PicNumList", pcn);
            LogOperation(request.UserName, "GetAllProjectCDR", hotel_name, request.GUID, request);
            return new() { isok = success, message = success ? "已提交到生成队列" : "提交到生成队列失败", status = success ? 200 : 500, response = null };

        }

        [HttpPost]
        public ReturnInfo GetProjectCdrEnums()
        {
            var protocols = GetOrInitEnumValues("protocol", DefaultProtocolOptions);
            var switchSeries = GetOrInitEnumValues("switch_series", DefaultSwitchSeriesOptions);
            var socketSeries = new List<string> { "玻璃", "塑料", "无" };

            return new ReturnInfo
            {
                isok = true,
                message = "",
                status = 200,
                response = JsonConvert.SerializeObject(new
                {
                    protocols,
                    switchSeries,
                    socketSeries
                })
            };
        }

        /// <summary>
        /// 获取下载CDR文件的路径表单
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        [HttpPost]
        public ReturnInfo GetWebProjectCDR([FromBody] QueryInfo info)
        {
            string sql = @"SELECT DISTINCT p.hotel_name, rt.room_name, pnl.model_type, pnl.gang_series, 
                    pnl.panel_list_name,pnl.cdr_filename,pnl.position,pnl.carving_quantity,pnl.thumbnail_large,pnl.carving_filename 
                    FROM `cdr_library`.`tbl_web_project` p
                    JOIN `cdr_library`.`tbl_web_room_type` rt ON p.room_data_json LIKE CONCAT('%', rt.guid, '%')
                    JOIN `cdr_library`.`tbl_web_panel_list` pnl ON rt.panel_group_count LIKE CONCAT('%', pnl.guid, '%')
                    WHERE p.guid=@guid;";
            var parameter = new MySqlParameter("@guid", info.GUID ?? (object)DBNull.Value);
            return ExecuteQuery(sql, new[] { parameter });
        }
        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> DownloadProjectCDR([FromBody] QueryInfo info)
        {
            try
            {
                string hotel_name = "";
                string sql = @"SELECT DISTINCT p.hotel_name, rt.room_name, pnl.model_type, pnl.gang_series, 
                    pnl.panel_list_name,pnl.cdr_filename,pnl.position,pnl.carving_quantity,pnl.thumbnail_large,pnl.carving_filename 
                    FROM `cdr_library`.`tbl_web_project` p
                    JOIN `cdr_library`.`tbl_web_room_type` rt ON p.room_data_json LIKE CONCAT('%', rt.guid, '%')
                    JOIN `cdr_library`.`tbl_web_panel_list` pnl ON rt.panel_group_count LIKE CONCAT('%', pnl.guid, '%')
                    WHERE p.guid=@guid;";
                var parameter = new MySqlParameter("@guid", info.GUID ?? (object)DBNull.Value);
                var queryResult = ExecuteQuery(sql, new[] { parameter });

                if (!queryResult.isok || queryResult.response == null)
                {
                    return NotFound("未找到相关数据");
                }

                var dt = JsonConvert.DeserializeObject<DataTable>(queryResult.response.ToString());
                if (dt == null || dt.Rows.Count == 0)
                {
                    return NotFound("未找到相关数据");
                }

                // 先获取项目级的cdr文件路径，确认存在，否则直接返回错误
                var projectResult = GetWebProject(info);
                if (!projectResult.isok || projectResult.response == null)
                {
                    return NotFound("未找到项目数据");
                }
                var projectDt = JsonConvert.DeserializeObject<DataTable>(projectResult.response.ToString());
                if (projectDt == null || projectDt.Rows.Count == 0)
                {
                    return NotFound("未找到项目数据");
                }
                string projectCdr = projectDt.Rows[0]["cdr_filename"]?.ToString()?.Trim() ?? string.Empty;
                if (string.IsNullOrWhiteSpace(projectCdr))
                {
                    return BadRequest("总路径不存在");
                }

                // 校验每条记录的必要文件是否已生成（thumbnail_large, carving_filename, cdr_filename）
                foreach (DataRow row in dt.Rows)
                {
                    string thumbnail = row["thumbnail_large"]?.ToString()?.Trim() ?? string.Empty;
                    string carving = row["carving_filename"]?.ToString()?.Trim() ?? string.Empty;
                    string cdr = row["cdr_filename"]?.ToString()?.Trim() ?? string.Empty;

                    if (string.IsNullOrEmpty(thumbnail) || string.IsNullOrEmpty(carving) || string.IsNullOrEmpty(cdr))
                    {
                        // 如果任一文件为空，则直接返回错误提示
                        return BadRequest("有图片尚未生成");
                    }
                }

                var memoryStream = new MemoryStream();
                using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
                {
                    var dateStr = DateTime.Now.ToString("yyyyMMdd");
                    var usedNames = new HashSet<string>();

                    foreach (DataRow row in dt.Rows)
                    {
                        hotel_name = row["hotel_name"]?.ToString() ?? "";
                        string carving_quantity = row["carving_quantity"]?.ToString() ?? "0";
                        string room_name = row["room_name"]?.ToString() ?? "";
                        string panel_list_name = row["panel_list_name"]?.ToString() ?? "";
                        string gang_series = row["gang_series"]?.ToString() ?? "";
                        string model_type = row["model_type"]?.ToString() ?? "";

                        string formatted_model_type = FormatNameFront12(model_type);

                        string baseFileName = $"数量{carving_quantity}_{hotel_name}_{room_name}_{panel_list_name}_{gang_series}_{formatted_model_type}_{dateStr}";

                        // CDR
                        string cdrPath = "\\PanelSelectionPic\\" + row["cdr_filename"]?.ToString();
                        if (!string.IsNullOrWhiteSpace(cdrPath))
                        {
                            AddFileToZip(archive, _webHostEnvironment.WebRootPath, cdrPath, $"CDR/{baseFileName}.cdr", usedNames);
                        }

                        // PNG
                        string pngPath = "\\PanelSelectionPic\\" + row["thumbnail_large"]?.ToString();
                        if (!string.IsNullOrWhiteSpace(pngPath))
                        {
                            AddFileToZip(archive, _webHostEnvironment.WebRootPath, pngPath, $"雕刻/{baseFileName}.png", usedNames);
                        }

                        // PLT
                        string pltPath = "\\PanelSelectionPic\\" + row["carving_filename"]?.ToString();
                        if (!string.IsNullOrWhiteSpace(pltPath))
                        {
                            AddFileToZip(archive, _webHostEnvironment.WebRootPath, pltPath, $"雕刻/{baseFileName}.plt", usedNames);
                        }
                    }

                    // 添加整体项目的 CDR 文件到 CDR/ 目录下，并生成同名 PDF 复制品加入 ZIP
                    if (!string.IsNullOrWhiteSpace(projectCdr))
                    {
                        var projectCdrRelative = "\\PanelSelectionPic\\" + projectCdr;
                        AddFileToZip(archive, _webHostEnvironment.WebRootPath, projectCdrRelative, $"CDR/{hotel_name}_总预览图.cdr", usedNames);

                        try
                        {
                            // 源文件物理路径
                            var webRoot = _webHostEnvironment.WebRootPath ?? "";
                            var sourcePhysical = Path.Combine(webRoot, projectCdrRelative.TrimStart('\\', '/'));

                            if (System.IO.File.Exists(sourcePhysical))
                            {
                                // 把路径最后3个字符替换为 pdf（大小写不敏感的替换）
                                string pdfFilename = projectCdr.Length >= 3
                                    ? projectCdr.Substring(0, projectCdr.Length - 3) + "pdf"
                                    : projectCdr + ".pdf";

                                var pdfRelative = "\\PanelSelectionPic\\" + pdfFilename;
                                var pdfPhysical = Path.Combine(webRoot, pdfRelative.TrimStart('\\', '/'));

                                // 确保目录存在，然后复制文件（覆盖同名文件）
                                var pdfDir = Path.GetDirectoryName(pdfPhysical);
                                if (!string.IsNullOrEmpty(pdfDir) && !Directory.Exists(pdfDir)) Directory.CreateDirectory(pdfDir);
                                System.IO.File.Copy(sourcePhysical, pdfPhysical, true);

                                // 将复制的 PDF 加入压缩包，文件名使用 .pdf 后缀
                                AddFileToZip(archive, webRoot, pdfRelative, $"CDR/{hotel_name}_总预览图.pdf", usedNames);
                            }
                        }
                        catch
                        {
                            // 复制失败不应阻塞主流程
                        }
                    }
                }

                // 记录日志
                LogOperation(info.UserName, "DownloadProjectCDR", hotel_name, info.GUID, info);

                memoryStream.Position = 0;
                return File(memoryStream, "application/zip", $"{hotel_name}_{DateTime.Now:yyyyMMddHHmmss}.zip");
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"打包下载失败: {ex.Message}");
            }
        }

        private string FormatNameFront12(string model_type)
        {
            if (string.IsNullOrEmpty(model_type)) return "";
            var parts = model_type.Split('_');
            if (parts.Length <= 3) return "";
            return string.Join("_", parts.Skip(1).Take(parts.Length - 3));
        }

        private void AddFileToZip(ZipArchive archive, string webRootPath, string filePath, string entryPath, HashSet<string> usedNames)
        {
            try
            {
                // Handle duplicates
                string finalEntryPath = entryPath;
                int counter = 1;
                while (usedNames.Contains(finalEntryPath))
                {
                    string ext = Path.GetExtension(entryPath);
                    string nameWithoutExt = Path.GetFileNameWithoutExtension(entryPath);
                    string dir = Path.GetDirectoryName(entryPath)?.Replace("\\", "/") ?? "";
                    if (!string.IsNullOrEmpty(dir)) dir += "/";
                    finalEntryPath = $"{dir}{nameWithoutExt}_{counter}{ext}";
                    counter++;
                }
                usedNames.Add(finalEntryPath);

                // Resolve physical path
                if (filePath.StartsWith("http", StringComparison.OrdinalIgnoreCase))
                {
                    if (Uri.TryCreate(filePath, UriKind.Absolute, out Uri uri))
                    {
                        filePath = uri.LocalPath;
                    }
                }

                filePath = filePath.TrimStart('/', '\\');
                string physicalPath = Path.Combine(webRootPath, filePath);

                if (System.IO.File.Exists(physicalPath))
                {
                    archive.CreateEntryFromFile(physicalPath, finalEntryPath);
                }
            }
            catch
            {
                // Ignore errors for individual files
            }
        }

        #endregion

        #region tbl_web_room_type 增删改查
        [HttpPost]
        public ReturnInfo WebRoomTypeQuery() => ExecuteQuery("SELECT * FROM `cdr_library`.`tbl_web_room_type` WHERE is_valid = 1");

        [HttpPost]
        public ReturnInfo WebRoomTypeAdd([FromBody] TblWebRoomType room)
        {
            string sql = @"
                INSERT INTO `cdr_library`.`tbl_web_room_type` 
                (room_name, room_description, panel_group_count, remarks, creator, created_at, is_valid, guid) 
                VALUES (@room_name, @room_description, @panel_group_count, @remarks, @creator, @created_at, 1, @guid)";

            // 兜底：panel_group_count 为空时用 []，避免前端 JSON.parse 报错
            var panelGroupCount = string.IsNullOrWhiteSpace(room.panel_group_count) ? "[]" : room.panel_group_count;

            var parameters = new[]
            {
                new MySqlParameter("@room_name", room.room_name),
                new MySqlParameter("@room_description", room.room_description ?? (object)DBNull.Value),
                new MySqlParameter("@panel_group_count", panelGroupCount),
                new MySqlParameter("@remarks", room.remarks ?? (object)DBNull.Value),
                new MySqlParameter("@creator", room.creator),
                new MySqlParameter("@created_at", room.created_at ?? DateTime.Now),
                new MySqlParameter("@guid", Guid.NewGuid().ToString("N"))
            };

            return ExecuteNonQueryWithGuid(sql, parameters);
        }

        [HttpPost]
        public ReturnInfo WebRoomTypeUpdate([FromBody] TblWebRoomType room)
        {
            string sql = @"
                UPDATE `cdr_library`.`tbl_web_room_type` SET 
                room_name=@room_name, room_description=@room_description, panel_group_count=@panel_group_count, 
                remarks=@remarks, creator=IFNULL(@creator, creator) 
                WHERE guid=@guid";

            var parameters = new[]
            {
                new MySqlParameter("@room_name", room.room_name),
                new MySqlParameter("@room_description", room.room_description ?? (object)DBNull.Value),
                new MySqlParameter("@panel_group_count", room.panel_group_count ?? (object)DBNull.Value),
                new MySqlParameter("@remarks", room.remarks ?? (object)DBNull.Value),
                new MySqlParameter("@creator", room.creator ?? (object)DBNull.Value),
                new MySqlParameter("@guid", room.guid ?? (object)DBNull.Value)
            };

            return ExecuteNonQueryWithGuid(sql, parameters, room.guid);
        }

        [HttpPost]
        public ReturnInfo WebRoomTypeDelete([FromBody] string guid)
        {
            string sql = "UPDATE `cdr_library`.`tbl_web_room_type` SET is_valid=0 WHERE guid=@guid";
            var parameter = new MySqlParameter("@guid", guid ?? (object)DBNull.Value);
            return ExecuteNonQueryWithGuid(sql, new[] { parameter }, guid);
        }
        #endregion

        #region 综合查询
        [HttpPost]
        public ReturnInfo QueryGeneratingItems()
        {
            try
            {
                var generatingItems = new
                {
                    PanelLists = ExecuteQuery("SELECT guid, thumbnail_large FROM `cdr_library`.`tbl_web_panel_list` WHERE design_status = '生成中' AND is_valid = 1"),
                    PanelUnits = ExecuteQuery("SELECT guid, thumbnail_large FROM `cdr_library`.`tbl_web_panel_unit` WHERE design_status = '生成中' AND is_valid = 1")
                };

                return new ReturnInfo
                {
                    isok = true,
                    message = "查询成功",
                    status = 200,
                    response = Newtonsoft.Json.JsonConvert.SerializeObject(generatingItems)
                };
            }
            catch (Exception ex)
            {
                return new ReturnInfo
                {
                    isok = false,
                    message = ex.Message,
                    status = 500,
                    response = null
                };
            }
        }
        #endregion

        #region 工具方法

        /// <summary>
        /// 记录操作日志
        /// </summary>
        private void LogOperation(string username, string actionType, string hotelName, string targetId, object operationData)
        {
            try
            {
                string userIp = HttpContext.Connection.RemoteIpAddress?.ToString();
                string userAgent = Request.Headers["User-Agent"].ToString();

                // Ensure required fields are not null to satisfy DB constraints
                var safeUsername = string.IsNullOrEmpty(username) ? (User?.Identity?.Name ?? "system") : username;
                var log = new TblWebLog
                {
                    username = safeUsername,
                    action_type = actionType,
                    hotel_name = hotelName,
                    target_id = targetId,
                    operation = JsonConvert.SerializeObject(operationData),
                    user_ip = userIp,
                    user_agent = userAgent,
                    user_location = "", // 默认空字符串
                    method = actionType, // 使用actionType作为method
                    target_type = "Project" // 默认为Project，可根据需要调整
                };

                WebLogAdd(log);
            }
            catch (Exception ex)
            {
                // 记录日志失败不应影响主流程，仅打印错误
                Console.WriteLine($"记录日志失败: {ex.Message}");
            }
        }

        /// <summary>
        /// Publish到Redis
        /// </summary>
        public bool PublishToRedis(RedisInfo request)
        {
            try
            {
                CSRedisCacheHelper.Publish(request.Type, Newtonsoft.Json.JsonConvert.SerializeObject(request.ObjToRedis));
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// 通用查询执行方法（参数化版本）
        /// </summary>
        private ReturnInfo ExecuteQuery(string sql, MySqlParameter[] parameters = null)
        {
            try
            {
                using var conn = new MySqlConnection(ReadConfig.Instance.MySQLConnectionString);
                conn.Open();

                using var cmd = new MySqlCommand(sql, conn);
                if (parameters != null)
                {
                    cmd.Parameters.AddRange(parameters);
                }

                using var adapter = new MySqlDataAdapter(cmd);
                var ds = new DataSet();
                adapter.Fill(ds);

                if (ds.Tables.Count == 0 || ds.Tables[0].Rows.Count == 0)
                {
                    return new ReturnInfo
                    {
                        isok = true,
                        message = "查询成功，但未返回数据",
                        status = 200,
                        response = null
                    };
                }

                return new ReturnInfo
                {
                    isok = true,
                    message = "查询成功",
                    status = 200,
                    response = Newtonsoft.Json.JsonConvert.SerializeObject(ds.Tables[0])
                };
            }
            catch (Exception ex)
            {
                return new ReturnInfo
                {
                    isok = false,
                    message = ex.Message,
                    status = 500,
                    response = null
                };
            }
        }

        /// <summary>
        /// 通用增删改执行方法（参数化版本，返回GUID）
        /// </summary>
        private ReturnInfo ExecuteNonQueryWithGuid(string sql, MySqlParameter[] parameters, string guid = null)
        {
            try
            {
                using var conn = new MySqlConnection(ReadConfig.Instance.MySQLConnectionString);
                conn.Open();

                using var cmd = new MySqlCommand(sql, conn);
                if (parameters != null)
                {
                    cmd.Parameters.AddRange(parameters);
                }

                int affectedRows = cmd.ExecuteNonQuery();

                if (affectedRows > 0)
                {
                    return new ReturnInfo
                    {
                        isok = true,
                        message = "操作成功",
                        status = 200,
                        response = guid ?? GetGuidFromParameters(parameters) ?? "操作完成"
                    };
                }

                return new ReturnInfo
                {
                    isok = false,
                    message = "操作未影响任何数据",
                    status = 400,
                    response = null
                };
            }
            catch (Exception ex)
            {
                return new ReturnInfo
                {
                    isok = false,
                    message = ex.Message,
                    status = 500,
                    response = null
                };
            }
        }

        /// <summary>
        /// 从参数中提取GUID值
        /// </summary>
        private string GetGuidFromParameters(MySqlParameter[] parameters)
        {
            if (parameters == null) return null;

            var guidParam = parameters.FirstOrDefault(p => p.ParameterName == "@guid");
            return guidParam?.Value?.ToString();
        }

        private static readonly List<string> DefaultProtocolOptions = new() { "强电无线", "弱电485" };
        private static readonly List<string> DefaultSwitchSeriesOptions = new() { "T1水立方", "T1水晶方", "T3水星阁" };

        private static void ApplyProjectCdrDefaults(GetAllProjectCdrRequest request)
        {
            if (request == null) return;

            request.Protocol = string.IsNullOrWhiteSpace(request.Protocol) ? "强电无线" : request.Protocol.Trim();
            request.SwitchSeries = string.IsNullOrWhiteSpace(request.SwitchSeries) ? "T1水立方" : request.SwitchSeries.Trim();
            // SocketSeries: null 代表未填写 -> 默认“玻璃”；空字符串代表前端选择“无” -> 保留空字符串
            if (request.SocketSeries == null || string.IsNullOrWhiteSpace(request.SocketSeries))
            {
                request.SocketSeries = "";
            }
            else
            {
                request.SocketSeries = request.SocketSeries.Trim();
            }

            request.ChineseFont = string.IsNullOrWhiteSpace(request.ChineseFont) ? "思源黑体" : request.ChineseFont.Trim();
            request.EnglishFont = string.IsNullOrWhiteSpace(request.EnglishFont) ? "Futura Std Medium" : request.EnglishFont.Trim();
            request.ChineseFontSize ??= 11.0m;
            request.EnglishFontSize ??= 7.0m;
        }

        private List<string> GetOrInitEnumValues(string fieldKey, IEnumerable<string> defaultValues)
        {
            var defaults = (defaultValues ?? Array.Empty<string>())
                .Where(v => !string.IsNullOrWhiteSpace(v))
                .Select(v => v.Trim())
                .Distinct()
                .ToList();

            string sql = "SELECT values_json FROM `cdr_library`.`tbl_web_enum_values` WHERE field_key=@field_key AND is_valid=1 LIMIT 1";
            var rs = ExecuteQuery(sql, new[] { new MySqlParameter("@field_key", fieldKey) });

            if (rs?.response == null)
            {
                UpsertEnumValues(fieldKey, defaults);
                return defaults;
            }

            try
            {
                var table = JsonConvert.DeserializeObject<DataTable>(rs.response.ToString());
                var valuesJson = table.Rows[0]["values_json"]?.ToString();
                var parsed = ParseEnumValuesJson(valuesJson, defaults);
                if (parsed.Count == 0)
                {
                    UpsertEnumValues(fieldKey, defaults);
                    return defaults;
                }
                return parsed;
            }
            catch
            {
                UpsertEnumValues(fieldKey, defaults);
                return defaults;
            }
        }

        private static List<string> ParseEnumValuesJson(string valuesJson, List<string> fallback)
        {
            if (string.IsNullOrWhiteSpace(valuesJson)) return fallback;

            try
            {
                var arr = JArray.Parse(valuesJson);
                var list = arr
                    .Select(t => t?.ToString())
                    .Where(s => !string.IsNullOrWhiteSpace(s))
                    .Select(s => s.Trim())
                    .Distinct()
                    .ToList();

                return list;
            }
            catch
            {
                return fallback;
            }
        }

        private void EnsureEnumValueExists(string fieldKey, string value, IEnumerable<string> defaultValues)
        {
            if (string.IsNullOrWhiteSpace(fieldKey) || string.IsNullOrWhiteSpace(value)) return;

            var normalizedValue = value.Trim();
            var values = GetOrInitEnumValues(fieldKey, defaultValues);

            if (values.Contains(normalizedValue)) return;

            values.Add(normalizedValue);
            UpsertEnumValues(fieldKey, values);
        }

        private void UpsertEnumValues(string fieldKey, IEnumerable<string> values)
        {
            var list = (values ?? Array.Empty<string>())
                .Where(v => !string.IsNullOrWhiteSpace(v))
                .Select(v => v.Trim())
                .Distinct()
                .ToList();

            string sql = @"INSERT INTO `cdr_library`.`tbl_web_enum_values`(field_key, values_json, is_valid, created_at)
VALUES(@field_key, @values_json, 1, NOW())
ON DUPLICATE KEY UPDATE values_json=@values_json, is_valid=1";

            var parameters = new[]
            {
                new MySqlParameter("@field_key", fieldKey ?? (object)DBNull.Value),
                new MySqlParameter("@values_json", JsonConvert.SerializeObject(list))
            };
            ExecuteNonQueryWithGuid(sql, parameters);
        }

        #endregion
    }

    #region tbl_web 表对应的模型类（保持不变）

    public class RoomConfig
    {
        public List<PanelConfig> Panels { get; set; }
    }

    public class PanelConfig
    {
        public int ModelId { get; set; }
        public List<LocationConfig> LocationConfigs { get; set; }
    }

    public class LocationConfig
    {
        public int LocationId { get; set; }
        public int? TxtId { get; set; }
        public int? IconId { get; set; }
    }

    public class QueryInfo
    {
        public int? FID { get; set; }
        public int? PID { get; set; }
        public int? MID { get; set; }
        public string? GUID { get; set; }
        public string? UserName { get; set; }
    }
    /// <summary>
    /// 请求类：用于 GetAllProjectCDR 接口
    /// 包含：GUID, UserName, Protocol(协议), ProjectName(项目名称), SwitchSeries(开关系列), SocketSeries(插座系列)
    /// </summary>
    public class GetAllProjectCdrRequest
    {
        public string? GUID { get; set; }
        public string? UserName { get; set; }
        public string? Protocol { get; set; }
        public string? ProjectName { get; set; }
        public string? SwitchSeries { get; set; }
        public string? SocketSeries { get; set; }
        public string? ChineseFont { get; set; }
        public decimal? ChineseFontSize { get; set; }
        public string? EnglishFont { get; set; }
        public decimal? EnglishFontSize { get; set; }
    }
    public class TblWebLog
    {
        public long id { get; set; }
        public string? username { get; set; }
        public DateTime? action_time { get; set; }
        public string? action_type { get; set; }
        public string? hotel_name { get; set; }
        public string? target_type { get; set; }
        public string? method { get; set; }
        public string? target_id { get; set; }
        public string? user_ip { get; set; }
        public string? user_location { get; set; }
        public string? user_agent { get; set; }
        public bool? is_valid { get; set; }
        public string? operation { get; set; }
        public TblWebLog()
        {
            this.action_time = DateTime.Now;
            this.is_valid = true;
        }
    }

    public class TblWebPanelList
    {
        public long id { get; set; }
        public string panel_list_name { get; set; }
        public string? gang_series { get; set; }
        public string? model_type { get; set; }
        public int? panel_count { get; set; }  // 新增字段
        public int? carving_quantity { get; set; }  // 新增字段：雕刻数量
        public string? position { get; set; }  // 新增字段：位置
        public string? carving_filename { get; set; }  // 新增字段：雕刻图
        public string? design_status { get; set; }  // 新增字段：设计状态
        public long? gang_material_id { get; set; }
        public string? panel_info_json { get; set; }
        public string? logo_json { get; set; }
        public string? cdr_filename { get; set; }
        public string? thumbnail_large { get; set; }
        public string? thumbnail_small { get; set; }
        public string? erp_part_number { get; set; }
        public string? remarks { get; set; }
        public DateTime? created_at { get; set; }
        public bool? is_valid { get; set; }
        public string? guid { get; set; }
        public TblWebPanelList()
        {
            this.created_at = DateTime.Now;
            this.is_valid = true;
        }
    }
    public class LogoConfig
    {
        public string LogoPosition { get; set; }
        public string LogoFileName { get; set; }
        public string LogoIndexNumber { get; set; }
    }

    public class TblWebPanelUnit
    {
        public long id { get; set; }
        public string panel_name { get; set; }
        public string? product_type { get; set; }
        public int? element_count { get; set; }
        public string? elements_json { get; set; }
        public string? cdr_filename { get; set; }
        public string? thumbnail_large { get; set; }
        public string? thumbnail_small { get; set; }
        public string? remarks { get; set; }
        public string? erp_part_number { get; set; }
        public string? design_status { get; set; }
        public string owner { get; set; }
        public DateTime created_at { get; set; }
        public DateTime published_at { get; set; }
        public string? share_type { get; set; }
        public bool? is_valid { get; set; }
        public string? guid { get; set; }
        public string? project_guid { get; set; }
        public int? pattern_id { get; set; }
        public string? color { get; set; }
        public string? direction { get; set; }
        public int? series_id { get; set; }
        public TblWebPanelUnit()
        {
            this.created_at = DateTime.Now;
            this.published_at = DateTime.Now;
            this.is_valid = true;
        }
    }

    public class TblWebProject
    {
        public long id { get; set; }
        public string report_id { get; set; }
        public string hotel_name { get; set; }
        public int room_type_count { get; set; }
        public string room_data_json { get; set; }
        public string? remarks { get; set; }
        public string creator { get; set; }
        public DateTime created_at { get; set; }
        public bool is_valid { get; set; }
        public string? guid { get; set; }
        public string? edit_status { get; set; }
        public string? cdr_filename { get; set; }
        public string? protocol { get; set; }
        public string? project_name { get; set; }
        public string? switch_series { get; set; }
        public string? socket_series { get; set; }
        public string? cn_font { get; set; }
        public decimal? cn_font_size { get; set; }
        public string? en_font { get; set; }
        public decimal? en_font_size { get; set; }
        public TblWebProject()
        {
            this.created_at = DateTime.Now;
            this.is_valid = true;
        }
    }

    public class TblWebRoomType
    {
        public long id { get; set; }
        public string room_name { get; set; }
        public string? room_description { get; set; }
        public string? panel_group_count { get; set; }
        public string? remarks { get; set; }
        public string creator { get; set; }
        public DateTime? created_at { get; set; }
        public bool? is_valid { get; set; }
        public string? guid { get; set; }

        public TblWebRoomType()
        {
            this.created_at = DateTime.Now;
            this.is_valid = true;
        }
    }
    #endregion
}

#region 请求参数类

/// <summary>
/// 复制面板单元请求参数
/// </summary>
public class CopyPanelUnitRequest
{
    /// <summary>
    /// 原始面板单元的GUID
    /// </summary>
    public string OriginalGuid { get; set; }

    /// <summary>
    /// 新记录的owner值
    /// </summary>
    public string NewOwner { get; set; }

    /// <summary>
    /// 新记录的project_guid值
    /// </summary>
    public string NewProjectGuid { get; set; }
}

/// <summary>
/// 复制面板组请求参数
/// </summary>
public class CopyPanelListRequest
{
    /// <summary>
    /// 原始面板组的GUID
    /// </summary>
    public string OriginalGuid { get; set; }

    /// <summary>
    /// 目标房型的GUID（同酒店/同项目下的另一房型）
    /// </summary>
    public string TargetRoomGuid { get; set; }
}
#endregion