Explorar o código

自贡12345 - 案例库

libin hai 4 meses
pai
achega
e0846f7e8f

+ 399 - 0
src/Hotline.Api/Controllers/CaseController.cs

@@ -0,0 +1,399 @@
+using MapsterMapper;
+using Microsoft.AspNetCore.Mvc;
+using XF.Domain.Authentications;
+using XF.Domain.Repository;
+using Hotline.Application.Caselibrary;
+using Hotline.Share.Dtos.Caselibrary;
+using SqlSugar;
+using Hotline.CaseLibrary;
+using XF.Domain.Exceptions;
+using Hotline.Share.Dtos;
+using Hotline.Share.Tools;
+using Hotline.Share.Enums.Caselibrary;
+using Hotline.Application.ExportWord;
+using Hotline.Application.Tools;
+
+namespace Hotline.Api.Controllers
+{
+    /// <summary>
+    /// 案例库
+    /// </summary>
+    public class CaseController : BaseController
+    {
+
+        #region 注入
+
+        private readonly IMapper _mapper;
+        private readonly ISessionContext _sessionContext;
+        private readonly ICaseApplication _CaseApplication;
+        private readonly IRepository<CaseType> _CaseTypeRepository;
+        private readonly IRepository<CaseList> _CaseListRepository;
+        private readonly IRepository<CaseCollect> _CaseCollectRepository;
+        private readonly IWordHelperService _wordHelperService;
+
+
+        public CaseController(
+           IMapper mapper,
+           ISessionContext sessionContext,
+           ICaseApplication CaseApplication,
+           IRepository<CaseType> CaseTypeRepository,
+           IRepository<CaseList> CaseListRepository,
+           IRepository<CaseCollect> CaseCollectRepository,
+           IWordHelperService wordHelperService)
+        {
+            _mapper = mapper;
+            _sessionContext = sessionContext;
+            _CaseApplication = CaseApplication;
+            _CaseTypeRepository = CaseTypeRepository;
+            _CaseListRepository = CaseListRepository;
+            _CaseCollectRepository = CaseCollectRepository;
+            _wordHelperService = wordHelperService;
+        }
+
+        #endregion
+
+        #region 案例库类型管理
+
+        /// <summary>
+        /// 获取列表层级分类
+        /// </summary>
+        /// <param name="IsEnable">是否启用</param>
+        /// <returns></returns>
+        [HttpGet("type/treelist")]
+        public async Task<List<CaseTypeDto>> QueryAllTreeList(bool? IsEnable)
+        {
+            return await _CaseTypeRepository.Queryable()
+                .WhereIF(IsEnable.HasValue, x => x.IsEnable == IsEnable)
+                .Where(x => SqlFunc.Subqueryable<CaseTypeOrg>().Where(to => to.TypeId == x.Id).Any() ||
+                SqlFunc.Subqueryable<CaseTypeOrg>().Where(to => to.TypeId == x.Id).NotAny()
+                )
+                .Select(x => new CaseTypeDto()
+                {
+                    Id = x.Id.SelectAll()
+                }
+                )
+                .OrderBy(x => x.Sort).ToTreeAsync(it => it.children, it => it.ParentId, null, it => it.Id);
+        }
+
+        /// <summary>
+        /// 新增
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("type/add")]
+        public async Task<string> AddType([FromBody] AddCaseTypeDto dto)
+        {
+            return await _CaseApplication.AddTypeAsync(dto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 编辑
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPut("type/update")]
+        public async Task UpdateType([FromBody] UpdateCaseTypeDto dto)
+        {
+            await _CaseApplication.UpdateTypeAsync(dto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 查询详情
+        /// </summary>
+        /// <param name="Id"></param>
+        /// <returns></returns>
+        [HttpGet("type/info/{Id}")]
+        public async Task<CaseType> GetType(string Id)
+        {
+            var types = await _CaseTypeRepository.Queryable()
+                .Includes(x => x.CaseTypeOrgs)   // 填充子对象
+                .Where(x => x.Id == Id)
+                .FirstAsync(HttpContext.RequestAborted);
+            if (types is null)
+                throw UserFriendlyException.SameMessage("查询失败!");
+            return types;
+        }
+
+        /// <summary>
+        /// 删除
+        /// </summary>
+        /// <param name="Id"></param>
+        /// <returns></returns>
+        [HttpDelete("type/remove/{Id}")]
+        public async Task RemoveType(string Id)
+        {
+            await _CaseApplication.RemoveTypeAsync(Id, HttpContext.RequestAborted);
+        }
+
+        #endregion
+
+        #region 案例库管理
+
+        /// <summary>
+        /// 案例库分类列表
+        /// </summary>
+        /// <param name="IsEnable"></param>
+        /// <returns></returns>
+        [HttpGet("list/treelist")]
+        public async Task<List<CaseTypeDto>> QueryAllCaseTypeTreeList(bool? IsEnable)
+        {
+            return await _CaseTypeRepository.Queryable()
+                .WhereIF(IsEnable.HasValue, x => x.IsEnable == IsEnable)
+                .Where(x => SqlFunc.Subqueryable<CaseTypeOrg>().Where(to => to.TypeId == x.Id).Any() ||
+                SqlFunc.Subqueryable<CaseTypeOrg>().Where(to => to.TypeId == x.Id).NotAny()
+                )
+                .Select(x => new CaseTypeDto()
+                {
+                    Id = x.Id.SelectAll(),
+                    CaseNum = SqlFunc.Subqueryable<CaseRelationType>().LeftJoin<CaseList>((kr, k) => kr.CaseId == k.Id)
+                         .Where((kr, k) => kr.CaseTypeSpliceName.StartsWith(x.SpliceName))
+                         .DistinctCount(kr => kr.CaseId)
+                }
+                )
+                .OrderBy(x => x.Sort).ToTreeAsync(it => it.children, it => it.ParentId, null, it => it.Id);
+        }
+
+        /// <summary>
+        /// 案例库列表
+        /// </summary>
+        /// <param name="pagedDto"></param>
+        /// <returns></returns>
+        [HttpGet("list")]
+        public async Task<PagedDto<CaseDataDto>> QueryAllCaseList([FromQuery] CaseListDto pagedDto)
+        {
+            return (await _CaseApplication.QueryAllCaseListAsync(pagedDto, HttpContext.RequestAborted)).ToPaged();
+        }
+
+        /// <summary>
+        /// 案例库草稿
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("list/draft")]
+        public async Task<string> CaseDraft([FromBody] AddCaseListDto dto)
+        {
+            dto.Status = ECaseStatus.NewDrafts;
+            return await _CaseApplication.AddCaseAsync(dto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 案例库草稿修改
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPut("list/draftupdate")]
+        public async Task UpdateCaseDraft([FromBody] UpdateCaseListDto dto)
+        {
+            dto.Status = ECaseStatus.NewDrafts;
+            await _CaseApplication.UpdateCaseAsync(dto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 案例库草稿上架到审核
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPut("list/draftadd")]
+        public async Task AddCaseDraft([FromBody] UpdateCaseListDto dto)
+        {
+            dto.ApplyStatus = ECaseApplyStatus.Add;
+            dto.Status = ECaseStatus.Auditing;
+            await _CaseApplication.UpdateCaseAsync(dto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 案例库新增到审核
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("list/add")]
+        public async Task<string> AddCase([FromBody] AddCaseListDto dto)
+        {
+            dto.ApplyStatus = ECaseApplyStatus.Add;
+            dto.Status = ECaseStatus.Auditing;
+            return await _CaseApplication.AddCaseAsync(dto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 案例库编辑提交到审核
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPut("list/update")]
+        public async Task UpdateCase([FromBody] UpdateCaseListDto dto)
+        {
+            dto.ApplyStatus = ECaseApplyStatus.Update;
+            dto.Status = ECaseStatus.Auditing;
+            await _CaseApplication.UpdateCaseAsync(dto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 案例库下架
+        /// </summary>
+        /// <param name="Id">案例库ID</param>
+        /// <returns></returns>
+        [HttpGet("list/offshelf/{Id}")]
+        public async Task OffshelfCase(string Id)
+        {
+            UpdateCaseListDto dto = new UpdateCaseListDto();
+            dto.Id = Id;
+            dto.ApplyStatus = ECaseApplyStatus.Offshelf;
+            dto.Status = ECaseStatus.Auditing;
+
+            await _CaseApplication.AuditCaseAsync(dto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 案例库审核(新增、修改、下架)
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPut("list/examin")]
+        public async Task ExaminCase([FromBody] AuditCaseListDto dto)
+        {
+            var Case = await _CaseListRepository.GetAsync(dto.Id);
+            if (Case == null)
+                throw UserFriendlyException.SameMessage("案例库查询失败");
+
+            var CaseDto = _mapper.Map<UpdateCaseListDto>(Case);
+
+            if (dto.State == 0)
+            {//不同意
+                CaseDto.Status = ECaseStatus.Revert;
+            }
+            else if (dto.State == 1)
+            {//同意 
+                if (CaseDto.ApplyStatus == ECaseApplyStatus.Add)
+                {
+                    CaseDto.Status = ECaseStatus.OnShelf;
+                }
+                if (CaseDto.ApplyStatus == ECaseApplyStatus.Update)
+                {
+                    CaseDto.Status = ECaseStatus.OnShelf;
+                    CaseDto.UpdateTime = DateTime.Now;
+                }
+                if (CaseDto.ApplyStatus == ECaseApplyStatus.Offshelf)
+                {
+                    CaseDto.Status = ECaseStatus.OffShelf;
+                }
+            }
+            CaseDto.Id = dto.Id;
+            CaseDto.ExaminTime = DateTime.Now;
+            CaseDto.ExaminManId = _sessionContext.UserId;
+            CaseDto.ExaminOrganizeId = _sessionContext.OrgId;
+
+            await _CaseApplication.AuditCaseAsync(CaseDto, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 案例库详情
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("list/info")]
+        public async Task<CaseInfoDto> GetCase([FromBody] PvCaseListDto dto)
+        {
+            return await _CaseApplication.GetCaseAsync(dto.Id, dto.IsAddPv, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 案例库评分
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPut("list/score")]
+        public async Task ScoreCase([FromBody] PvCaseListDto dto)
+        {
+            var collect = await _CaseCollectRepository.GetAsync(x => x.CaseId == dto.Id && x.CreatorId == _sessionContext.UserId);
+            if (collect != null)
+            {
+                if (collect.Score > 0)
+                    throw UserFriendlyException.SameMessage("当前知识已经评分");
+
+                collect.Score = dto.Score;
+                await _CaseCollectRepository.UpdateAsync(collect, HttpContext.RequestAborted);
+            }
+            else
+            {
+                collect = new CaseCollect();
+                collect.CaseId = dto.Id;
+                collect.Score = dto.Score;
+                await _CaseCollectRepository.AddAsync(collect, HttpContext.RequestAborted);
+            }
+
+            //计算总分
+            var sugar = _CaseCollectRepository.Queryable().Where(x => x.CaseId == dto.Id);
+            var count = await sugar.CountAsync();
+            var collects = await sugar.SumAsync(x => x.Score);
+            var scoreTemp = collects / count;
+            var Case = await _CaseListRepository.GetAsync(x => x.Id == dto.Id);
+            if (Case != null)
+            {
+                Case.Score = decimal.Round(scoreTemp.Value, 1);
+                await _CaseListRepository.UpdateAsync(Case, HttpContext.RequestAborted);
+            }
+        }
+
+        /// <summary>
+        /// 案例库查重
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("list/exist")]
+        public async Task<bool> ExistCase([FromBody] CaseExistDto dto)
+        {
+            var any = await _CaseListRepository.Queryable()
+                .Where(x => x.Status == ECaseStatus.Auditing || x.Status >= ECaseStatus.OnShelf)
+                .WhereIF(!string.IsNullOrEmpty(dto.Title), x => x.Title.Equals(dto.Title))
+                .WhereIF(!string.IsNullOrEmpty(dto.Content), x => x.Content.Equals(dto.Content))
+                .WhereIF(!string.IsNullOrEmpty(dto.Id), x => x.Id != dto.Id)
+                .AnyAsync();
+            return any;
+        }
+
+        /// <summary>
+        /// 案例库导出
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("list/info/export")]
+        public async Task<IActionResult> CaseInfoExport([FromBody] CaseInfoExportDto dto)
+        {
+            if (dto.Ids.Length > 1)
+            {
+                var streams = await _CaseApplication.CaseInfoListExportAsync(dto, HttpContext.RequestAborted);
+                byte[] fileBytes = _wordHelperService.ConvertZipStream(streams);
+                var name = DateTime.Now.ToString("yyyyMMddHHmmss");
+                return File(fileBytes, "application/octet-stream", $"{name}.zip");
+            }
+            var info = await _CaseListRepository.GetAsync(dto.Ids[0]) ?? throw UserFriendlyException.SameMessage("案例不存在");
+            return info.Content.HtmlToStream(dto.FileType).GetFileStreamResult(dto.FileType, info.Title, false);
+        }
+
+        #endregion
+
+        #region 案例库检索
+
+        /// <summary>
+        /// 案例库列表前10
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("list/top10")]
+        public async Task<List<CasePageViewDto>> QueryTop10CaseList()
+        {
+            return await _CaseListRepository.Queryable()
+                .Take(10)
+                .Where(x => x.Status == ECaseStatus.OnShelf)
+                .Select(x => new CasePageViewDto
+                {
+                    Id = x.Id,
+                    Title = x.Title,
+                    PageView = x.PageView
+                })
+                .OrderBy(x => x.PageView, OrderByType.Desc)
+                .ToListAsync();
+        }
+
+        #endregion
+    }
+}

+ 0 - 3
src/Hotline.Api/Controllers/PlanController.cs

@@ -12,9 +12,6 @@ using Hotline.Share.Tools;
 using Hotline.Share.Enums.Planlibrary;
 using Hotline.Application.ExportWord;
 using Hotline.Application.Tools;
-using Hotline.KnowledgeBase;
-using Hotline.Share.Dtos.Knowledge;
-using System.Data;
 
 namespace Hotline.Api.Controllers
 {

+ 568 - 0
src/Hotline.Application/Caselibrary/CaseApplication.cs

@@ -0,0 +1,568 @@
+using Hotline.Settings.Hotspots;
+using Hotline.Share.Dtos.Knowledge;
+using Hotline.Share.Dtos.Caselibrary;
+using Hotline.Share.Enums.Caselibrary;
+using MapsterMapper;
+using XF.Domain.Authentications;
+using XF.Domain.Dependency;
+using XF.Domain.Exceptions;
+using XF.Domain.Repository;
+using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.SeedData;
+using Hotline.File;
+using Hotline.Application.Bulletin;
+using Hotline.Share.Tools;
+using Hotline.Application.Tools;
+using SqlSugar;
+using Hotline.CaseLibrary;
+
+namespace Hotline.Application.Caselibrary
+{
+    /// <summary>
+    /// 案例库处理
+    /// </summary>
+    public class CaseApplication : ICaseApplication, IScopeDependency
+    {
+
+        #region 注册
+
+        private readonly IRepository<CaseList> _CaseListRepository;                      //案例库列表
+        private readonly IRepository<CaseRelationType> _CaseRelationTypeRepository;      //案例库关联类型
+        private readonly IRepository<CaseType> _CaseTypeRepository;                      //案例库分类管理
+        private readonly IRepository<CaseTypeOrg> _CaseTypeOrgRepository;                //案例库分类关联机构
+        private readonly ISessionContext _sessionContext;
+        private readonly IMapper _mapper;
+        private readonly IRepository<Hotspot> _hotspotTypeRepository;
+        private readonly IFileRepository _fileRepository;
+        private readonly IBulletinApplication _bulletinApplication;
+
+        public CaseApplication(
+            IRepository<CaseList> CaseListRepository,
+            IRepository<CaseRelationType> CaseRelationTypeRepository,
+            IRepository<CaseType> CaseTypeRepository,
+            IRepository<CaseTypeOrg> CaseTypeOrgRepository,
+            ISessionContext sessionContext,
+            IMapper mapper,
+            IRepository<Hotspot> hotspotTypeRepository,
+            IFileRepository fileRepository,
+            IBulletinApplication bulletinApplication)
+        {
+            _CaseListRepository = CaseListRepository;
+            _CaseRelationTypeRepository = CaseRelationTypeRepository;
+            _CaseTypeRepository = CaseTypeRepository;
+            _CaseTypeOrgRepository = CaseTypeOrgRepository;
+            _sessionContext = sessionContext;
+            _mapper = mapper;
+            _hotspotTypeRepository = hotspotTypeRepository;
+            _fileRepository = fileRepository;
+            _bulletinApplication = bulletinApplication;
+        }
+
+        #endregion
+
+        #region 案例库类型管理
+
+        #region 案例库类型 - 新增
+
+        /// <summary>
+        /// 案例库类型 - 新增
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public async Task<string> AddTypeAsync(AddCaseTypeDto dto, CancellationToken cancellationToken)
+        {
+            var sandard = await _CaseTypeRepository.GetAsync(p => p.ParentId == dto.ParentId && p.Name == dto.Name && p.IsDeleted == false, cancellationToken);
+            if (sandard is not null)
+                throw UserFriendlyException.SameMessage("当前层级已存在相同名称的分类!");
+
+            var type = _mapper.Map<CaseType>(dto);
+            type.InitId();
+            type.IsEnable = true;
+            //获取分类名称全称
+            string FullName = await GetFullName(type.ParentId);
+            //处理全称,如果为第一级直接用全称,否则获取全称后拼接名称
+            type.SpliceName = string.IsNullOrEmpty(FullName) ? dto.Name : FullName + "-" + dto.Name;
+            var id = await _CaseTypeRepository.AddAsync(type, cancellationToken);
+            return id;
+        }
+
+        #endregion
+
+        #region 案例库类型 - 编辑
+
+        /// <summary>
+        /// 案例库类型 - 编辑
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public async Task UpdateTypeAsync(UpdateCaseTypeDto dto, CancellationToken cancellationToken)
+        {
+            //查询原有数据
+            var type = await _CaseTypeRepository.GetAsync(dto.Id, cancellationToken);
+            if (type is null)
+                throw UserFriendlyException.SameMessage("编辑失败!");
+            bool result = type.Name != dto.Name || type.ParentId != dto.ParentId;
+            //是否更改分类名称或者层级
+
+            //转换
+            _mapper.Map(dto, type);
+            //如果更改了名称或者修改了层级,则修改全称,未更改不修改
+            if (result)
+            {
+                string FullName = await GetFullName(type.ParentId);//获取分类名称全称
+                type.SpliceName = string.IsNullOrEmpty(FullName) ? dto.Name : FullName + "-" + dto.Name;//处理全称,如果为第一级直接用全称,否则获取全称后拼接名称
+            }
+
+            //修改数据
+            await _CaseTypeRepository.UpdateAsync(type, cancellationToken);
+
+            //如果修改了名称,对应修改子分类全称
+            if (result)
+                await UpdateChildNode(type.Id);
+
+            // 修改关联机构
+            await _CaseTypeOrgRepository.RemoveAsync(x => x.TypeId == type.Id, false, cancellationToken);
+            if (dto.TypeOrgDtos != null && dto.TypeOrgDtos.Any())
+            {
+                List<CaseTypeOrg> orgs = _mapper.Map<List<CaseTypeOrg>>(dto.TypeOrgDtos);
+                orgs.ForEach(x => x.TypeId = type.Id);
+                await _CaseTypeOrgRepository.AddRangeAsync(orgs, cancellationToken);
+            }
+        }
+
+        #endregion
+
+        #region 案例库类型 - 删除
+
+        /// <summary>
+        /// 案例库类型 - 删除
+        /// </summary>
+        /// <param name="Id"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public async Task RemoveTypeAsync(string Id, CancellationToken cancellationToken)
+        {
+            //查询数据是否存在
+            var sandard = await _CaseTypeRepository.GetAsync(p => p.Id == Id && p.IsDeleted == false, cancellationToken);
+            if (sandard is null)
+                throw UserFriendlyException.SameMessage("分类不存在!");
+
+            //查询是否有子级分类
+            var checkChild = await _CaseTypeRepository.CountAsync(p => p.ParentId == Id && p.IsDeleted == false, cancellationToken);
+            if (checkChild > 0)
+                throw UserFriendlyException.SameMessage("存在子级分类!");
+
+            //查询是否有案例分类
+            var checkKnowledge = await _CaseListRepository.CountAsync(p => p.CaseTypes.Any(t => t.CaseId == Id), cancellationToken);
+            if (checkKnowledge > 0)
+                throw UserFriendlyException.SameMessage("分类存在案例!");
+
+            //删除操作
+            await _CaseTypeRepository.RemoveAsync(sandard, true, cancellationToken);
+        }
+
+        #endregion
+
+        #endregion
+
+        #region 案例库管理
+
+        #region 案例库 - 列表
+
+        /// <summary>
+        /// 案例库 - 列表
+        /// </summary>
+        /// <param name="pagedDto"></param>
+        /// <returns></returns>
+        public async Task<(int, IList<CaseDataDto>)> QueryAllCaseListAsync(CaseListDto pagedDto, CancellationToken cancellationToken)
+        {
+            //if (!_sessionContext.OrgIsCenter)
+            //{// 部门只能查询【部门案例库】
+            //    pagedDto.Attribution = "部门案例库";
+            //}
+
+            var typeSpliceName = string.Empty;
+            var hotspotHotSpotFullName = string.Empty;
+
+            if (!string.IsNullOrEmpty(pagedDto.CaseTypeID))
+            {
+                var type = await _CaseTypeRepository.GetAsync(x => x.Id == pagedDto.CaseTypeID);
+                typeSpliceName = type?.SpliceName;
+            }
+
+            if (!string.IsNullOrEmpty(pagedDto.HotspotId))
+            {
+                var hotspot = await _hotspotTypeRepository.GetAsync(x => x.Id == pagedDto.HotspotId);
+                hotspotHotSpotFullName = hotspot?.HotSpotFullName;
+            }
+
+            //单表分页
+            var (total, temp) = await _CaseListRepository.Queryable()
+                .Includes(x => x.CaseTypes)
+                .Includes(x => x.HotspotType)
+                .Where(x => x.IsDeleted == false)
+                .Where(x => (x.Status == ECaseStatus.Drafts && x.CreatorId == _sessionContext.UserId) || (x.Status != ECaseStatus.Drafts))
+                .WhereIF(OrgSeedData.CenterId != pagedDto.CreateOrgId && !string.IsNullOrEmpty(pagedDto.CreateOrgId), x => x.CreatorOrgId != null && x.CreatorOrgId.StartsWith(pagedDto.CreateOrgId!))
+                .WhereIF(!string.IsNullOrEmpty(pagedDto.Attribution), x => x.Attribution == pagedDto.Attribution)
+                .WhereIF(!string.IsNullOrEmpty(pagedDto.Title), x => x.Title.Contains(pagedDto.Title))
+                .WhereIF(!string.IsNullOrEmpty(pagedDto.Keyword), x => x.Title.Contains(pagedDto.Keyword!) ||
+                                                                  x.CreatorName!.Contains(pagedDto.Keyword!) ||
+                                                                  x.CreatorOrgName!.Contains(pagedDto.Keyword!))
+                .WhereIF(pagedDto.Status.HasValue && pagedDto.Status != ECaseStatus.OffShelf &&
+                                                     pagedDto.Status != ECaseStatus.NewDrafts &&
+                                                     pagedDto.Status != ECaseStatus.All,
+                                                        x => x.Status == pagedDto.Status && ((x.ExpiredTime != null && x.ExpiredTime > DateTime.Now) || x.ExpiredTime == null))
+                .WhereIF(pagedDto.Status.HasValue && pagedDto.Status == ECaseStatus.OffShelf, x => x.Status == pagedDto.Status || (x.ExpiredTime != null && x.ExpiredTime < DateTime.Now && x.Status != ECaseStatus.Drafts))
+                .WhereIF(pagedDto.Status.HasValue && pagedDto.Status == ECaseStatus.NewDrafts, x => x.Status == ECaseStatus.Drafts || x.Status == ECaseStatus.Revert)
+                .WhereIF(pagedDto.IsPublic.HasValue, x => x.IsPublic == pagedDto.IsPublic)
+                .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.CaseTypes.Any(t => t.CaseTypeSpliceName.StartsWith(typeSpliceName)))
+                .WhereIF(!string.IsNullOrEmpty(hotspotHotSpotFullName), x => x.HotspotType.HotSpotFullName.EndsWith(hotspotHotSpotFullName!))
+
+                .WhereIF(pagedDto.CreationTimeStart.HasValue, x => x.CreationTime >= pagedDto.CreationTimeStart)
+                .WhereIF(pagedDto.CreationTimeEnd.HasValue, x => x.CreationTime <= pagedDto.CreationTimeEnd)
+
+                .WhereIF(pagedDto.OnShelfTimeStart.HasValue, x => x.OnShelfTime >= pagedDto.OnShelfTimeStart)
+                .WhereIF(pagedDto.OnShelfTimeEnd.HasValue, x => x.OnShelfTime <= pagedDto.OnShelfTimeEnd)
+
+                .WhereIF(pagedDto.OffShelfTimeStart.HasValue, x => x.OffShelfTime >= pagedDto.OffShelfTimeStart)
+                .WhereIF(pagedDto.OffShelfTimeEnd.HasValue, x => x.OffShelfTime <= pagedDto.OffShelfTimeEnd)
+
+                .WhereIF(pagedDto.UpdateTimeStart.HasValue, x => x.UpdateTime >= pagedDto.UpdateTimeStart)
+                .WhereIF(pagedDto.UpdateTimeEnd.HasValue, x => x.UpdateTime <= pagedDto.UpdateTimeEnd)
+
+                .WhereIF(pagedDto.ExaminTimeStart.HasValue, x => x.ExaminTime >= pagedDto.ExaminTimeStart)
+                .WhereIF(pagedDto.ExaminTimeEnd.HasValue, x => x.ExaminTime <= pagedDto.ExaminTimeEnd)
+
+                .OrderByIF(string.IsNullOrEmpty(pagedDto.SortField), d => d.CreationTime, OrderByType.Desc)
+                .OrderByIF(pagedDto is { SortField: "PageView" }, d => d.PageView, OrderByType.Desc)         //阅读量
+                .OrderByIF(pagedDto is { SortField: "Score" }, d => d.Score, OrderByType.Desc)               //评分
+                .OrderByIF(pagedDto is { SortField: "CreationTime" }, d => d.CreationTime, OrderByType.Desc) //创建时间
+
+                .ToPagedListAsync(pagedDto.PageIndex, pagedDto.PageSize, cancellationToken);
+
+            return (total, _mapper.Map<IList<CaseDataDto>>(temp));
+        }
+
+        #endregion
+
+        #region 案例库 - 新增
+
+        /// <summary>
+        /// 新增
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public async Task<string> AddCaseAsync(AddCaseListDto dto, CancellationToken cancellationToken)
+        {
+            var pList = _mapper.Map<CaseList>(dto);
+
+            var any = await _CaseListRepository.Queryable().Where(x => x.Status == ECaseStatus.OnShelf && x.Title == dto.Title).AnyAsync();
+            if (any)
+                throw UserFriendlyException.SameMessage("当前案例标题存在重复标题!");
+
+            if (dto.Files != null && dto.Files.Count > 0)
+                pList.FileJson = await _fileRepository.AddFileAsync(dto.Files, pList.Id, "", cancellationToken);
+            await _CaseListRepository.AddAsync(pList, cancellationToken);
+
+            if (dto.CaseType.Any())
+            {
+                List<CaseRelationType> types = _mapper.Map<List<CaseRelationType>>(dto.CaseType);
+                types.ForEach(x => x.CaseId = pList.Id);
+                await _CaseRelationTypeRepository.AddRangeAsync(types, cancellationToken);
+            }
+
+            return pList.Id;
+        }
+
+        #endregion
+
+        #region 案例库 - 修改
+
+        /// <summary>
+        /// 修改
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public async Task UpdateCaseAsync(UpdateCaseListDto dto, CancellationToken cancellationToken)
+        {
+            var Case = await _CaseListRepository.GetAsync(dto.Id);
+
+            if (Case == null)
+                throw UserFriendlyException.SameMessage("案例库查询失败");
+
+            if ((Case.Status == ECaseStatus.OnShelf || Case.Status == ECaseStatus.Auditing) && (Case.ExpiredTime.HasValue && Case.ExpiredTime.Value > DateTime.Now))
+                throw UserFriendlyException.SameMessage("案例库数据不可修改");
+
+            var any = await _CaseListRepository.Queryable().Where(x => x.Status == ECaseStatus.OnShelf && x.Title == dto.Title && x.Id != dto.Id).AnyAsync();
+            if (any)
+                throw UserFriendlyException.SameMessage("当前案例标题存在重复标题!");
+
+            _mapper.Map(dto, Case);
+
+            Case.HotspotId = dto.HotspotId;
+
+            if (dto.Files != null && dto.Files.Count > 0)
+                Case.FileJson = await _fileRepository.AddFileAsync(dto.Files, Case.Id, "", cancellationToken);
+            else
+                Case.FileJson = new List<Share.Dtos.File.FileJson>();
+
+            await _CaseListRepository.UpdateNullAsync(Case, cancellationToken);
+            if (dto.CaseType.Any())
+            {
+                var anyRelationTypes = await _CaseRelationTypeRepository.Queryable().Where(x => x.CaseId == Case.Id).ToListAsync();
+                if (anyRelationTypes.Any())
+                    await _CaseRelationTypeRepository.RemoveRangeAsync(anyRelationTypes);
+                List<CaseRelationType> types = _mapper.Map<List<CaseRelationType>>(dto.CaseType);
+                types.ForEach(x => x.CaseId = dto.Id);
+                await _CaseRelationTypeRepository.AddRangeAsync(types, cancellationToken);
+            }
+        }
+
+        #endregion
+
+        #region 案例库 - 下架审核
+
+        /// <summary>
+        /// 下架审核
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public async Task AuditCaseAsync(UpdateCaseListDto dto, CancellationToken cancellationToken)
+        {
+            var Case = await _CaseListRepository.GetAsync(dto.Id);
+
+            if (Case == null)
+                throw UserFriendlyException.SameMessage("案例库查询失败");
+
+            Case.Status = (ECaseStatus)dto.Status!;
+            Case.ApplyStatus = (ECaseApplyStatus)dto.ApplyStatus!;
+            Case.ExaminTime = dto.ExaminTime;
+            Case.ExaminManId = dto.ExaminManId;
+            Case.ExaminOrganizeId = dto.ExaminOrganizeId;
+            Case.UpdateTime = dto.UpdateTime;
+
+            await _CaseListRepository.UpdateNullAsync(Case, cancellationToken);
+        }
+
+        #endregion
+
+        #region 案例库 - 详情
+
+        /// <summary>
+        /// 详情
+        /// </summary>
+        /// <param name="Id"></param>
+        /// <param name="IsAddPv">默认不增加,false不增加,true增加浏览量</param>
+        /// <returns></returns>
+        public async Task<CaseInfoDto> GetCaseAsync(string Id, bool? IsAddPv, CancellationToken cancellationToken)
+        {
+            var Case = await _CaseListRepository.GetAsync(Id);
+            if (Case == null)
+                throw UserFriendlyException.SameMessage("案例库查询失败");
+            ;
+            //转化
+            var CaseInfoDto = _mapper.Map<CaseInfoDto>(Case);
+
+            if (Case != null && !string.IsNullOrEmpty(Case.Content))
+                CaseInfoDto.Content = _bulletinApplication.GetSiteUrls(Case.Content);
+
+            // 热点
+            //var hot = await _hotspotTypeRepository.GetAsync(Case.HotspotId, cancellationToken);
+            //if (hot != null)
+            //    CaseDto.HotspotId = hot.HotSpotFullName;
+
+            if (CaseInfoDto.FileJson != null && CaseInfoDto.FileJson.Any())
+            {
+                var ids = CaseInfoDto.FileJson.Select(x => x.Id).ToList();
+                CaseInfoDto.Files = await _fileRepository.GetFilesAsync(ids, cancellationToken);
+            }
+
+            // 更新浏览量
+            if (IsAddPv == true)
+            {
+                //修改浏览量
+                Case.PageView++;
+                //修改点击量
+                await _CaseListRepository.UpdateAsync(Case, cancellationToken);
+            }
+            return CaseInfoDto;
+        }
+
+        #endregion
+
+        #region 案例库 - 批量导出
+
+        /// <summary>
+        /// 案例库 - 批量导出
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        public async Task<Dictionary<string, Stream>> CaseInfoListExportAsync(CaseInfoExportDto dto, CancellationToken cancellationToken)
+        {
+            var streamList = new Dictionary<string, Stream>();
+            var knowList = await _CaseListRepository.Queryable()
+                .Where(m => dto.Ids.Contains(m.Id))
+                .Select(m => new { m.Title, m.Content })
+                .ToListAsync(cancellationToken);
+
+            var tasks = knowList.Select(async item =>
+            {
+                var stream = await Task.Run(() => item.Content.HtmlToStream(dto.FileType), cancellationToken);
+                return new KeyValuePair<string, Stream>(
+                    item.Title + dto.FileType.GetFileExtension(),
+                    stream
+                );
+            });
+
+            var results = await Task.WhenAll(tasks);
+
+            foreach (var kvp in results)
+            {
+                if (!streamList.ContainsKey(kvp.Key))
+                {
+                    streamList.Add(kvp.Key, kvp.Value);
+                }
+            }
+
+            return streamList;
+        }
+
+        #endregion
+
+        #endregion
+
+        #region 私有方法
+
+        #region 查询所有子级
+
+        /// <summary>
+        /// 查询所有子级
+        /// </summary>
+        /// <param name="treeDatas">分类数据</param>
+        /// <param name="ID">需要查询哪级下面的分类</param>
+        /// <param name="checkId">选中的数据ID</param>
+        /// <returns></returns>
+        private List<TreeListDto> GetChildren(List<CaseType> treeDatas, string ID, string? checkId)
+        {
+            List<TreeListDto> nodeList = new();
+            //根据ID查询子级
+            var children = treeDatas.Where(q => q.ParentId == ID);
+            foreach (var dr in children)
+            {
+                //组装数据
+                TreeListDto node = new()
+                {
+                    name = dr.Name,
+                    ParentID = dr.ParentId,
+                    value = dr.Id,
+                    IsEnable = dr.IsEnable
+                };
+                //是否选中
+                if (!string.IsNullOrEmpty(checkId) && checkId != Guid.Empty.ToString() && checkId == dr.Id)
+                    node.selected = true;
+                //子级数据赋值
+                node.children = GetChildren(treeDatas, node.value, checkId);
+                //添加数据
+                nodeList.Add(node);
+            }
+            return nodeList;
+        }
+
+        #endregion
+
+        #region  获取全称
+
+        /// <summary>
+        /// 获取全称
+        /// </summary>
+        /// <param name="Id"></param>
+        /// <returns></returns>
+        private async Task<string> GetFullName(string? Id)
+        {
+            //获取全部父级名称
+            var list = await GetParentNode(Id);
+            //倒叙
+            list.Reverse();
+            //拆分
+            return string.Join("-", list.ToArray());
+        }
+
+        /// <summary>
+        /// 查询父级名称
+        /// </summary>
+        /// <param name="Id"></param>
+        /// <returns></returns>
+        private async Task<List<string>> GetParentNode(string? Id)
+        {
+            List<string> list = new();
+            //查询父级数据
+            var type = await _CaseTypeRepository.GetAsync(p => p.Id == Id);
+            if (type != null)
+            {
+                //添加名称
+                list.Add(type.Name);
+                list.AddRange(await GetParentNode(type.ParentId));
+            }
+            return list;
+        }
+
+        #endregion
+
+        #region 修改子级分类全称
+
+        /// <summary>
+        /// 修改子级分类全称
+        /// </summary>
+        /// <param name="Id"></param>
+        /// <returns></returns>
+        private async Task UpdateChildNode(string Id)
+        {
+            //查询子分类
+            var list = await GetChildNode(Id);
+            if (list is not null && list.Count > 0)
+            {
+                foreach (var item in list)
+                {
+                    //获取全称
+                    string FullName = await GetFullName(item.ParentId);
+                    item.SpliceName = string.IsNullOrEmpty(FullName) ? item.Name : FullName + "-" + item.Name;
+                    //修改全称
+                    await _CaseTypeRepository.UpdateAsync(item);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 查询子级节点数据
+        /// </summary>
+        /// <param name="Id"></param>
+        /// <returns></returns>
+        private async Task<List<CaseType>> GetChildNode(string Id)
+        {
+            List<CaseType> list = new();
+            //查询数据
+            var typelist = await _CaseTypeRepository.QueryAsync(p => p.ParentId == Id);
+            if (typelist != null)
+            {
+                //处理数据
+                foreach (var item in typelist)
+                {
+                    list.Add(item);
+                    list.AddRange(await GetChildNode(item.Id));
+                }
+            }
+            return list;
+        }
+
+        #endregion
+
+        #endregion
+
+    }
+}

+ 83 - 0
src/Hotline.Application/Caselibrary/ICaseApplication.cs

@@ -0,0 +1,83 @@
+using Hotline.Share.Dtos.Caselibrary;
+
+namespace Hotline.Application.Caselibrary
+{
+    public interface ICaseApplication
+    {
+
+        #region 案例库类型管理
+
+        /// <summary>
+        ///案例库类型 - 新增
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        Task<string> AddTypeAsync(AddCaseTypeDto dto, CancellationToken cancellationToken);
+
+        /// <summary>
+        ///案例库类型 - 编辑
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        Task UpdateTypeAsync(UpdateCaseTypeDto dto, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 案例库类型 - 删除
+        /// </summary>
+        /// <param name="Id"></param>
+        /// <returns></returns>
+        Task RemoveTypeAsync(string Id, CancellationToken cancellationToken);
+
+        #endregion
+
+        #region 案例库管理
+
+        /// <summary>
+        /// 案例库 - 列表
+        /// </summary>
+        /// <param name="pagedDto"></param>
+        /// <returns></returns>
+        Task<(int, IList<CaseDataDto>)> QueryAllCaseListAsync(CaseListDto pagedDto, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 案例库 - 新增
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task<string> AddCaseAsync(AddCaseListDto dto, CancellationToken cancellationToken);
+
+        /// <summary>
+        ///案例库类型 - 编辑
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        Task UpdateCaseAsync(UpdateCaseListDto dto, CancellationToken cancellationToken);
+
+        /// <summary>
+        ///案例库类型 - 下架审核
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        Task AuditCaseAsync(UpdateCaseListDto dto, CancellationToken cancellationToken);
+
+        /// <summary>
+        ///案例库类型 - 详情
+        /// </summary>
+        /// <param name="Id"></param>
+        /// <param name="IsAddPv">默认不增加,false不增加,true增加浏览量</param>
+        /// <returns></returns>
+        Task<CaseInfoDto> GetCaseAsync(string Id, bool? IsAddPv, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 案例库类型 - 批量导出
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task<Dictionary<string, Stream>> CaseInfoListExportAsync(CaseInfoExportDto dto, CancellationToken cancellationToken);
+
+        #endregion
+
+    }
+}

+ 3 - 6
src/Hotline.Application/Planlibrary/PlanApplication.cs

@@ -14,10 +14,7 @@ using Hotline.File;
 using Hotline.Application.Bulletin;
 using Hotline.Share.Tools;
 using Hotline.Application.Tools;
-using Hotline.KnowledgeBase;
-using NPOI.SS.Formula.Functions;
 using SqlSugar;
-using Senparc.CO2NET.Extensions;
 
 namespace Hotline.Application.Planlibrary
 {
@@ -157,7 +154,7 @@ namespace Hotline.Application.Planlibrary
             if (checkChild > 0)
                 throw UserFriendlyException.SameMessage("存在子级分类!");
 
-            //查询是否有知识分类
+            //查询是否有预案分类
             var checkKnowledge = await _planListRepository.CountAsync(p => p.PlanTypes.Any(t => t.PlanId == Id), cancellationToken);
             if (checkKnowledge > 0)
                 throw UserFriendlyException.SameMessage("分类存在预案!");
@@ -264,7 +261,7 @@ namespace Hotline.Application.Planlibrary
 
             var any = await _planListRepository.Queryable().Where(x => x.Status == EPlanStatus.OnShelf && x.Title == dto.Title).AnyAsync();
             if (any)
-                throw UserFriendlyException.SameMessage("当前知识标题存在重复标题!");
+                throw UserFriendlyException.SameMessage("当前预案标题存在重复标题!");
 
             if (dto.Files != null && dto.Files.Count > 0)
                 pList.FileJson = await _fileRepository.AddFileAsync(dto.Files, pList.Id, "", cancellationToken);
@@ -302,7 +299,7 @@ namespace Hotline.Application.Planlibrary
 
             var any = await _planListRepository.Queryable().Where(x => x.Status == EPlanStatus.OnShelf && x.Title == dto.Title && x.Id != dto.Id).AnyAsync();
             if (any)
-                throw UserFriendlyException.SameMessage("当前知识标题存在重复标题!");
+                throw UserFriendlyException.SameMessage("当前预案标题存在重复标题!");
 
             _mapper.Map(dto, plan);
 

+ 185 - 0
src/Hotline.Share/Dtos/Caselibrary/CaseDataDto.cs

@@ -0,0 +1,185 @@
+using Hotline.Share.Dtos.Hotspots;
+using Hotline.Share.Enums.Article;
+using Hotline.Share.Enums.Caselibrary;
+using XF.Utility.EnumExtensions;
+
+namespace Hotline.Share.Dtos.Caselibrary
+{
+    public record CaseDataDto
+    {
+        /// <summary>
+        /// 案例ID
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 标题
+        /// </summary>
+        public string Title { get; set; }
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        public string CreatorName { get; set; }
+
+        /// <summary>
+        /// 浏览量
+        /// </summary>
+        public int PageView { get; set; }
+
+        /// <summary>
+        /// 是否公开
+        /// </summary>
+        public bool IsPublic { get; set; }
+
+        /// <summary>
+        /// 申请部门ID
+        /// </summary>
+        public string CreatorOrgId { get; set; }
+
+        /// <summary>
+        /// 申请部门
+        /// </summary>
+        public string CreatorOrgName { get; set; }
+
+        /// <summary>
+        /// 申请时间
+        /// </summary>
+        public DateTime CreationTime { get; set; }
+
+        /// <summary>
+        /// 上架时间
+        /// </summary>
+        public DateTime? OnShelfTime { get; set; }
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        public DateTime? UpdateTime { get; set; }
+
+        /// <summary>
+        /// 审核时间
+        /// </summary>
+        public DateTime? ExaminTime { get; set; }
+
+        /// <summary>
+        /// 下架时间
+        /// </summary>
+        public DateTime? OffShelfTime { get; set; }
+
+        /// <summary>
+        /// 过期时间
+        /// </summary>
+        public DateTime? ExpiredTime { get; set; }
+
+        /// <summary>
+        /// 案例归属
+        /// </summary>
+        public string? Attribution { get; set; }
+
+        /// <summary>
+        /// 文档状态
+        /// </summary>
+        public ECaseStatus Status { get; set; }
+
+        public ECaseStatus NewStatus => Status != ECaseStatus.Drafts && DateTime.Now > ExpiredTime ? ECaseStatus.Overdue : Status;
+
+        /// <summary>
+        /// 文档状态名称
+        /// </summary>
+        public string StatusName => NewStatus.GetDescription();
+
+        /// <summary>
+        /// 案例分类
+        /// </summary>
+        public List<CaseRelationTypeDto> CaseTypes { get; set; }
+
+        /// <summary>
+        /// 案例分类名称
+        /// </summary>
+        public string CaseTypeText => GetCaseTypeText(CaseTypes);
+
+        /// <summary>
+        /// 获取案例分类名称
+        /// </summary>
+        /// <returns></returns>
+        public string GetCaseTypeText(List<CaseRelationTypeDto> items)
+        {
+
+            if (CaseTypes != null && CaseTypes.Any())
+            {
+                var names = CaseTypes.Select(x => x.CaseTypeName).ToList();
+                return string.Join(",", names);
+            }
+            return "";
+        }
+
+        public HotspotDto HotspotType { get; set; }
+
+        /// <summary>
+        /// 热点
+        /// </summary>
+        public string HotspotName => HotspotType != null ? HotspotType.HotSpotFullName : string.Empty;
+
+    }
+
+    public record CaseRelationTypeDto
+    {
+        /// <summary>
+        /// 案例库类型ID
+        /// </summary>
+        public string CaseTypeId { get; set; }
+
+        /// <summary>
+        /// 案例库类型名称
+        /// </summary>
+        public string CaseTypeName { get; set; }
+
+
+        /// <summary>
+        /// 案例库类型名称
+        /// </summary>
+        public string CaseTypeSpliceName { get; set; }
+    }
+}
+
+
+public class CaseExistDto
+{
+    /// <summary>
+    /// 标题
+    /// </summary>
+    public string Title { get; set; }
+
+    /// <summary>
+    /// 内容
+    /// </summary>
+    public string Content { get; set; }
+
+    /// <summary>
+    /// ID
+    /// </summary>
+    public string? Id { get; set; }
+}
+
+
+public class CaseInfoExportDto
+{
+    /// <summary>
+    /// 导出的案例Id集合
+    /// </summary>
+    public string[] Ids { get; set; }
+
+    /// <summary>
+    /// 导出格式
+    /// </summary>
+    public EFileType FileType { get; set; }
+}
+
+
+public class CasePageViewDto
+{
+    public string Id { get; set; }
+    public string Title { get; set; }
+    public int PageView { get; set; }
+}

+ 347 - 0
src/Hotline.Share/Dtos/Caselibrary/CaseListDto.cs

@@ -0,0 +1,347 @@
+using Hotline.Share.Dtos.File;
+using Hotline.Share.Enums.Caselibrary;
+using Hotline.Share.Requests;
+
+namespace Hotline.Share.Dtos.Caselibrary
+{
+    /// <summary>
+    /// 案例库列表
+    /// </summary>
+    public record CaseListDto : PagedRequest
+    {
+        /// <summary>
+        /// 标题
+        /// </summary>
+        public string Title { get; set; }
+
+        /// <summary>
+        /// 关键词
+        /// </summary>
+        public string? Keyword { get; set; }
+
+        /// <summary>
+        /// 内容
+        /// </summary>
+        public string Content { get; set; }
+
+
+        /// <summary>
+        /// 案例状态
+        /// </summary>
+        public ECaseStatus? Status { get; set; }
+
+        /// <summary>
+        /// 案例归属
+        /// </summary>
+        public string? Attribution { get; set; }
+
+        /// <summary>
+        /// 分类ID
+        /// </summary>
+        public string? CaseTypeID { get; set; }
+
+        /// <summary>
+        /// 热点
+        /// </summary>
+        public string? HotspotId { get; set; }
+
+        /// <summary>
+        /// 是否公开
+        /// </summary>
+        public bool? IsPublic { get; set; }
+
+        /// <summary>
+        /// 是否删除
+        /// </summary>
+        public bool? IsDeleted { get; set; } = false;
+
+        /// <summary>
+        /// 阅读次数
+        /// </summary>
+        public int PageView { get; set; } = 0;
+
+        /// <summary>
+        /// 创建开始时间
+        /// </summary>
+        public DateTime? CreationTimeStart { get; set; }
+
+        /// <summary>
+        /// 创建结束时间
+        /// </summary>
+        public DateTime? CreationTimeEnd { get; set; }
+
+        /// <summary>
+        /// 上架开始时间
+        /// </summary>
+        public DateTime? OnShelfTimeStart { get; set; }
+        /// <summary>
+        /// 上架结束时间
+        /// </summary>
+        public DateTime? OnShelfTimeEnd { get; set; }
+
+        /// <summary>
+        /// 下架开始时间
+        /// </summary>
+        public DateTime? OffShelfTimeStart { get; set; }
+
+        /// <summary>
+        /// 下架结束时间
+        /// </summary>
+        public DateTime? OffShelfTimeEnd { get; set; }
+
+        /// <summary>
+        /// 更新开始时间
+        /// </summary>
+        public DateTime? UpdateTimeStart { get; set; }
+
+        /// <summary>
+        /// 更新结束时间
+        /// </summary>
+        public DateTime? UpdateTimeEnd { get; set; }
+
+        /// <summary>
+        /// 审核人ID
+        /// </summary>
+        public string? ExaminManId { get; set; }
+
+        /// <summary>
+        /// 审核部门ID
+        /// </summary>
+        public string? ExaminOrganizeId { get; set; }
+
+        /// <summary>
+        /// 当前部门ID
+        /// </summary>
+        public string CreateOrgId { get; set; }
+
+        /// <summary>
+        /// 审核开始时间
+        /// </summary>
+        public DateTime? ExaminTimeStart { get; set; }
+
+        /// <summary>
+        /// 审核结束时间
+        /// </summary>
+        public DateTime? ExaminTimeEnd { get; set; }
+
+        /// <summary>
+        /// 失效时间
+        /// </summary>
+        public DateTime? ExpiredTime { get; set; }
+
+        /// <summary>
+        /// 评分
+        /// </summary>
+        public decimal? Score { get; set; } = decimal.Zero;
+
+        /// <summary>
+        /// 排序(需要被排序的字段名)
+        /// </summary>
+        public string? SortField { get; set; }
+
+        /// <summary>
+        /// 排序方向 (0: 升序Asc; 1: 降序Desc)
+        /// </summary>
+        public int OrderByType { get; set; } = 1;
+    }
+
+
+    public record AddCaseListDto
+    {
+        /// <summary>
+        /// 标题
+        /// </summary>
+        public string Title { get; set; }
+
+        /// <summary>
+        /// 关键词
+        /// </summary>
+        public string? Keyword { get; set; }
+
+        /// <summary>
+        /// 内容
+        /// </summary>
+        public string Content { get; set; }
+
+        /// <summary>
+        /// 案例归属
+        /// </summary>
+        public string Attribution { get; set; }
+
+        ///// <summary>
+        ///// 分类ID
+        ///// </summary>
+        //public string? CaseTypeID { get; set; }
+
+        /// <summary>
+        /// 分类
+        /// </summary>
+        public List<CaseRelationTypeDto> CaseType { get; set; }
+
+        /// <summary>
+        /// 热点
+        /// </summary>
+        public string? HotspotId { get; set; }
+
+        /// <summary>
+        /// 是否公开
+        /// </summary>
+        public bool? IsPublic { get; set; }
+
+        /// <summary>
+        /// 失效时间
+        /// </summary>
+        public DateTime? ExpiredTime { get; set; }
+
+        /// <summary>
+        /// 案例状态
+        /// </summary>
+        public ECaseApplyStatus? ApplyStatus { get; set; }
+
+        /// <summary>
+        /// 案例状态
+        /// </summary>
+        public ECaseStatus? Status { get; set; }
+
+        /// <summary>
+        /// 上传附件
+        /// </summary>
+        public List<FileDto>? Files { get; set; }
+    }
+
+    public record UpdateCaseListDto : AddCaseListDto
+    {
+        /// <summary>
+        /// 案例库ID
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 审核人ID
+        /// </summary>
+        public string? ExaminManId { get; set; }
+
+        /// <summary>
+        /// 审核部门ID
+        /// </summary>
+        public string? ExaminOrganizeId { get; set; }
+
+        /// <summary>
+        /// 审核时间
+        /// </summary>
+        public DateTime? ExaminTime { get; set; }
+
+        /// <summary>
+        /// 修改时间
+        /// </summary>
+        public DateTime? UpdateTime { get; set; }
+    }
+
+    public record DelCaseListDto
+    {
+        /// <summary>
+        /// 案例库ID
+        /// </summary>
+        public string Id { get; set; }
+    }
+
+
+    /// <summary>
+    /// 案例库列表
+    /// </summary>
+    public record CaseInfoDto
+    {
+        /// <summary>
+        /// 标识
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 标题
+        /// </summary>
+        public string Title { get; set; }
+
+        /// <summary>
+        /// 关键词
+        /// </summary>
+        public string? Keyword { get; set; }
+
+        /// <summary>
+        /// 内容
+        /// </summary>
+        public string Content { get; set; }
+
+        /// <summary>
+        /// 是否公开
+        /// </summary>
+        public bool? IsPublic { get; set; }
+
+        /// <summary>
+        /// 阅读次数
+        /// </summary>
+        public int PageView { get; set; } = 0;
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        public DateTime? CreationTime { get; set; }
+
+        /// <summary>
+        /// 失效时间
+        /// </summary>
+        public DateTime? ExpiredTime { get; set; }
+
+        /// <summary>
+        /// 评分
+        /// </summary>
+        public decimal? Score { get; set; } = decimal.Zero;
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        public string? CreatorName { get; set; }
+
+        /// <summary>
+        /// 创建部门
+        /// </summary>
+        public string? CreatorOrgName { get; set; }
+
+        public List<FileDto> Files { get; set; }
+
+        /// <summary>
+        /// 附件
+        /// </summary>
+        public List<FileJson>? FileJson { get; set; }
+    }
+
+    public record AuditCaseListDto
+    {
+        /// <summary>
+        /// 案例库ID
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 审核状态 0不同意 1同意
+        /// </summary>
+        public int State { get; set; }
+    }
+
+    public record PvCaseListDto
+    {
+        /// <summary>
+        /// 案例库ID
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 浏览量:默认不增加,false不增加,true增加浏览量
+        /// </summary>
+        public bool IsAddPv { get; set; } = false;
+
+        /// <summary>
+        /// 评分
+        /// </summary>
+        public decimal Score { get; set; }
+    }
+}

+ 189 - 0
src/Hotline.Share/Dtos/Caselibrary/CaseTypeDto.cs

@@ -0,0 +1,189 @@
+using Hotline.Settings;
+using XF.Utility.EnumExtensions;
+
+namespace Hotline.Share.Dtos.Caselibrary
+{
+    /// <summary>
+    /// 编辑
+    /// </summary>
+    public record UpdateCaseTypeDto : AddCaseTypeDto
+    {
+        /// <summary>
+        /// ID
+        /// </summary>
+        public string Id { get; set; }
+    }
+
+    /// <summary>
+    ///新增
+    /// </summary>
+    public record AddCaseTypeDto
+    {
+        /// <summary>
+        /// 类型名称
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 父级ID
+        /// </summary>
+        public string? ParentId { get; set; } 
+
+        /// <summary>
+        /// 排序
+        /// </summary>
+        public int Sort { get; set; } = 0;
+
+        public List<AddCaseTypeOrgDto>? TypeOrgDtos { get; set; }
+
+    }
+
+    /// <summary>
+    /// 初始化返回数据
+    /// </summary>
+    public record CaseTypeDto
+    {
+        /// <summary>
+        /// ID
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 类型名称拼接(所有父级分类名称)
+        /// <example>
+        /// A类型/A.1类型/A.1.1类型
+        /// </example>
+        /// </summary>
+        public string SpliceName { get; set; }
+
+		/// <summary>
+		/// 类型名称
+		/// </summary>
+		public string Name { get; set; }
+
+        /// <summary>
+        /// 是否启用
+        /// </summary>
+        public bool IsEnable { get; set; }
+
+        /// <summary>
+        /// 排序
+        /// </summary>
+        public int Sort { get; set; }
+
+        /// <summary>
+        /// 父级ID
+        /// </summary>
+        public string ParentId { get; set; }
+
+        /// <summary>
+        /// 树形分类
+        /// </summary>
+        public List<Knowledge.TreeListDto> TreeLists { get; set; } = new();
+
+        /// <summary>
+        /// 子级
+        /// </summary>
+        public List<CaseTypeDto> children { get; set; }
+
+        public int CaseNum { get; set; }
+	}
+
+    public record CaseOrgDto
+    {
+	    /// <summary>
+	    /// ID
+	    /// </summary>
+	    public string Id { get; set; }
+
+		/// <summary>
+		/// 组织架构名称
+		/// </summary>
+		public string Name { get; set; }
+
+	    /// <summary>
+	    /// 组织架构简称
+	    /// </summary>
+	    public string ShortName { get; set; }
+
+	    /// <summary>
+	    /// 区域Code(行政区域代码)
+	    /// </summary>
+	    public string? AreaCode { get; set; }
+
+	    /// <summary>
+	    /// 区域名称(行政区域名称)
+	    /// </summary>
+	    public string? AreaName { get; set; }
+
+	    /// <summary>
+	    /// 部门级别
+	    /// </summary>
+	    public int Level { get; set; }
+
+	    /// <summary>
+	    /// 部门类型
+	    /// </summary>
+	    public EOrgType OrgType { get; set; }
+
+	    /// <summary>
+	    /// 上级ID
+	    /// </summary>
+	    public string? ParentId { get; set; }
+
+	    /// <summary>
+	    /// 上级名称
+	    /// </summary>
+	    public string? ParentName { get; set; }
+
+	    /// <summary>
+	    /// 是否启用
+	    /// </summary>
+	    public bool IsEnable { get; set; }
+
+	    /// <summary>
+	    /// 是否为中心
+	    /// </summary>
+	    public bool IsCenter { get; set; }
+
+	    public List<CaseOrgDto> Children { get; set; }
+
+	    public string OrgTypeText => OrgType.GetDescription();
+
+
+	    /// <summary>
+	    /// 旧系统id
+	    /// </summary>
+	    public int? oldBmid { get; set; }
+
+	    public int CaseNum { get; set; }
+	}
+
+    public record CaseHotSpotDto
+    {
+	    /// <summary>
+	    /// ID
+	    /// </summary>
+	    public string Id { get; set; }
+
+	    public int CaseNum { get; set; }
+
+		public string HotSpotName { get; set; }
+	    public string ParentId { get; set; }
+	    public string PYCode { get; set; }
+
+	    public string FullPYCode { get; set; }
+	    public string ProvinceCode { get; set; }
+	    public int OrderBy { get; set; }
+	    public string TrunkNum { get; set; }
+	    /// <summary>
+	    /// 生成的时候写入
+	    /// </summary>
+	    public string HotSpotFullName { get; set; }
+
+	    public List<CaseHotSpotDto> Children { get; set; }
+
+	    public bool HasChild { get; set; }
+
+	}
+}

+ 51 - 0
src/Hotline.Share/Dtos/Caselibrary/CaseTypeOrgDto.cs

@@ -0,0 +1,51 @@
+
+namespace Hotline.Share.Dtos.Caselibrary
+{
+	public record AddCaseTypeOrgDto
+	{
+		/// <summary>
+		/// 机构ID
+		/// </summary>
+		public string OrgId { get; set; }
+
+		/// <summary>
+		/// 机构名称
+		/// </summary>
+		public string OrgName { get; set; }
+	}
+
+	public record CaseTypeOrgDto : AddCaseTypeOrgDto
+	{
+		public DateTime? LastModificationTime { get; set; }
+
+		public bool IsDeleted { get; set; }
+
+		/// <summary>
+		/// 删除时间
+		/// </summary>
+		public DateTime? DeletionTime { get; set; }
+
+
+		/// <summary>
+		/// 创建时间
+		/// </summary>
+		public DateTime CreationTime { get; set; }
+
+		public string Id { get; set; }
+
+		/// <summary>
+		/// 组织Id
+		/// </summary>
+		public string? CreatorOrgId { get; set; }
+
+
+		public string? CreatorOrgName { get; set; }
+
+		/// <summary>
+		/// 创建人
+		/// </summary>
+		public string? CreatorId { get; set; }
+
+		public string? CreatorName { get; set; }
+	}
+}

+ 6 - 10
src/Hotline.Share/Dtos/Planlibrary/PlanDataDto.cs

@@ -1,9 +1,5 @@
-using Hotline.Share.Dtos.FlowEngine.Workflow;
-using Hotline.Share.Dtos.Hotspots;
-using Hotline.Share.Dtos.Order;
-using Hotline.Share.Dtos.Org;
+using Hotline.Share.Dtos.Hotspots;
 using Hotline.Share.Enums.Article;
-using Hotline.Share.Enums.KnowledgeBase;
 using Hotline.Share.Enums.Planlibrary;
 using XF.Utility.EnumExtensions;
 
@@ -12,7 +8,7 @@ namespace Hotline.Share.Dtos.Planlibrary
     public record PlanDataDto
     {
         /// <summary>
-        /// 知识ID
+        /// 预案ID
         /// </summary>
         public string Id { get; set; }
 
@@ -130,18 +126,18 @@ namespace Hotline.Share.Dtos.Planlibrary
     public record PlanRelationTypeDto
     {
         /// <summary>
-        /// 知识库类型ID
+        /// 预案库类型ID
         /// </summary>
         public string PlanTypeId { get; set; }
 
         /// <summary>
-        /// 知识库类型名称
+        /// 预案库类型名称
         /// </summary>
         public string PlanTypeName { get; set; }
 
 
         /// <summary>
-        /// 知识库类型名称
+        /// 预案库类型名称
         /// </summary>
         public string PlanTypeSpliceName { get; set; }
     }
@@ -170,7 +166,7 @@ public class PlanExistDto
 public class PlanInfoExportDto
 {
     /// <summary>
-    /// 导入的知识Id集合
+    /// 导入的预案Id集合
     /// </summary>
     public string[] Ids { get; set; }
 

+ 2 - 9
src/Hotline.Share/Dtos/Planlibrary/PlanListDto.cs

@@ -1,18 +1,11 @@
 using Hotline.Share.Dtos.File;
-using Hotline.Share.Dtos.Knowledge;
-using Hotline.Share.Enums.KnowledgeBase;
 using Hotline.Share.Enums.Planlibrary;
 using Hotline.Share.Requests;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace Hotline.Share.Dtos.Planlibrary
 {
     /// <summary>
-    /// 知识库列表
+    /// 预案库列表
     /// </summary>
     public record PlanListDto : PagedRequest
     {
@@ -254,7 +247,7 @@ namespace Hotline.Share.Dtos.Planlibrary
 
 
     /// <summary>
-    /// 知识库列表
+    /// 预案库列表
     /// </summary>
     public record PlanInfoDto
     {

+ 1 - 6
src/Hotline.Share/Dtos/Planlibrary/PlanTypeOrgDto.cs

@@ -1,9 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
+
 namespace Hotline.Share.Dtos.Planlibrary
 {
 	public record AddPlanTypeOrgDto

+ 33 - 0
src/Hotline.Share/Enums/Caselibrary/ECaseApplyStatus.cs

@@ -0,0 +1,33 @@
+using System.ComponentModel;
+
+namespace Hotline.Share.Enums.Caselibrary;
+
+/// <summary>
+/// 案例审批-申请类型
+/// </summary>
+public enum ECaseApplyStatus
+{
+    /// <summary>
+    /// 新增
+    /// </summary>
+    [Description("新增")]
+    Add = 0,
+
+    /// <summary>
+    /// 修改
+    /// </summary>
+    [Description("修改")]
+    Update = 1,
+
+    /// <summary>
+    /// 删除
+    /// </summary>
+    [Description("删除")]
+    Delete = 2,
+
+	/// <summary>
+	/// 下架
+	/// </summary>
+	[Description("下架")]
+    Offshelf = 3,
+}

+ 64 - 0
src/Hotline.Share/Enums/Caselibrary/ECaseStatus.cs

@@ -0,0 +1,64 @@
+using System.ComponentModel;
+
+namespace Hotline.Share.Enums.Caselibrary
+{
+    /// <summary>
+    /// 案例状态
+    /// </summary>
+    public enum ECaseStatus
+    {
+        /// <summary>
+        /// 全部
+        /// </summary>
+        [Description("全部")]
+        All = -1,
+
+        /// <summary>
+        /// 待提交
+        /// </summary>
+        [Description("待提交")]
+        Drafts = 0,
+
+        /// <summary>
+        /// 审批中
+        /// </summary>
+        [Description("审批中")]
+        Auditing = 1,
+
+        /// <summary>
+        /// 失败
+        /// </summary>
+        [Description("失败")]
+        Failed = 2,
+
+        /// <summary>
+        /// 已上架
+        /// </summary>
+        [Description("已上架")]
+        OnShelf = 3,
+
+        /// <summary>
+        /// 已下架
+        /// </summary>
+        [Description("已下架")]
+        OffShelf = 4,
+
+        /// <summary>
+        /// 已驳回
+        /// </summary>
+        [Description("已驳回")]
+        Revert = 5,
+
+        /// <summary>
+        /// 已过期
+        /// </summary>
+        [Description("已过期")]
+        Overdue = 6,
+
+        /// <summary>
+        /// 草稿
+        /// </summary>
+        [Description("草稿")]
+        NewDrafts = 7
+    }
+}

+ 1 - 1
src/Hotline.Share/Enums/Planlibrary/EPlanApplyStatus.cs

@@ -3,7 +3,7 @@
 namespace Hotline.Share.Enums.Planlibrary;
 
 /// <summary>
-/// 知识审批-申请类型
+/// 预案审批-申请类型
 /// </summary>
 public enum EPlanApplyStatus
 {

+ 1 - 6
src/Hotline.Share/Enums/Planlibrary/EPlanStatus.cs

@@ -1,9 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.ComponentModel;
 
 namespace Hotline.Share.Enums.Planlibrary
 {

+ 34 - 0
src/Hotline/CaseLibrary/CaseCollect.cs

@@ -0,0 +1,34 @@
+using SqlSugar;
+using System.ComponentModel;
+using XF.Domain.Repository;
+
+namespace Hotline.CaseLibrary
+{
+    [Description("案例库收藏")]
+    public class CaseCollect : FullStateEntity
+    {
+        /// <summary>
+        /// 案例库ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "案例库ID")]
+        public string CaseId { get; set; }
+
+        /// <summary>
+        /// 案例库
+        /// </summary>
+        [Navigate(NavigateType.OneToOne, nameof(CaseId))]
+        public CaseList Case { get; set; }
+
+        /// <summary>
+        /// 评分
+        /// </summary>
+        [SugarColumn(ColumnDescription = "评分")]
+        public decimal? Score { get; set; } = decimal.Zero;
+
+        /// <summary>
+        /// 是否收藏
+        /// </summary>
+        [SugarColumn(ColumnDescription = "是否收藏")]
+        public bool? Collect { get; set; }
+    }
+}

+ 168 - 0
src/Hotline/CaseLibrary/CaseList.cs

@@ -0,0 +1,168 @@
+using Hotline.Settings;
+using Hotline.Settings.Hotspots;
+using Hotline.Users;
+using SqlSugar;
+using System.ComponentModel;
+using XF.Domain.Repository;
+using Hotline.Share.Enums.Caselibrary;
+using Hotline.Share.Dtos.File;
+
+namespace Hotline.CaseLibrary;
+
+/// <summary>
+/// 案例库列表内容
+/// </summary>
+[Description("案例库列表")]
+[SugarIndex("index_caselibrary_hotspotId", nameof(CaseList.HotspotId), OrderByType.Desc)]
+[SugarIndex("index_caselibrary_attribution", nameof(CaseList.Attribution), OrderByType.Desc)]
+[SugarIndex("index_caselibrary_creatorOrgId", nameof(CaseList.CreatorOrgId), OrderByType.Desc)]
+public class CaseList : CreationEntity
+{
+    /// <summary>
+    /// 标题
+    /// </summary>
+    [SugarColumn(ColumnDescription = "标题")]
+    public string Title { get; set; }
+
+    /// <summary>
+    /// 关键词
+    /// </summary>
+    [SugarColumn(ColumnDataType = "json", IsJson = true, IsNullable = true, ColumnDescription = "关键词")]
+    public List<string>? Keywords { get; set; }
+
+    /// <summary>
+    /// 内容
+    /// </summary>
+    [SugarColumn(ColumnDataType = "text", ColumnDescription = "内容")]
+    public string Content { get; set; }
+
+    /// <summary>
+    /// 案例申请状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "案例申请状态")]
+    public ECaseApplyStatus ApplyStatus { get; set; }
+
+    /// <summary>
+    /// 案例状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "案例状态")]
+    public ECaseStatus Status { get; set; }
+
+    /// <summary>
+    /// 案例归属
+    /// </summary>
+    [SugarColumn(ColumnDescription = "案例归属")]
+    public string Attribution { get; set; }
+
+    /// <summary>
+    /// 热点
+    /// </summary>
+    [SugarColumn(ColumnDescription = "热点")]
+    public string? HotspotId { get; set; }
+
+    /// <summary>
+    /// 热点
+    /// </summary>
+    [Navigate(NavigateType.OneToOne, nameof(HotspotId))]//一对一 
+    public Hotspot? HotspotType { get; set; }
+
+    /// <summary>
+    /// 是否公开
+    /// </summary>
+    [SugarColumn(ColumnDescription = "是否公开")]
+    public bool IsPublic { get; set; }
+
+    /// <summary>
+    /// 是否删除
+    /// </summary>
+    [SugarColumn(ColumnDescription = "是否删除")]
+    public bool? IsDeleted { get; set; } = false;
+
+    /// <summary>
+    /// 阅读次数
+    /// </summary>
+    [SugarColumn(ColumnDescription = "阅读次数")]
+    public int PageView { get; set; } = 0;
+
+    /// <summary>
+    /// 上架时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "上架时间")]
+    public DateTime? OnShelfTime { get; set; }
+
+    /// <summary>
+    /// 下架时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "下架时间")]
+    public DateTime? OffShelfTime { get; set; }
+
+    /// <summary>
+    /// 创建人
+    /// </summary>
+    [Navigate(NavigateType.OneToOne, nameof(CreatorId))]
+    public User? Creator { get; set; }
+
+    /// <summary>
+    /// 创建部门
+    /// </summary>
+    [Navigate(NavigateType.OneToOne, nameof(CreatorOrgId))]//一对一 
+    public SystemOrganize? SystemOrganize { get; set; }
+
+    /// <summary>
+    /// 更新时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "更新时间")]
+    public DateTime? UpdateTime { get; set; }
+
+    /// <summary>
+    /// 审核人ID
+    /// </summary>
+    public string? ExaminManId { get; set; }
+
+    /// <summary>
+    /// 审核人
+    /// </summary>
+    [Navigate(NavigateType.OneToOne, nameof(ExaminManId))]
+    public User? ExaminMan { get; set; }
+
+    /// <summary>
+    /// 审核部门ID
+    /// </summary>
+    [SugarColumn(ColumnDescription = "审核部门")]
+    public string? ExaminOrganizeId { get; set; }
+
+    /// <summary>
+    /// 审核部门
+    /// </summary>
+    [Navigate(NavigateType.OneToOne, nameof(ExaminOrganizeId))]//一对一 
+    public SystemOrganize? ExaminOrganize { get; set; }
+
+    /// <summary>
+    /// 审核时间
+    /// </summary>
+    public DateTime? ExaminTime { get; set; }
+
+    /// <summary>
+    /// 失效时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "失效时间")]
+    public DateTime? ExpiredTime { get; set; }
+
+    /// <summary>
+    /// 评分
+    /// </summary>
+    [SugarColumn(ColumnDescription = "评分")]
+    public decimal? Score { get; set; } = decimal.Zero;
+
+    /// <summary>
+	/// 案例库类型关联ID
+	/// </summary>
+	[Navigate(NavigateType.OneToMany, nameof(CaseRelationType.CaseId))]
+    public List<CaseRelationType> CaseTypes { get; set; }
+
+    /// <summary>
+    /// 附件
+    /// </summary>
+    [SugarColumn(ColumnDataType = "json", IsJson = true, IsNullable = true)]
+    public List<FileJson>? FileJson { get; set; }
+}

+ 42 - 0
src/Hotline/CaseLibrary/CaseRelationType.cs

@@ -0,0 +1,42 @@
+using SqlSugar;
+using System.ComponentModel;
+using XF.Domain.Repository;
+
+namespace Hotline.CaseLibrary;
+
+/// <summary>
+/// 案例库关联类型
+/// </summary>
+[Description("案例库关联类型")]
+[SugarIndex("index_caseRelationType_caseId", nameof(CaseRelationType.CaseId), OrderByType.Desc)]
+[SugarIndex("index_caseRelationType_typeId", nameof(CaseRelationType.CaseTypeId), OrderByType.Desc)]
+[SugarIndex("index_caseRelationType_spliceName", nameof(CaseRelationType.CaseTypeSpliceName), OrderByType.Desc)]
+public class CaseRelationType : FullStateEntity
+{
+    /// <summary>
+    /// 案例库ID
+    /// </summary>
+    public string CaseId { get; set; }
+
+    /// <summary>
+    /// 案例库类型ID
+    /// </summary>
+    public string CaseTypeId { get; set; }
+
+    /// <summary>
+    /// 案例库类型名称
+    /// </summary>
+    public string CaseTypeName { get; set; }
+
+    /// <summary>
+    /// 案例库类型名称
+    /// </summary>
+    public string CaseTypeSpliceName { get; set; }
+
+    /// <summary>
+    /// 案例库类型
+    /// </summary>
+    [Navigate(NavigateType.OneToOne, nameof(CaseTypeId))]
+    public CaseType CaseType { get; set; }
+}
+

+ 53 - 0
src/Hotline/CaseLibrary/CaseType.cs

@@ -0,0 +1,53 @@
+using SqlSugar;
+using System.ComponentModel;
+using XF.Domain.Repository;
+
+namespace Hotline.CaseLibrary;
+
+/// <summary>
+/// 案例分类
+/// </summary>
+[Description("案例分类")]
+public class CaseType : FullStateEntity
+{
+    /// <summary>
+    /// 类型名称
+    /// </summary>
+    public string Name { get; set; }
+
+    /// <summary>
+    /// 类型名称拼接(所有父级分类名称)
+    /// <example>
+    /// A类型/A.1类型/A.1.1类型
+    /// </example>
+    /// </summary>
+    public string SpliceName { get; set; }
+
+    /// <summary>
+    /// 是否启用
+    /// </summary>
+    public bool IsEnable { get; set; }
+
+    /// <summary>
+    /// 排序
+    /// </summary>
+    public int Sort { get; set; }
+
+    /// <summary>
+    /// 父级ID
+    /// </summary>
+    [SugarColumn(IsNullable = true)]
+    public string? ParentId { get; set; }
+
+
+    /// <summary>
+    /// 旧系统类型id
+    /// </summary>
+    public int? OldTypeId { get; set; }
+
+    /// <summary>
+    /// 类型关联机构
+    /// </summary>
+    [Navigate(NavigateType.OneToMany, nameof(CaseTypeOrg.TypeId))]
+    public List<CaseTypeOrg> CaseTypeOrgs { get; set; }
+}

+ 30 - 0
src/Hotline/CaseLibrary/CaseTypeOrg.cs

@@ -0,0 +1,30 @@
+using SqlSugar;
+using System.ComponentModel;
+using XF.Domain.Repository;
+
+namespace Hotline.CaseLibrary;
+
+/// <summary>
+/// 案例分类关联机构
+/// </summary>
+[Description("案例分类关联机构")]
+[SugarIndex("index_caseTypeOrg_typeId", nameof(CaseTypeOrg.TypeId), OrderByType.Desc)]
+[SugarIndex("index_caseTypeOrg_orgId", nameof(CaseTypeOrg.OrgId), OrderByType.Desc)]
+public class CaseTypeOrg : FullStateEntity
+{
+    /// <summary>
+    /// 案例分类ID
+    /// </summary>
+    public string TypeId { get; set; }
+
+    /// <summary>
+    /// 机构ID
+    /// </summary>
+    public string OrgId { get; set; }
+
+    /// <summary>
+    /// 机构名称
+    /// </summary>
+    public string OrgName { get; set; }
+}
+

+ 1 - 7
src/Hotline/Planlibrary/PlanCollect.cs

@@ -1,11 +1,5 @@
-using Hotline.KnowledgeBase;
-using SqlSugar;
-using System;
-using System.Collections.Generic;
+using SqlSugar;
 using System.ComponentModel;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using XF.Domain.Repository;
 
 namespace Hotline.Planlibrary

+ 0 - 1
src/Hotline/Planlibrary/PlanList.cs

@@ -5,7 +5,6 @@ using SqlSugar;
 using System.ComponentModel;
 using XF.Domain.Repository;
 using Hotline.Share.Enums.Planlibrary;
-using Hotline.KnowledgeBase;
 using Hotline.Share.Dtos.File;
 
 namespace Hotline.Planlibrary;

+ 1 - 1
src/Hotline/Planlibrary/PlanTypeOrg.cs

@@ -7,7 +7,7 @@ namespace Hotline.Planlibrary;
 /// <summary>
 /// 预案分类关联机构
 /// </summary>
-[Description("知识分类关联机构")]
+[Description("预案分类关联机构")]
 [SugarIndex("index_planTypeOrg_typeId", nameof(PlanTypeOrg.TypeId), OrderByType.Desc)]
 [SugarIndex("index_planTypeOrg_orgId", nameof(PlanTypeOrg.OrgId), OrderByType.Desc)]
 public class PlanTypeOrg : FullStateEntity