tangjiang 11 ماه پیش
والد
کامیت
46ddab76d8

+ 919 - 0
src/Hotline.Api/Controllers/WebPortalController.cs

@@ -0,0 +1,919 @@
+using DotNetCore.CAP;
+using Hotline.Article;
+using Hotline.Orders;
+using Hotline.Settings.Hotspots;
+using Hotline.Share.Dtos.Push.FWMessage;
+using Hotline.Share.Dtos.Push;
+using Hotline.Share.Dtos.WebPortal;
+using Hotline.Share.Enums.Order;
+using Hotline.WebPortal;
+using MapsterMapper;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+using XF.Domain.Filters;
+using XF.Domain.Repository;
+using XF.Domain.Cache;
+using Hotline.Push.Notifies;
+using Hotline.Share.Enums.Push;
+using StackExchange.Redis;
+using MediatR;
+using XF.Domain.Constants;
+using Hotline.Caching.Interfaces;
+using Hotline.Application.Orders;
+using XF.Domain.Authentications;
+using Hotline.Share.Dtos.DataSharing.PusherHotlineDto;
+using Hotline.Share.Dtos.Order;
+using Org.BouncyCastle.Ocsp;
+using System.Threading;
+
+namespace Hotline.Api.Controllers
+{
+    public class WebPortalController : BaseController
+    {
+        private readonly IMapper _mapper;
+        private readonly IMediator _mediator;
+        private readonly IRepository<Bulletin> _bulletinRepository;
+        private readonly IRepository<WebUserRegister> _webUserRegisterRepository;
+        private readonly IRepository<WebUserAuth> _webUserAuthRepository;
+        private readonly IRepository<WebFlowAccept> _webFlowAcceptRepository;
+        private readonly ITypedCache<WriteLettersSendSmsDto> _writeLettersSendSms;
+        private readonly IRepository<Hotspot> _hotspotRepository;
+        private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+        private readonly IRepository<OrderPublish> _orderPublishRepository;
+        private readonly IOrderRepository _orderRepository;
+        private readonly IRepository<OrderVisit> _orderVisitRepository;
+        private readonly IOrderApplication _orderApplication;
+        private readonly ISessionContext _sessionContext;
+        private readonly IRepository<OrderVisitDetail> _orderVisitDetailRepository;
+
+
+        public WebPortalController(IMapper mapper,
+            IMediator mediator,
+            IRepository<Bulletin> bulletinRepository,
+             IRepository<WebUserRegister> webUserRegisterRepository,
+            IRepository<WebUserAuth> webUserAuthRepository,
+            IRepository<WebFlowAccept> webFlowAcceptRepository,
+           ITypedCache<WriteLettersSendSmsDto> writeLettersSendSms,
+           IRepository<Hotspot> hotspotRepository,
+           ISystemSettingCacheManager systemSettingCacheManager,
+           IRepository<OrderPublish> orderPublishRepository,
+           IOrderRepository orderRepository,
+           IRepository<OrderVisit> orderVisitRepository,
+           IOrderApplication orderApplication,
+           ISessionContext sessionContext,
+            IRepository<OrderVisitDetail> orderVisitDetailRepository
+            )
+        {
+            _mapper = mapper;
+            _mediator = mediator;
+            _bulletinRepository = bulletinRepository;
+            _webUserRegisterRepository = webUserRegisterRepository;
+            _webUserAuthRepository = webUserAuthRepository;
+            _webFlowAcceptRepository = webFlowAcceptRepository;
+            _writeLettersSendSms = writeLettersSendSms;
+            _hotspotRepository = hotspotRepository;
+            _systemSettingCacheManager = systemSettingCacheManager;
+            _orderPublishRepository = orderPublishRepository;
+            _orderRepository = orderRepository;
+            _orderVisitRepository = orderVisitRepository;
+            _orderApplication = orderApplication;
+            _sessionContext = sessionContext;
+            _orderVisitDetailRepository = orderVisitDetailRepository;
+        }
+
+        #region 通知
+        /// <summary>
+        /// 获取列表
+        /// </summary>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getarticlelist")]
+        public async Task<OpenResponse> GetArticleList([FromBody] QueryArticleListDto dto)
+        {
+            RefAsync<int> total = 0;
+            var items = await _bulletinRepository.Queryable()
+                .Where(p => p.LoseEfficacyTime >= DateTime.Now)
+                .Where(d => SqlFunc.JsonListObjectAny(d.PushRanges, "Key", "2"))
+                 .Where(p => p.BulletinTypeId == dto.NoticeType)
+                 .WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Title.Contains(dto.Condition))
+                 .OrderByDescending(p => p.BulletinTime)
+                 .Select(it => new
+                 {
+                     Page = SqlFunc.RowNumber($"{it.BulletinTime} desc "),// SqlFunc.MappingColumn(default(int), "  row_number()  over( order by 'NoticeCreateDate'  ) "),
+                     NoticeID = it.Id,
+                     NoticeContent = it.Content,
+                     NoticeTypeName = it.BulletinTypeName,
+                     NoticeTitle = it.Title,
+                     NoticeBMName = it.SourceOrgName,
+                     NoticeCreateDate = it.BulletinTime,
+                     VideoUrl = "",
+                     WNLT_FullCode = ""
+                 })
+                .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
+
+            //计算总页数
+            int nPageCount = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(total) / dto.PageSize));
+            ArticleListDataDto dataDto = new()
+            {
+                PageCount = nPageCount,
+                data = _mapper.Map<IReadOnlyList<ArticleListDto>>(items)
+            };
+
+            return OpenResponse.Ok(WebPortalDeResponse<ArticleListDataDto>.Success(dataDto));
+        }
+
+        /// <summary>
+        /// 获取通知公告前几条数据
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getarticlelistbynum")]
+        public async Task<OpenResponse> GetArticleListByNum([FromBody] ArticleIdByNumDto dto)
+        {
+            var items = await _bulletinRepository.Queryable()
+                .Where(p => p.LoseEfficacyTime >= DateTime.Now)
+                .Where(d => SqlFunc.JsonListObjectAny(d.PushRanges, "Key", "2"))
+                 .Where(p => p.BulletinTypeId == dto.BulletinTypeId)
+                 .WhereIF(!string.IsNullOrEmpty(dto.CheckChar), p => p.Content.Contains(dto.CheckChar))
+                 .OrderByDescending(p => p.BulletinTime)
+                 .Select(it => new
+                 {
+                     DataID = it.Id,
+                     it.Title,
+                     CreateDate = it.BulletinTime,
+                     it.Content
+                 })
+                 .Take(dto.Num)
+                .ToListAsync();
+
+            var data = _mapper.Map<IReadOnlyList<DataListTopDto>>(items);
+            return OpenResponse.Ok(WebPortalDeResponse<IReadOnlyList<DataListTopDto>>.Success(data, "成功"));
+        }
+
+        /// <summary>
+        /// 获取详情,修改阅读次数
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getarticledetails")]
+        public async Task<OpenResponse> GetArticleDetails([FromBody] ArticleIdDto dto)
+        {
+            var data = await _bulletinRepository.GetAsync(p => p.Id == dto.Id, HttpContext.RequestAborted);
+            ArticleDetailsDto detailsDto = null;
+            if (data != null)
+            {
+                data.ReadedNum = data.ReadedNum++;
+                await _bulletinRepository.UpdateAsync(data, HttpContext.RequestAborted);
+
+                detailsDto = new()
+                {
+                    NoticeID = data.Id,
+                    NoticeTypeName = data.BulletinTypeName,
+                    NoticeTitle = data.Title,
+                    NoticeBMName = data.SourceOrgName,
+                    NoticeCreateDate = data.BulletinTime,
+                    NoticeRCount = data.ReadedNum,
+                    WNED_VideoUrl = "",
+                    NoticeContent = data.Content
+                };
+            }
+            else
+                detailsDto = new();
+            List<ArticleDetailsDto> dataDto = new() { detailsDto };
+            return OpenResponse.Ok(WebPortalDeResponse<IReadOnlyList<ArticleDetailsDto>>.Success(dataDto));
+        }
+
+        /// <summary>
+        /// 上一条和下一条
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getpreviousandnext")]
+        public async Task<OpenResponse> GetPreviousAndNext([FromBody] ArticlePreviousAndNextDto dto)
+        {
+            var sugar = _bulletinRepository.Queryable()
+                .Where(p => p.LoseEfficacyTime >= DateTime.Now)
+                .Where(d => SqlFunc.JsonListObjectAny(d.PushRanges, "Key", "2"))
+                ;
+            if (dto.FullSearch == "1")//全文搜索
+            {
+                sugar.Where(p => p.BulletinTypeId == "1" || p.BulletinTypeId == "5" || p.BulletinTypeId == "6" || p.BulletinTypeId == "7" || p.BulletinTypeId == "3" || p.BulletinTypeId == "4")
+                    .WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Content.Contains(dto.Condition) || p.Title.Contains(dto.Condition));
+            }
+            else//指定分类
+            {
+                sugar.WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Title.Contains(dto.Condition))
+                    .Where(p => p.BulletinTypeId == dto.NoticeTypeId);
+            }
+
+            var list = await sugar.OrderByDescending(p => p.BulletinTime)
+                .Select(it => new
+                {
+                    Page = SqlFunc.RowNumber($"{it.BulletinTime} desc "),// SqlFunc.MappingColumn(default(int), "  row_number()  over( order by 'NoticeCreateDate'  ) "),
+                    NoticeID = it.Id,
+                    NoticeTitle = it.Title,
+                    NoticeCreateDate = it.BulletinTime
+                })
+                .ToListAsync();
+            //数据为空返回空数据
+            if (list == null || list.Count == 0)
+                return OpenResponse.Ok(WebPortalDeResponse<List<ArticlePreviousAndNextDataDto>>.Success(null));
+            else
+            {
+                var temp = list.Find(p => p.NoticeID == dto.ID);
+                if (temp != null)
+                {
+                    List<ArticlePreviousAndNextDataDto> returnDto = new();
+                    //上一条
+                    var pTemp = list.Find(p => p.Page == temp.Page - 1);
+                    if (pTemp != null)
+
+                        returnDto.Add(new()
+                        {
+                            NoticeID = pTemp.NoticeID,
+                            NoticeTitle = pTemp.NoticeTitle,
+                            pntype = "p"
+                        });
+
+                    //下一条
+                    var nTemp = list.Find(p => p.Page == temp.Page + 1);
+                    if (nTemp != null)
+                        returnDto.Add(new()
+                        {
+                            NoticeID = nTemp.NoticeID,
+                            NoticeTitle = nTemp.NoticeTitle,
+                            pntype = "n"
+                        });
+                    return OpenResponse.Ok(WebPortalDeResponse<List<ArticlePreviousAndNextDataDto>>.Success(returnDto));
+                }
+                else
+                    return OpenResponse.Ok(WebPortalDeResponse<List<ArticlePreviousAndNextDataDto>>.Success(null));
+            }
+        }
+
+        /// <summary>
+        /// 全文检索
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getfulltextsearchlist")]
+        public async Task<OpenResponse> GetFullTextSearchList([FromBody] QueryArticleListDto dto)
+        {
+            if (string.IsNullOrEmpty(dto.Condition))
+                return OpenResponse.Ok(WebPortalDeResponse<FullTextSearchListDataDto>.Success(null));
+
+            RefAsync<int> total = 0;
+            var items = await _bulletinRepository.Queryable()
+                .Where(p => p.LoseEfficacyTime >= DateTime.Now)
+                .Where(d => SqlFunc.JsonListObjectAny(d.PushRanges, "Key", "2"))
+                 .Where(p => p.BulletinTypeId == "1" || p.BulletinTypeId == "5" || p.BulletinTypeId == "6" || p.BulletinTypeId == "7" || p.BulletinTypeId == "3" || p.BulletinTypeId == "4")
+                 .WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Title.Contains(dto.Condition))
+                 .OrderByDescending(p => p.BulletinTime)
+                 .Select(it => new
+                 {
+                     Page = SqlFunc.RowNumber($"{it.BulletinTime} desc "),
+                     NoticeID = it.Id,
+                     Content = it.Content,
+                     NoticeTypeID = it.BulletinTypeId,
+                     NoticeTypeName = it.BulletinTypeName,
+                     NoticeTitle = it.Title,
+                     NoticeBMName = it.SourceOrgName,
+                     NoticeCreateDate = it.BulletinTime,
+                     TypeCode = "",
+                 })
+                .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
+
+            //计算总页数
+            int nPageCount = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(total) / dto.PageSize));
+            FullTextSearchListDataDto dataDto = new()
+            {
+                PageCount = nPageCount,
+                data = _mapper.Map<IReadOnlyList<FullTextSearchListDto>>(items)
+            };
+            return OpenResponse.Ok(WebPortalDeResponse<FullTextSearchListDataDto>.Success(dataDto));
+        }
+
+        #endregion
+
+        #region 用户
+        /// <summary>
+        /// 添加统一认证用户数据
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("adduserauth")]
+        public async Task<OpenResponse> AddUserAuth([FromBody] UserModelDto dto)
+        {
+            bool bResult = false;
+            // 唯一记录ID
+            string id = dto.id;
+            // 注册用户ID      
+            string webUserID = "";
+            // 统一认证用户注册ID
+            string wUAID = "";
+            //查询是否有注册用户
+            var dataUser = await _webUserAuthRepository.GetAsync(p => p.DataId == id, HttpContext.RequestAborted);
+            if (dataUser != null)
+            {
+                webUserID = dataUser.WebUserID;
+                wUAID = dataUser.Id;
+            }
+            //没有注册用户添加数据
+            if (string.IsNullOrEmpty(webUserID))
+            {
+                var userRegister = _mapper.Map<WebUserRegister>(dto);
+                userRegister.RegDate = DateTime.Now;
+                userRegister.LastDate = DateTime.Now;
+                webUserID = await _webUserRegisterRepository.AddAsync(userRegister, HttpContext.RequestAborted);
+            }
+
+            //统一认证数据
+            var userAuth = _mapper.Map<WebUserAuth>(dto);
+            if (!string.IsNullOrEmpty(wUAID))
+            {
+                //修改数据
+                userAuth.Id = wUAID;
+                await _webUserAuthRepository.UpdateAsync(userAuth, HttpContext.RequestAborted);
+                bResult = true;
+            }
+            else
+            {
+                //新增数据
+                userAuth.WebGUID = Guid.NewGuid().ToString();
+                userAuth.WebUserID = webUserID;
+                var addId = await _webUserAuthRepository.AddAsync(userAuth, HttpContext.RequestAborted);
+                if (!string.IsNullOrEmpty(addId))
+                    bResult = true;
+            }
+            //判断是否成功
+            if (bResult)
+                return OpenResponse.Ok(WebPortalDeResponse<string>.Success(webUserID));
+            else
+                return OpenResponse.Ok(WebPortalDeResponse<string>.Failed());
+        }
+
+        /// <summary>
+        /// 用户中心用户写信数据
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("getorderbyuserlist")]
+        [AllowAnonymous]
+        public async Task<OpenResponse> GetOrderByUserList([FromBody] QueryOrderListByUserDto dto)
+        {
+            var dataUser = await _webUserAuthRepository.GetAsync(p => p.DataId == dto.UserId, HttpContext.RequestAborted);
+            if (dataUser != null)
+            {
+                RefAsync<int> total = 0;
+                var items = await _webFlowAcceptRepository.Queryable()
+                   .LeftJoin<Hotline.Orders.Order>((o, or) => o.OrderId == or.Id)
+                   .Where(o => o.WebUserID == dataUser.WebUserID)
+               //重新构建数据
+               .Select((o, or) => new
+               {
+                   FlowID = o.OrderId,
+                   FlowCode = o.Code,
+                   FlowPwd = o.Pwd,
+                   FlowTitle = o.Title,
+                   FlowFromName = or.SourceChannel,
+                   FlowPurTypeName = o.PurTypeName,
+                   ConTypeName = or.HotspotName,
+                   FlowAddDate = o.CreationTime,
+                   RSFlagName = or.Status < EOrderStatus.Filed ? "办理中" : "办理完成",
+                   WebUserID = o.WebUserID
+               })
+           //将结果合并成一个表
+           .MergeTable()
+                  .Where(p => p.WebUserID == dataUser.WebUserID)
+               .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
+
+                //计算总页数
+                int nPageCount = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(total) / dto.PageSize));
+                OrderListReturnDto returnDto = new()
+                {
+                    PageNum = dto.PageIndex,
+                    PageCount = nPageCount,
+                    Data = _mapper.Map<IReadOnlyList<OrderListDto>>(items)
+                };
+
+                return OpenResponse.Ok(WebPortalDeResponse<OrderListReturnDto>.Success(returnDto, "成功"));
+            }
+            return OpenResponse.Ok(WebPortalDeResponse<OrderListReturnDto>.Success(new OrderListReturnDto(), "成功"));
+
+        }
+        #endregion
+
+        #region 短信、基础设置
+        /// <summary>
+        /// 短信验证码发送
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("writeletterssendsms")]
+        public async Task<OpenResponse> WriteLettersSendSms([FromBody] WebPortalSendSmsModelDto dto)
+        {
+            string keyToken = "SmsUserWriteKey_" + dto.TelNum + "_" + DateTime.Now.ToString("yyyyMMdd");
+
+            var data = await _writeLettersSendSms.GetAsync(keyToken, HttpContext.RequestAborted);
+            if (data != null)//已经发过短信
+            {
+                //是否可以继续发送短信(10次)
+                if (data.SendCount > 10)
+                    //短信发送超过10条
+                    return OpenResponse.Ok(WebPortalDeResponse<string>.Success("-2"));
+
+                // 验证是否在两分钟之内
+                TimeSpan duration = DateTime.Now - data.AddTime; // 计算时间差
+                if ((int)duration.TotalSeconds < 120)
+                    // 距离上次发送时间不足两分钟
+                    return OpenResponse.Ok(WebPortalDeResponse<string>.Success("-4"));
+
+                //修改缓存信息
+                data.SendCount++;
+                data.SmsCode = dto.SmsCode;
+                data.AddTime = DateTime.Now;
+
+            }
+            else
+            {
+                //没有发过短信,直接写入数据
+                data = new WriteLettersSendSmsDto
+                {
+                    AddTime = DateTime.Now,
+                    SmsCode = dto.SmsCode,
+                    MobileNum = dto.TelNum,
+                    SendCount = 1
+                };
+            }
+            //这里发送短信  宜宾12345市民服务热线网站验证码{0},用于写信时验证联系电话。验证码有效期10分钟。
+
+            var messageDto = new Share.Dtos.Push.MessageDto
+            {
+                PushBusiness = EPushBusiness.MsgCode,
+                ExternalId = "",
+                OrderId = "",
+                PushPlatform = EPushPlatform.Web,
+                Remark = "",
+                Name = dto.TelNum,
+                TemplateCode = "1007",
+                Params = new List<string>() { dto.SmsCode },
+                TelNumber = dto.TelNum,
+
+            };
+            await _mediator.Publish(new PushMessageNotify(messageDto), HttpContext.RequestAborted);
+
+            //修改缓存
+            await _writeLettersSendSms.SetAsync(keyToken, data, TimeSpan.FromDays(1), HttpContext.RequestAborted);
+
+            return OpenResponse.Ok(WebPortalDeResponse<string>.Success("1"));
+        }
+
+        /// <summary>
+        /// 验证短信验证码是否正确   正确返回1,错误返回-1
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("checksmscode")]
+        public async Task<OpenResponse> CheckSmsCode([FromBody] WebPortalSendSmsModelDto dto)
+        {
+            string keyToken = "SmsUserWriteKey_" + dto.TelNum + "_" + DateTime.Now.ToString("yyyyMMdd");
+
+            var data = await _writeLettersSendSms.GetAsync(keyToken, HttpContext.RequestAborted);
+            //检测是否存在
+            if (data != null)
+                return OpenResponse.Ok(WebPortalDeResponse<string>.Success(data.SmsCode));
+
+            //不存在
+            return OpenResponse.Ok(WebPortalDeResponse<string>.Success("-1"));
+        }
+
+        /// <summary>
+        /// 获取热点分类的树形
+        /// </summary>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("gethotspottreelist")]
+        public async Task<OpenResponse> GetHotspotTreeList()
+        {
+            var data = await _hotspotRepository.Queryable().ToTreeAsync(it => it.Children, it => it.ParentId, null);
+            return OpenResponse.Ok(WebPortalDeResponse<List<Hotspot>>.Success(data));
+        }
+
+        /// <summary>
+        /// 系统主题颜色
+        /// </summary>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getsystemsettingstheme")]
+        public async Task<OpenResponse> GetSystemSettingsTheme()
+        {
+            var WebSystemSettingsTheme = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsOpenJudicialManagement).SettingValue[0]);
+
+            if (WebSystemSettingsTheme == false)
+                return OpenResponse.Ok(WebPortalDeResponse<string>.Success(""));
+
+            else
+                return OpenResponse.Ok(WebPortalDeResponse<string>.Success("class=gray2"));
+
+        }
+        #endregion
+
+        #region 办件
+        /// <summary>
+        /// 获取信件前6条数据
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("getorderlistbynum")]
+        [AllowAnonymous]
+        public async Task<OpenResponse> GetOrderListByNum([FromBody] QueryOrderListByNumDto dto)
+        {
+            var items = await _orderPublishRepository.Queryable()
+               .Includes(p => p.Order)
+                .Where(p => p.PublishState == true)
+                .Where(p => p.Order.Id != null)
+                .OrderByDescending(p => p.CreationTime)
+               .Select(p => new
+               {
+                   DataID = p.OrderId,
+                   Title = p.Order.Title,
+                   CreateDate = p.CreationTime
+               })
+                .Take(dto.Num)
+                .ToListAsync();
+            var data = _mapper.Map<IReadOnlyList<DataListTopDto>>(items);
+            return OpenResponse.Ok(WebPortalDeResponse<IReadOnlyList<DataListTopDto>>.Success(data, "成功"));
+        }
+
+        /// <summary>
+        /// 办件摘编列表数据
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("getorderlist")]
+        [AllowAnonymous]
+        public async Task<OpenResponse> GetOrderList([FromBody] QueryOrderListDto dto)
+        {
+            RefAsync<int> total = 0;
+            var items = await _orderPublishRepository.Queryable()
+                 .Includes(d => d.Order)
+                .Where(d => d.PublishState == true && d.Order.Id != null)
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowCode), d => d.Order.No == dto.FlowCode)
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowName), d => d.Order.Title.Contains(dto.FlowName))
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowSType), d => d.Order.AcceptTypeCode == dto.FlowSType)
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowRType), d => d.Order.HotspotId == dto.FlowRType)
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowSDate), d => d.Order.StartTime >= DateTime.Parse(DateTime.Parse(dto.FlowSDate).ToString("yyyy-MM-dd 00:00:00")))//dto.FlowSDate
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowEDate), d => d.Order.StartTime <= DateTime.Parse(DateTime.Parse(dto.FlowEDate).ToString("yyyy-MM-dd 00:00:00")))// dto.FlowEDate
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowFrom), d => d.Order.FromName.Contains(dto.FlowFrom))
+                .OrderByDescending(p => p.CreationTime)
+                .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
+
+            //计算总页数
+            int nPageCount = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(total) / dto.PageSize));
+            OrderListReturnDto returnDto = new()
+            {
+                PageNum = dto.PageIndex,
+                PageCount = nPageCount,
+                Data = _mapper.Map<IReadOnlyList<OrderListDto>>(items)
+            };
+
+            return OpenResponse.Ok(WebPortalDeResponse<OrderListReturnDto>.Success(returnDto, "成功"));
+        }
+
+        /// <summary>
+        /// 办件摘编详情
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getorderdetailbyid")]
+        public async Task<OpenResponse> GetOrderDetailById([FromBody] ArticleIdDto dto)
+        {
+            var data = await _orderRepository.GetAsync(p => p.Id == dto.Id, HttpContext.RequestAborted);
+
+            var orderDetail = _mapper.Map<OrderDetail>(data);
+            if (data != null)
+            {
+                //如果是省工单或者是省政民互动的工单,市民不能进行评价
+                if (data.IsProvince == true || data.SourceChannelCode == "ZMHD")
+                    orderDetail.IsProvinceOrder = "1";
+                else
+                    orderDetail.IsProvinceOrder = "0";
+
+                //获取发布的数据
+                var orderPublish = await _orderPublishRepository.GetAsync(p => p.OrderId == data.Id, HttpContext.RequestAborted);
+                if (orderPublish != null)
+                {
+                    orderDetail.FlowTitle = orderPublish.ArrangeTitle;
+                    orderDetail.FlowContent = orderPublish.ArrangeContent;
+                    orderDetail.FlowResult = orderPublish.ArrangeOpinion;
+                    if (orderPublish.PublishState)
+                        orderDetail.FlowPubFlagName = "公开";
+                }
+
+                //能否进行评价
+                var orderVisit = await _orderVisitRepository.GetAsync(p => p.OrderId == data.Id && p.VisitState != EVisitState.None, HttpContext.RequestAborted);
+                if (orderVisit == null)
+                    orderDetail.VisitType = "0";
+                else
+                {
+                    orderDetail.VisitType = orderVisit.VisitState switch
+                    {
+                        EVisitState.WaitForVisit => "1",
+                        EVisitState.Visited => "2",
+                        EVisitState.Visiting or EVisitState.NoSatisfiedWaitForVisit or EVisitState.None => "0",
+                        _ => "0",
+                    };
+                }
+
+            }
+            List<OrderDetail> dataDto = new() { orderDetail };
+            return OpenResponse.Ok(WebPortalDeResponse<IReadOnlyList<OrderDetail>>.Success(dataDto));
+        }
+
+        /// <summary>
+        /// 根据编号和密码查询信件ID
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getorderid")]
+        public async Task<OpenResponse> GetOrderId([FromBody] GetOrderCodePwd dto)
+        {
+            var data = await _orderRepository.GetAsync(p => p.No == dto.OrderNo && p.Password == dto.Pwd, HttpContext.RequestAborted);
+            return OpenResponse.Ok(WebPortalDeResponse<string>.Success(data?.Id));
+        }
+
+        /// <summary>
+        /// 办件摘编详情
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getorderdetailbyno")]
+        public async Task<OpenResponse> GetOrderDetailByNo([FromBody] GetOrderCodePwd dto)
+        {
+            var data = await _orderRepository.GetAsync(p => p.No == dto.OrderNo && p.Password == dto.Pwd, HttpContext.RequestAborted);
+            if (data != null)
+            {
+                var orderDetail = _mapper.Map<OrderDetail>(data);
+                if (data != null)
+                {
+                    //如果是省工单或者是省政民互动的工单,市民不能进行评价
+                    if (data.IsProvince == true || data.SourceChannelCode == "ZMHD")
+                        orderDetail.IsProvinceOrder = "1";
+                    else
+                        orderDetail.IsProvinceOrder = "0";
+
+                    //获取发布的数据
+                    var orderPublish = await _orderPublishRepository.GetAsync(p => p.OrderId == data.Id, HttpContext.RequestAborted);
+                    if (orderPublish != null)
+                    {
+                        orderDetail.FlowTitle = orderPublish.ArrangeTitle;
+                        orderDetail.FlowContent = orderPublish.ArrangeContent;
+                        orderDetail.FlowResult = orderPublish.ArrangeOpinion;
+                        if (orderPublish.PublishState)
+                            orderDetail.FlowPubFlagName = "公开";
+                    }
+
+                    //能否进行评价
+                    var orderVisit = await _orderVisitRepository.GetAsync(p => p.OrderId == data.Id && p.VisitState != EVisitState.None, HttpContext.RequestAborted);
+                    if (orderVisit == null)
+                        orderDetail.VisitType = "0";
+                    else
+                    {
+                        orderDetail.VisitType = orderVisit.VisitState switch
+                        {
+                            EVisitState.WaitForVisit => "1",
+                            EVisitState.Visited => "2",
+                            EVisitState.Visiting or EVisitState.NoSatisfiedWaitForVisit or EVisitState.None => "0",
+                            _ => "0",
+                        };
+                    }
+
+                }
+                List<OrderDetail> dataDto = new() { orderDetail };
+                return OpenResponse.Ok(WebPortalDeResponse<IReadOnlyList<OrderDetail>>.Success(dataDto));
+            }
+            return OpenResponse.Ok(WebPortalDeResponse<IReadOnlyList<string>>.Failed(code: "0", description: "查询失败"));
+        }
+
+        /// <summary>
+        /// 写信接口
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("orderacceptance")]
+        public async Task<OpenResponse> OrderAcceptance([FromBody] WebFlowAccept dto)
+        {
+            var data = _mapper.Map<Hotline.Share.Dtos.Order.AddOrderDto>(dto);
+            data.SourceChannel = "网站";
+            data.SourceChannelCode = "WZ";
+            data.Source = ESource.WebPortal;
+            if (!string.IsNullOrEmpty(data.LicenceNo))
+            {
+                data.LicenceTypeCode = "10";
+                data.LicenceType = "中华人民共和国居民身份证";
+            }
+            data.ExternalId = Guid.NewGuid().ToString();
+
+            data.IdentityType = EIdentityType.Citizen;
+            data.Transpond = false;
+            data.IsEnforcementOrder = false;
+
+
+            var result = await _orderApplication.ReceiveOrderFromExternalAsync(data, _sessionContext, HttpContext.RequestAborted);
+
+            OrderAcceptanceReturnDto returnDto = new();
+            if (result != null)
+            {
+                returnDto.PWD = result.Password;
+                returnDto.Code = result.No;
+                returnDto.State = "1";
+
+                dto.Pwd = result.Password;
+                dto.Code = result.No;
+                dto.OrderId = result.Id;
+                await _webFlowAcceptRepository.AddAsync(dto, HttpContext.RequestAborted);
+
+            }
+            else
+                returnDto.State = "0";
+
+
+            return OpenResponse.Ok(WebPortalDeResponse<OrderAcceptanceReturnDto>.Success(returnDto));
+        }
+
+        /// <summary>
+        /// 受理类型和热点统计
+        /// </summary>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getchartdata")]
+        public async Task<OpenResponse> GetChartData()
+        {
+            var startDate = Convert.ToDateTime(DateTime.Now.AddMonths(-1).ToString("yyyy-MM-dd 00:00:00"));
+            var endDate = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd 23:59:59"));
+
+            //数据查询
+            var listType = await _orderRepository.Queryable()
+                .Where(p => p.StartTime >= startDate && p.StartTime <= endDate && p.Status > EOrderStatus.WaitForAccept)
+                .Select(it => new
+                {
+                    it.AcceptType,
+                    it.AcceptTypeCode
+                })
+                .MergeTable()//将查询出来的结果合并成一个新表
+                 .GroupBy(it => new { it.AcceptType, it.AcceptTypeCode })//对新表进行分组
+                 .Select(it => new
+                 {
+                     name = it.AcceptType,
+                     value = SqlFunc.AggregateCount(it.AcceptTypeCode)
+                 })
+                 .ToListAsync();
+
+            //数据查询-查询总数前5的数据
+            var listHot = await _orderRepository.Queryable()
+                .Where(p => p.StartTime >= startDate && p.StartTime <= endDate && p.Status > EOrderStatus.WaitForAccept)
+                .Select(it => new
+                {
+                    it.HotspotId,
+                    it.HotspotName
+                })
+                .MergeTable()//将查询出来的结果合并成一个新表
+                 .GroupBy(it => new { it.HotspotId, it.HotspotName })//对新表进行分组
+                 .Select(it => new
+                 {
+                     typeName = it.HotspotName,
+                     num = SqlFunc.AggregateCount(it.HotspotId)
+                 })
+                 .OrderByDescending(it => it.num)
+                 .Take(5)
+                 .ToListAsync();
+
+            GetChartDataDto dataDto = new()
+            {
+                formCount = _mapper.Map<IReadOnlyList<OrderFormCount>>(listType),
+                hotCount = _mapper.Map<IReadOnlyList<OrderHotCount>>(listHot)
+            };
+
+            return OpenResponse.Ok(WebPortalDeResponse<GetChartDataDto>.Success(dataDto));
+        }
+
+        /// <summary>
+        /// 获取统计数据
+        /// </summary>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getstatist")]
+        public async Task<OpenResponse> GetStatist()
+        {
+            var startDate = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd 00:00:00"));
+            var endDate = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd 23:59:59"));
+            GetStatistDto getStatistDto = new()
+            {
+                AllCount = await _orderRepository.Queryable().Where(p => p.Status > EOrderStatus.WaitForAccept).CountAsync(),
+                AllTrandCount = await _orderRepository.Queryable().Where(p => p.Status >= EOrderStatus.Filed).CountAsync(),
+                DayCount = await _orderRepository.Queryable().Where(p => p.StartTime >= startDate && p.StartTime <= endDate && p.Status > EOrderStatus.WaitForAccept).CountAsync(),
+                DayTrandCount = await _orderRepository.Queryable().Where(p => p.ActualHandleTime >= startDate && p.ActualHandleTime <= endDate && p.Status >= EOrderStatus.Filed).CountAsync()
+            };
+
+            return OpenResponse.Ok(WebPortalDeResponse<GetStatistDto>.Success(getStatistDto));
+        }
+        #endregion
+
+        #region 评价
+        /// <summary>
+        /// 获取待评价部门信息
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("getwaitvisitdata")]
+        public async Task<OpenResponse> GetWaitVisitData([FromBody] ArticleIdDto dto)
+        {
+            var dataOrder = await _orderVisitRepository.GetAsync(p => p.OrderId == dto.Id && p.VisitState != EVisitState.None, HttpContext.RequestAborted);
+            if (dataOrder == null)
+                return OpenResponse.Ok(WebPortalDeResponse<List<WaitVisitListDataDto>>.Failed("工单不能评价!"));
+
+            if (dataOrder.VisitState != EVisitState.WaitForVisit)
+                return OpenResponse.Ok(WebPortalDeResponse<List<WaitVisitListDataDto>>.Failed("工单已经评价!"));
+
+            var data = await _orderVisitDetailRepository.Queryable()
+                .Where(p => p.VisitId == dataOrder.Id && p.VisitTarget == EVisitTarget.Org)
+                .ToListAsync();
+
+            var listDat = _mapper.Map<List<WaitVisitListDataDto>>(data);
+            return OpenResponse.Ok(WebPortalDeResponse<List<WaitVisitListDataDto>>.Success(listDat));
+        }
+
+        /// <summary>
+        /// 评价内容
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("receivevisitdata")]
+        public async Task<OpenResponse> ReceiveVisitData([FromBody] OrderVisitListDataDto dto)
+        {
+            if (dto == null || dto.VistListDtos == null || dto.VistListDtos.Count == 0)
+                return OpenResponse.Ok(WebPortalDeResponse<string>.Failed("评价数据不能为空!"));
+
+            var dataOrder = await _orderVisitRepository.GetAsync(p => p.OrderId == dto.OrderId && p.VisitState != EVisitState.None, HttpContext.RequestAborted);
+            if (dataOrder == null)
+                return OpenResponse.Ok(WebPortalDeResponse<string>.Failed("工单已经评价!"));
+
+            //组装数据
+            OrderVisitWebDto visitWebDto = new()
+            {
+                VisitType = Hotline.Share.Enums.Order.EVisitType.WebVisit,
+                VisitTime = DateTime.Now
+            };
+
+            List<OrderVisitDetailWebDto> orderVisitDetails = new();
+
+            #region 部门评价
+            //部门评价
+            //遍历数据
+            foreach (var item in dto.VistListDtos)
+            {
+                Hotline.Share.Dtos.Kv kv = item.SatisfactionCode switch
+                {
+                    "5" => new Hotline.Share.Dtos.Kv { Key = "5", Value = "非常满意" },
+                    "4" => new Hotline.Share.Dtos.Kv { Key = "4", Value = "满意" },
+                    "2" => new Hotline.Share.Dtos.Kv { Key = "2", Value = "不满意" },
+                    _ => new Hotline.Share.Dtos.Kv { Key = "7", Value = "不做评价" },
+                };
+                //组装回访数据
+                visitWebDto.Id = item.VisitId;
+                OrderVisitDetailWebDto detailDto = new()
+                {
+                    Id = item.Id,
+                    VisitTarget = Hotline.Share.Enums.Order.EVisitTarget.Org,
+                    OrgProcessingResults = kv,
+                    OrgNoSatisfiedReason = new List<Hotline.Share.Dtos.Kv>(),
+                    OrgHandledAttitude = kv,
+                    VisitContent = item.VisitContent
+                };
+                orderVisitDetails.Add(detailDto);
+            }
+            #endregion
+
+            visitWebDto.OrderVisitDetailDto = orderVisitDetails;
+
+            //推送数据
+            await _orderApplication.OrderVisitWeb(visitWebDto, HttpContext.RequestAborted);
+            return OpenResponse.Ok(WebPortalDeResponse<string>.Success("1"));
+        }
+        #endregion
+    }
+}

+ 104 - 0
src/Hotline.Application/Mappers/WebPortalMapperConfigs.cs

@@ -0,0 +1,104 @@
+using Hotline.Orders;
+using Hotline.Share.Dtos.Order;
+using Hotline.Share.Dtos.WebPortal;
+using Hotline.Share.Enums.Order;
+using Hotline.WebPortal;
+using Mapster;
+
+namespace Hotline.Application.Mappers
+{
+    public class WebPortalMapperConfigs : IRegister
+    {
+        public void Register(TypeAdapterConfig config)
+        {
+            #region 门户网站数据
+
+            //办件摘编列表
+            config.ForType<OrderPublish, OrderListDto>()
+               .Ignore(d => d.Source)
+               .Map(d => d.FlowID, x => x.OrderId)
+               .Map(d => d.FlowCode, x => x.Order.No)
+               .Map(d => d.FlowPwd, x => x.Order.Password)
+               .Map(d => d.FlowTitle, x => string.IsNullOrEmpty(x.ArrangeTitle) ? x.Order.Title : x.ArrangeTitle)
+               .Map(d => d.FlowFromName, x => x.Order.SourceChannel)
+               .Map(d => d.FlowPurTypeName, x => x.Order.AcceptType)
+               .Map(d => d.ConTypeName, x => x.Order.HotspotName)
+               .Map(d => d.FlowAddDate, x => x.Order.StartTime)
+               .Map(d => d.PubDate, x => x.CreationTime)
+               .Map(d => d.RSFlagName, x => x.Order.Status < EOrderStatus.Filed ? "办理中" : "办理完成")
+          ;
+
+            //办件摘编详情
+            config.ForType<Order, OrderDetail>()
+            .Map(d => d.FlowID, x => x.Id)
+            .Map(d => d.FlowCode, x => x.No)
+            .Map(d => d.Pwd, x => x.Password)
+             .Map(d => d.FlowTitle, x => x.Title)
+            .Map(d => d.FlowPubFlagName, x => "不公开")
+              .Map(d => d.FlowContent, x => x.Content)
+             .Map(d => d.FlowResult, x => x.ActualOpinion)
+            .Map(d => d.FlowFromName, x => x.SourceChannel)
+            .Map(d => d.FlowPurTypeName, x => x.AcceptType)
+            .Map(d => d.FlowConTypeName, x => x.HotspotName)
+            .Map(d => d.FlowAddDate, x => x.StartTime)
+            .Map(d => d.FlowEndDate, x => x.ActualHandleTime)
+            .Map(d => d.FlowBMID, x => x.ActualHandleOrgCode)
+            .Map(d => d.FlowBMName, x => x.ActualHandleOrgName)
+            .Map(d => d.FlowLKName, x => x.FromName)
+            .Map(d => d.FlowRSFlagName, x => x.Status < EOrderStatus.Filed ? "办理中" : "办理完成")
+       ;
+
+            //注册用户数据
+            config.ForType<UserModelDto, WebUserRegister>()
+            .Map(d => d.WebUserName, x => x.name)
+            .Map(d => d.WebLoginName, x => x.username)
+            .Map(d => d.PhoneNum, x => x.employeeMobile)
+            .Map(d => d.IDCard, x => x.idNumber)
+            .Map(d => d.Address, x => x.address)
+            .Map(d => d.Gender, x => x.sex)
+            ;
+
+            //统一认证数据
+            config.ForType<UserModelDto, WebUserAuth>()
+            .Map(d => d.DataId, x => x.id)
+            .Map(d => d.Type, x => x.type)
+            .Map(d => d.Address, x => x.address)
+            .Map(d => d.Birth, x => x.birth)
+            .Map(d => d.DocumentType, x => x.document_type)
+            .Map(d => d.IdNumber, x => x.idNumber)
+            .Map(d => d.Nationality, x => x.nationality)
+            .Map(d => d.Name, x => x.name)
+            .Map(d => d.Sex, x => x.sex)
+            .Map(d => d.DisplayName, x => x.displayName)
+            .Map(d => d.Username, x => x.username)
+            .Map(d => d.EmployeeMobile, x => x.employeeMobile)
+            ;
+
+            //门户网站写信
+            config.ForType<WebFlowAccept, AddOrderDto>()
+            .Map(d => d.FromName, x => x.LKName)
+            .Map(d => d.FromGender, x => x.Sex == "男" ? EGender.Male : x.Sex == "女" ? EGender.Female : EGender.Unknown)
+            .Map(d => d.IsSecret, x => x.IsSecret == "1" ? true : false)
+            .Map(d => d.LicenceNo, x => x.IDCard)
+            .Map(d => d.Contact, x => x.Mobile)
+            // .Map(d => d.FullAddress, x => x.Address)
+            .Map(d => d.Title, x => x.Title)
+            .Map(d => d.Content, x => x.Content)
+            .Map(d => d.AcceptType, x => x.PurTypeName)
+            .Map(d => d.AcceptTypeCode, x => x.PurTypeID)
+            ;
+
+            //回访部门
+            config.ForType<OrderVisitDetail, WaitVisitListDataDto>()
+            .Map(d => d.Id, x => x.Id)
+            .Map(d => d.VisitId, x => x.VisitId)
+            .Map(d => d.VisitOrgCode, x => x.VisitOrgCode)
+            .Map(d => d.VisitOrgName, x => x.VisitOrgName)
+            .Map(d => d.VisitContent, x => x.VisitContent)
+            .Map(d => d.SatisfactionCode, x => x.OrgProcessingResults != null ? x.OrgProcessingResults.Value : "")
+            ;
+
+            #endregion
+        }
+    }
+}

+ 9 - 0
src/Hotline.Application/Orders/IOrderApplication.cs

@@ -6,6 +6,7 @@ using System.Text;
 using System.Threading.Tasks;
 using Hotline.Orders;
 using Hotline.Share.Dtos;
+using Hotline.Share.Dtos.DataSharing.PusherHotlineDto;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Enums.Order;
@@ -51,6 +52,14 @@ namespace Hotline.Application.Orders
         /// </summary>
         Task UpdateOrderFilesAnonymousAsync(UpdateOrderFilesDto dto, CancellationToken cancellationToken);
 
+        /// <summary>
+        /// 工单回访
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task OrderVisitWeb(OrderVisitWebDto dto, CancellationToken cancellationToken);
+
         #region 工单办理
 
         ISugarQueryable<Order> QueryOrders(QueryOrderDto dto);

+ 81 - 5
src/Hotline.Application/Orders/OrderApplication.cs

@@ -1,4 +1,6 @@
-using Hotline.Caching.Interfaces;
+using DotNetCore.CAP;
+using Hotline.Application.Quality;
+using Hotline.Caching.Interfaces;
 using Hotline.File;
 using Hotline.FlowEngine.Workflows;
 using Hotline.Orders;
@@ -6,22 +8,21 @@ using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Repository.SqlSugar.Ts;
 using Hotline.Settings.TimeLimits;
 using Hotline.Share.Dtos;
+using Hotline.Share.Dtos.DataSharing.PusherHotlineDto;
 using Hotline.Share.Dtos.File;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Enums.Order;
+using Hotline.Share.Enums.Quality;
 using Hotline.Share.Enums.Settings;
 using Hotline.Tools;
-using Hotline.Users;
 using MapsterMapper;
-using MediatR;
 using SqlSugar;
 using XF.Domain.Authentications;
 using XF.Domain.Constants;
 using XF.Domain.Dependency;
-using XF.Domain.Entities;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 
@@ -39,6 +40,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     private readonly IRepositoryTextSearch<OrderTs> _repositoryts;
     private readonly IFileRepository _fileRepository;
     private readonly ISessionContext _sessionContext;
+    private readonly IRepository<OrderVisit> _orderVisitRepository;
+    private readonly IRepository<OrderVisitDetail> _orderVisitDetailRepository;
+    private readonly IQualityApplication _qualityApplication;
+    private readonly ICapPublisher _capPublisher;
 
     public OrderApplication(
         IOrderDomainService orderDomainService,
@@ -50,7 +55,11 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         IRepository<OrderWord> orderWrodRepository,
         IRepositoryTextSearch<OrderTs> repositoryts,
         IFileRepository fileRepository,
-        ISessionContext sessionContext
+        ISessionContext sessionContext,
+        IRepository<OrderVisit> orderVisitRepository,
+        IRepository<OrderVisitDetail> orderVisitDetailRepository,
+        IQualityApplication qualityApplication,
+        ICapPublisher capPublisher
         )
     {
         _orderDomainService = orderDomainService;
@@ -63,6 +72,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         _repositoryts = repositoryts;
         _fileRepository = fileRepository;
         _sessionContext = sessionContext;
+        _orderVisitRepository = orderVisitRepository;
+        _orderVisitDetailRepository = orderVisitDetailRepository;
+        _qualityApplication = qualityApplication;
+        _capPublisher = capPublisher;
     }
 
     /// <summary>
@@ -319,6 +332,69 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         }
     }
 
+    /// <summary>
+    /// 工单回访
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <param name="cancellationToken"></param>
+    /// <returns></returns>
+    public async Task OrderVisitWeb(OrderVisitWebDto dto, CancellationToken cancellationToken)
+    {
+        var visit = await _orderVisitRepository.Queryable()
+            .Includes(x => x.Order)
+            .Includes(x => x.OrderVisitDetails)
+            .FirstAsync(x => x.Id == dto.Id);
+        if (visit != null)
+        {
+            var first = dto.OrderVisitDetailDto.FirstOrDefault(x => x.VisitTarget == EVisitTarget.Org);
+
+            if (first != null)
+            {
+                visit.NowEvaluate = first.OrgProcessingResults;
+                visit.Order.Visited(first.OrgProcessingResults.Key, first.OrgProcessingResults.Value);
+            }
+            visit.VisitState = EVisitState.Visited;
+            visit.VisitTime = dto.VisitTime;
+            visit.VisitType = dto.VisitType;
+
+            for (int i = 0; i < visit.OrderVisitDetails.Count; i++)
+            {
+                var detail = visit.OrderVisitDetails[i];
+                var detaildto = dto.OrderVisitDetailDto.FirstOrDefault(x => x.Id == detail.Id);
+                if (detaildto != null)
+                {
+                    _mapper.Map(detaildto, visit.OrderVisitDetails[i]);
+                }
+            }
+
+            await _orderVisitRepository.UpdateAsync(visit, cancellationToken);
+            await _orderVisitDetailRepository.UpdateRangeAsync(visit.OrderVisitDetails, cancellationToken);
+            await _orderRepository.UpdateAsync(visit.Order, cancellationToken);
+            var orderDto = _mapper.Map<OrderDto>(visit.Order);
+            if (first != null)
+            {
+                //推省上
+                await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderVisited,
+                        new PublishVisitDto()
+                        {
+                            Order = orderDto,
+                            No = visit.No,
+                            VisitType = visit.VisitType,
+                            VisitName = visit.CreatorName,
+                            VisitTime = visit.VisitTime,
+                            VisitRemark = string.IsNullOrEmpty(first.VisitContent) ? first.OrgProcessingResults?.Value : first.VisitContent,
+                            AreaCode = visit.Order.AreaCode!,
+                            SubjectResultSatifyCode = first.OrgProcessingResults.Key,
+                            FirstSatisfactionCode = visit.Order.FirstVisitResultCode!,
+                            ClientGuid = ""
+                        }, cancellationToken: cancellationToken);
+            }
+            //写入质检
+            await _qualityApplication.AddQualityAsync(EQualitySource.Visit, orderDto.Id, visit.Id,
+                cancellationToken);
+        }
+    }
+
     public ISugarQueryable<Order> QueryOrders(QueryOrderDto dto)
     {
         var isCenter = _sessionContext.OrgIsCenter;

+ 6 - 108
src/Hotline.Application/Subscribers/DatasharingSubscriber.cs

@@ -1,5 +1,6 @@
 using DotNetCore.CAP;
 using Hotline.Application.FlowEngine;
+using Hotline.Application.Orders;
 using Hotline.Application.Quality;
 using Hotline.Authentications;
 using Hotline.File;
@@ -50,6 +51,7 @@ namespace Hotline.Application.Subscribers
         private readonly ISystemOrganizeRepository _systemOrganizeRepository;
         private readonly IRepository<TranspondCityRawData> _transpondCityRawDataRepository;
         private readonly IRepository<Workflow> _workflowRepository;
+        private readonly IOrderApplication _orderApplication;
 
         public DataSharingSubscriber(
             IRepository<OrderVisit> orderVisitRepository,
@@ -72,7 +74,8 @@ namespace Hotline.Application.Subscribers
             IRepository<SystemDicData> systemDicDataRepository,
             ISystemOrganizeRepository systemOrganizeRepository,
             IRepository<TranspondCityRawData> transpondCityRawDataRepository,
-            IRepository<Workflow> workflowRepository
+            IRepository<Workflow> workflowRepository,
+            IOrderApplication orderApplication
             )
         {
             _orderSendBackRepository = orderSendBackRepository;
@@ -95,6 +98,7 @@ namespace Hotline.Application.Subscribers
             _systemOrganizeRepository = systemOrganizeRepository;
             _transpondCityRawDataRepository = transpondCityRawDataRepository;
             _workflowRepository = workflowRepository;
+            _orderApplication = orderApplication;
         }
 
         /// <summary>
@@ -410,113 +414,7 @@ namespace Hotline.Application.Subscribers
         [CapSubscribe(Hotline.Share.Mq.EventNames.SharingOrderVisitAllOther)]
         public async Task OrderVisitWeb(OrderVisitWebDto dto, CancellationToken cancellationToken)
         {
-            var visit = await _orderVisitRepository.Queryable()
-                .Includes(x => x.Order)
-                .Includes(x => x.OrderVisitDetails)
-                .FirstAsync(x => x.Id == dto.Id);
-            if (visit != null)
-            {
-                var first = dto.OrderVisitDetailDto.FirstOrDefault(x => x.VisitTarget == EVisitTarget.Org);
-
-                if (first != null)
-                {
-                    visit.NowEvaluate = first.OrgProcessingResults;
-                    visit.Order.Visited(first.OrgProcessingResults.Key, first.OrgProcessingResults.Value);
-                }
-                visit.VisitState = EVisitState.Visited;
-                visit.VisitTime = dto.VisitTime;
-                visit.VisitType = dto.VisitType;
-
-                for (int i = 0; i < visit.OrderVisitDetails.Count; i++)
-                {
-                    var detail = visit.OrderVisitDetails[i];
-                    var detaildto = dto.OrderVisitDetailDto.FirstOrDefault(x => x.Id == detail.Id);
-                    if (detaildto != null)
-                    {
-                        _mapper.Map(detaildto, visit.OrderVisitDetails[i]);
-                    }
-                }
-
-                await _orderVisitRepository.UpdateAsync(visit, cancellationToken);
-                await _orderVisitDetailRepository.UpdateRangeAsync(visit.OrderVisitDetails, cancellationToken);
-                await _orderRepository.UpdateAsync(visit.Order, cancellationToken);
-                var orderDto = _mapper.Map<OrderDto>(visit.Order);
-                if (first != null)
-                {
-                    //推省上
-                    await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderVisited,
-                            new PublishVisitDto()
-                            {
-                                Order = orderDto,
-                                No = visit.No,
-                                VisitType = visit.VisitType,
-                                VisitName = visit.CreatorName,
-                                VisitTime = visit.VisitTime,
-                                VisitRemark = string.IsNullOrEmpty(first.VisitContent) ? first.OrgProcessingResults?.Value : first.VisitContent,
-                                AreaCode = visit.Order.AreaCode!,
-                                SubjectResultSatifyCode = first.OrgProcessingResults.Key,
-                                FirstSatisfactionCode = visit.Order.FirstVisitResultCode!,
-                                ClientGuid = ""
-                            }, cancellationToken: cancellationToken);
-                }
-                //处理网站通知差评数据
-                //if (visit.Order.Source == ESource.Hotline && visit.OrderVisitDetails.Any(x => x.OrgHandledAttitude?.Key == "1" || x.OrgHandledAttitude?.Key == "2" || x.OrgProcessingResults?.Key == "1" || x.OrgProcessingResults?.Key == "2"))
-                //{
-                //    //处理老数据
-                //    visit.VisitState = EVisitState.None;
-                //    await _orderVisitRepository.UpdateAsync(visit, cancellationToken);
-                //    //推送老数据变更给门户
-                //    await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderVisitedWeb, new PublishVisitAllDto()
-                //    {
-                //        Id = visit.Id,
-                //        Order = orderDto,
-                //        OrderVisitDetails = _mapper.Map<List<VisitDetailDto>>(visit.OrderVisitDetails),
-                //        VisitTime = visit.VisitTime,
-                //        VisitType = visit.VisitType,
-                //        VisitState = visit.VisitState,
-                //        PublishTime = visit.PublishTime,
-                //    }, cancellationToken: cancellationToken);
-
-                //    //包含不满意数据,重新生成新的回访
-                //    var newOrderVisit = _mapper.Map<OrderVisit>(visit);
-                //    newOrderVisit.InitId();
-                //    newOrderVisit.VisitState = EVisitState.NoSatisfiedWaitForVisit;
-                //    newOrderVisit.VisitTime = null;
-                //    newOrderVisit.IsCanHandle = false;
-                //    newOrderVisit.IsCanAiVisit = false;
-                //    newOrderVisit.AiVisitCount = 0;
-                //    await _orderVisitRepository.AddAsync(newOrderVisit, cancellationToken);
-                //    var list = _mapper.Map<List<OrderVisitDetail>>(visit.OrderVisitDetails);
-                //    list.ForEach(x =>
-                //    {
-                //        x.VisitId = newOrderVisit.Id;
-                //        x.VoiceEvaluate = null;
-                //        x.VoiceEvaluate = null;
-                //        x.OrgHandledAttitude = null;
-                //        x.OrgNoSatisfiedReason = null;
-                //        x.OrgProcessingResults = null;
-                //        x.VisitContent = "";
-                //    });
-                //    await _orderVisitDetailRepository.AddRangeAsync(list, cancellationToken);
-                //    //推送新数据给门户
-                //    await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderVisitedWeb, new PublishVisitAllDto()
-                //    {
-                //        Id = newOrderVisit.Id,
-                //        Order = orderDto,
-                //        OrderVisitDetails = _mapper.Map<List<VisitDetailDto>>(list),
-                //        VisitTime = newOrderVisit.VisitTime,
-                //        VisitType = newOrderVisit.VisitType,
-                //        VisitState = newOrderVisit.VisitState,
-                //        PublishTime = newOrderVisit.PublishTime,
-                //    }, cancellationToken: cancellationToken);
-                //}
-                //else
-                //{
-                //写入质检
-                await _qualityApplication.AddQualityAsync(EQualitySource.Visit, orderDto.Id, visit.Id,
-                    cancellationToken);
-                //}
-            }
+            await _orderApplication.OrderVisitWeb(dto, cancellationToken);
         }
 
         /// <summary>

+ 277 - 0
src/Hotline.Share/Dtos/WebPortal/ArticleDetailsDto.cs

@@ -0,0 +1,277 @@
+using Hotline.Share.Requests;
+
+namespace Hotline.Share.Dtos.WebPortal
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public class ArticleIdDto
+    {
+        /// <summary>
+        /// 公告ID
+        /// </summary>
+        public string Id { get; set; }
+    }
+
+    public class ArticleIdByNumDto
+    {
+        /// <summary>
+        /// 公告分类
+        /// </summary>
+        public string? BulletinTypeId { get; set; }
+
+        /// <summary>
+        /// 条数
+        /// </summary>
+        public int Num { get; set; }
+
+        /// <summary>
+        /// 是否查询图片
+        /// </summary>
+        public string CheckChar { get; set; }
+    }
+
+    /// <summary>
+    /// 公告上一条下一条
+    /// </summary>
+    public class ArticlePreviousAndNextDto
+    {
+        /// <summary>
+        /// 公告ID
+        /// </summary>
+        public string ID { get; set; }
+
+        /// <summary>
+        /// 公告分类
+        /// </summary>
+        public string? NoticeTypeId { get; set; }
+
+        /// <summary>
+        /// 关键字
+        /// </summary>
+        public string? Condition { get; set; }
+
+        /// <summary>
+        /// 是否全文查找
+        /// </summary>
+        public string? FullSearch { get; set; }
+
+    }
+
+    /// <summary>
+    /// 公告上一条下一条 返回数据
+    /// </summary>
+    public class ArticlePreviousAndNextDataDto
+    {
+        /// <summary>
+        /// 上一条P,下一条N
+        /// </summary>
+        public string pntype { get; set; }
+
+        /// <summary>
+        /// 公告ID
+        /// </summary>
+        public string NoticeID { get; set; }
+
+        /// <summary>
+        /// 公告标题
+        /// </summary>
+        public string NoticeTitle { get; set; }
+    }
+
+    /// <summary>
+    /// 公告详情
+    /// </summary>
+    public class ArticleDetailsDto
+    {
+        /// <summary>
+        /// 公告ID
+        /// </summary>
+        public string NoticeID { get; set; }
+
+        /// <summary>
+        /// 分类名称
+        /// </summary>
+        public string NoticeTypeName { get; set; }
+
+        /// <summary>
+        /// 标题
+        /// </summary>
+        public string NoticeTitle { get; set; }
+
+        /// <summary>
+        /// 发布部门
+        /// </summary>
+        public string NoticeBMName { get; set; }
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        public DateTime? NoticeCreateDate { get; set; }
+
+        /// <summary>
+        /// 阅读次数
+        /// </summary>
+        public int NoticeRCount { get; set; }
+
+        /// <summary>
+        /// 视频地址
+        /// </summary>
+        public string WNED_VideoUrl { get; set; }
+
+        /// <summary>
+        /// 内容
+        /// </summary>
+        public string NoticeContent { get; set; }
+    }
+
+    /// <summary>
+    /// 公告列表返回数据
+    /// </summary>
+    public class ArticleListDataDto
+    {
+        /// <summary>
+        /// 总页数
+        /// </summary>
+        public int PageCount { get; set; } = 0;
+
+        /// <summary>
+        /// 数据
+        /// </summary>
+        public IReadOnlyList<ArticleListDto> data { get; set; }
+    }
+
+    /// <summary>
+    /// 公告列表
+    /// </summary>
+    public class ArticleListDto
+    {
+        /// <summary>
+        /// 页码
+        /// </summary>
+        public int Page { get; set; }
+
+        /// <summary>
+        /// 内容
+        /// </summary>
+        public string NoticeContent { get; set; }
+
+        /// <summary>
+        /// ID
+        /// </summary>
+        public string NoticeID { get; set; }
+
+        /// <summary>
+        /// 分类名称
+        /// </summary>
+        public string NoticeTypeName { get; set; }
+
+        /// <summary>
+        /// 标题
+        /// </summary>
+        public string NoticeTitle { get; set; }
+
+        /// <summary>
+        /// 部门
+        /// </summary>
+        public string NoticeBMName { get; set; }
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        public DateTime? NoticeCreateDate { get; set; }
+
+        /// <summary>
+        /// 视频地址
+        /// </summary>
+        public string VideoUrl { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public string WNLT_FullCode { get; set; }
+    }
+
+    /// <summary>
+    /// 公告列表查询参数
+    /// </summary>
+    public record QueryArticleListDto : PagedRequest
+    {
+        /// <summary>
+        /// 公告分类
+        /// </summary>
+        public string? NoticeType { get; set; }
+
+        /// <summary>
+        /// 标题
+        /// </summary>
+        public string? Condition { get; set; }
+    }
+
+    /// <summary>
+    /// 全文搜索
+    /// </summary>
+    public class FullTextSearchListDto
+    {
+        /// <summary>
+        /// 索引
+        /// </summary>
+        public int Page { get; set; }
+
+        /// <summary>
+        /// ID
+        /// </summary>
+        public string NoticeID { get; set; }
+
+        /// <summary>
+        /// 分类名称ID
+        /// </summary>
+        public string NoticeTypeID { get; set; }
+
+        /// <summary>
+        /// 分类名称
+        /// </summary>
+        public string NoticeTypeName { get; set; }
+
+        /// <summary>
+        /// 标题
+        /// </summary>
+        public string NoticeTitle { get; set; }
+
+        /// <summary>
+        /// 创建部门
+        /// </summary>
+        public string NoticeBMName { get; set; }
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        public string NoticeCreateDate { get; set; }
+
+        /// <summary>
+        /// 内容
+        /// </summary>
+        public string Content { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public string TypeCode { get; set; }
+    }
+
+    /// <summary>
+    /// 全文搜索返回参数
+    /// </summary>
+    public class FullTextSearchListDataDto
+    {
+        /// <summary>
+        /// 总页数
+        /// </summary>
+        public int PageCount { get; set; } = 0;
+
+        /// <summary>
+        /// 数据
+        /// </summary>
+        public IReadOnlyList<FullTextSearchListDto> data { get; set; }
+    }
+}

+ 357 - 0
src/Hotline.Share/Dtos/WebPortal/GetOrderCodePwd.cs

@@ -0,0 +1,357 @@
+using Hotline.Share.Requests;
+
+namespace Hotline.Share.Dtos.WebPortal
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public class GetOrderCodePwd
+    {
+        public string OrderNo { get; set; }
+        public string Pwd { get; set; }
+    }
+
+    /// <summary>
+    /// 获取前几条数据
+    /// </summary>
+    public class QueryOrderListByNumDto
+    {
+        public int Num { get; set; }
+    }
+
+    /// <summary>
+    /// 会员中心查询用户信件
+    /// </summary>
+    public record QueryOrderListByUserDto : PagedRequest
+    {
+        /// <summary>
+        /// 信件编号
+        /// </summary>
+        public string? UserId { get; set; }
+    }
+
+    /// <summary>
+    /// 办件摘编列表查询参数
+    /// </summary>
+    public record QueryOrderListDto : PagedRequest
+    {
+        /// <summary>
+        /// 信件编号
+        /// </summary>
+        public string? FlowCode { get; set; }
+
+        /// <summary>
+        /// 标题
+        /// </summary>
+        public string? FlowName { get; set; }
+
+        /// <summary>
+        /// 受理类型
+        /// </summary>
+        public string? FlowSType { get; set; }
+
+        /// <summary>
+        /// 热点类型
+        /// </summary>
+        public string? FlowRType { get; set; }
+
+        /// <summary>
+        /// 开始时间
+        /// </summary>
+        public string? FlowSDate { get; set; }
+
+        /// <summary>
+        /// 结束时间
+        /// </summary>
+        public string? FlowEDate { get; set; }
+
+        /// <summary>
+        /// 来信人
+        /// </summary>
+        public string? FlowFrom { get; set; }
+    }
+
+    /// <summary>
+    /// 办件摘编列表数据
+    /// </summary>
+    public class OrderListDto
+    {
+        /// <summary>
+        /// 信件ID
+        /// </summary>
+        public string FlowID { get; set; }
+
+        /// <summary>
+        /// 信件编号
+        /// </summary>
+        public string FlowCode { get; set; }
+
+        /// <summary>
+        /// 查询密码
+        /// </summary>
+        public string FlowPwd { get; set; }
+
+        /// <summary>
+        /// 信件标题
+        /// </summary>
+        public string FlowTitle { get; set; }
+
+        /// <summary>
+        /// 来源
+        /// </summary>
+        public string FlowFromName { get; set; }
+
+        /// <summary>
+        /// 受理类型
+        /// </summary>
+        public string FlowPurTypeName { get; set; }
+
+        /// <summary>
+        /// 热点类型
+        /// </summary>
+        public string ConTypeName { get; set; }
+
+        /// <summary>
+        /// 来信时间
+        /// </summary>
+        public DateTime? FlowAddDate { get; set; }
+
+        /// <summary>
+        /// 发布时间
+        /// </summary>
+        public DateTime? PubDate { get; set; }
+
+        /// <summary>
+        /// 办理状态
+        /// </summary>
+        public string RSFlagName { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public int Source { get; set; } = 1;
+
+    }
+
+    /// <summary>
+    /// 办件摘编列表数据
+    /// </summary>
+    public class OrderListReturnDto
+    {
+        /// <summary>
+        /// 页码
+        /// </summary>
+        public int PageNum { get; set; }
+
+        /// <summary>
+        /// 总页数
+        /// </summary>
+        public int PageCount { get; set; }
+
+        /// <summary>
+        /// 数据对象
+        /// </summary>
+        public IReadOnlyList<OrderListDto> Data { get; set; }
+    }
+
+    /// <summary>
+    /// 信件明细
+    /// </summary>
+    public class OrderDetail
+    {
+        /// <summary>
+        /// 工单ID
+        /// </summary>
+        public string FlowID { get; set; }
+
+        /// <summary>
+        /// 部门id
+        /// </summary>
+        public string FlowBMID { get; set; }
+
+        /// <summary>
+        /// 部门名称
+        /// </summary>
+        public string FlowBMName { get; set; }
+
+        /// <summary>
+        /// 信件编号
+        /// </summary>
+        public string FlowCode { get; set; }
+
+        /// <summary>
+        /// 查询密码
+        /// </summary>
+        public string Pwd { get; set; }
+
+        /// <summary>
+        /// 信件标题
+        /// </summary>
+        public string FlowTitle { get; set; }
+
+        /// <summary>
+        /// 来源
+        /// </summary>
+        public string FlowFromName { get; set; }
+
+        /// <summary>
+        /// 受理类型
+        /// </summary>
+        public string FlowPurTypeName { get; set; }
+
+        /// <summary>
+        /// 热点类型
+        /// </summary>
+        public string FlowConTypeName { get; set; }
+
+        /// <summary>
+        /// 来信时间
+        /// </summary>
+        public DateTime? FlowAddDate { get; set; }
+
+        /// <summary>
+        /// 办结时间
+        /// </summary>
+        public DateTime? FlowEndDate { get; set; }
+
+        /// <summary>
+        /// 来信人
+        /// </summary>
+        public string FlowLKName { get; set; }
+
+        /// <summary>
+        /// 办理状态
+        /// </summary>
+        public string FlowRSFlagName { get; set; }
+
+        /// <summary>
+        /// 是否公开
+        /// </summary>
+        public string FlowPubFlagName { get; set; }
+
+        /// <summary>
+        /// 受理内容
+        /// </summary>
+        public string FlowContent { get; set; }
+
+        /// <summary>
+        /// 办理意见
+        /// </summary>
+        public string FlowResult { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public string PubFlag { get; set; }
+
+        /// <summary>
+        /// 是否是省工单或者政民互动工单 0:否,1:是
+        /// </summary>
+        public string IsProvinceOrder { get; set; }
+
+        /// <summary>
+        /// 是否可以评价 0:不能评价,1:可以评价,2:已评价
+        /// </summary>
+        public string VisitType { get; set; }
+    }
+
+    /// <summary>
+    /// 受理信息返回
+    /// </summary>
+    public class OrderAcceptanceReturnDto
+    {
+        public string State { get; set; }
+
+        public string Code { get; set; }
+
+        public string PWD { get; set; }
+    }
+
+    /// <summary>
+    /// 受理类型统计
+    /// </summary>
+    public class OrderFormCount
+    {
+        /// <summary>
+        /// 名称
+        /// </summary>
+        public string name { get; set; }
+
+        /// <summary>
+        /// 数量
+        /// </summary>
+        public int value { get; set; }
+    }
+
+    /// <summary>
+    /// 热点统计
+    /// </summary>
+    public class OrderHotCount
+    {
+        /// <summary>
+        /// 名称
+        /// </summary>
+        public string typeName { get; set; }
+
+        /// <summary>
+        /// 数量
+        /// </summary>
+        public int num { get; set; }
+    }
+
+    /// <summary>
+    /// 返回数据
+    /// </summary>
+    public class GetChartDataDto
+    {
+        /// <summary>
+        /// 受理类型统计
+        /// </summary>
+        public IReadOnlyList<OrderFormCount> formCount { get; set; }
+
+        /// <summary>
+        /// 热点统计
+        /// </summary>
+        public IReadOnlyList<OrderHotCount> hotCount { get; set; }
+    }
+
+    /// <summary>
+    /// 办件统计
+    /// </summary>
+    public class GetStatistDto
+    {
+        /// <summary>
+        /// 累计接件
+        /// </summary>
+        public int AllCount { get; set; }
+
+        /// <summary>
+        /// 累计已办
+        /// </summary>
+        public int AllTrandCount { get; set; }
+
+        /// <summary>
+        /// 今日接件
+        /// </summary>
+        public int DayCount { get; set; }
+
+        /// <summary>
+        /// 今日已办
+        /// </summary>
+        public int DayTrandCount { get; set; }
+    }
+
+    /// <summary>
+    /// 返回数据
+    /// </summary>
+    public class DataListTopDto
+    {
+        public string DataID { get; set; }
+
+        public string Title { get; set; }
+
+        public DateTime? CreateDate { get; set; }
+
+        public string? Content { get; set; }
+    }
+}

+ 75 - 0
src/Hotline.Share/Dtos/WebPortal/UserModelDto.cs

@@ -0,0 +1,75 @@
+namespace Hotline.Share.Dtos.WebPortal
+{
+    public class UserModelDto
+    {
+        /// <summary>
+        /// 数据获取状态
+        /// true
+        /// false
+        /// </summary>
+        public string success { get; set; }
+
+        /// <summary>
+        /// id 号码
+        /// </summary>
+        public string id { get; set; }
+
+        /// <summary>
+        /// 自然人
+        /// 法人
+        /// </summary>
+        public string type { get; set; }
+
+        /// <summary>
+        /// 地址
+        /// </summary>
+        public string address { get; set; }
+
+        /// <summary>
+        /// 生日
+        /// </summary>
+        public string birth { get; set; }
+
+        /// <summary>
+        /// 文档类型
+        /// </summary>
+        public string document_type { get; set; }
+
+        /// <summary>
+        /// 身份证号码
+        /// </summary>
+        public string idNumber { get; set; }
+
+        /// <summary>
+        /// 未知
+        /// </summary>
+        public string nationality { get; set; }
+
+        /// <summary>
+        /// 姓名
+        /// </summary>
+        public string name { get; set; }
+
+        /// <summary>
+        /// 性别
+        /// 1-男
+        /// 2-女
+        /// </summary>
+        public string sex { get; set; }
+
+        /// <summary>
+        /// 昵称
+        /// </summary>
+        public string displayName { get; set; }
+
+        /// <summary>
+        /// 登陆名
+        /// </summary>
+        public string username { get; set; }
+
+        /// <summary>
+        /// 手机号码
+        /// </summary>
+        public string employeeMobile { get; set; }
+    }
+}

+ 51 - 0
src/Hotline.Share/Dtos/WebPortal/WaitVisitListDataDto.cs

@@ -0,0 +1,51 @@
+namespace Hotline.Share.Dtos.WebPortal
+{
+    public class OrderVisitListDataDto
+    {
+        /// <summary>
+        /// 工单ID
+        /// </summary>
+        public string? OrderId { get; set; }
+
+        public List<WaitVisitListDataDto> VistListDtos { get; set; }
+    }
+
+    public class WaitVisitListDataDto
+    {
+        /// <summary>
+        /// ID
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 工单ID
+        /// </summary>
+        public string? OrderId { get; set; }
+
+        /// <summary>
+        /// 回访ID
+        /// </summary>
+        public string? VisitId { get; set; }
+
+        /// <summary>
+        /// 回访部门Code
+        /// </summary>
+        public string? VisitOrgCode { get; set; }
+
+        /// <summary>
+        /// 回访部门名称
+        /// </summary>
+        public string? VisitOrgName { get; set; }
+
+        /// <summary>
+        /// 评价内容
+        /// </summary>
+        public string? VisitContent { get; set; }
+
+        /// <summary>
+        /// 满意度
+        /// </summary>
+        public string? SatisfactionCode { get; set; }
+
+    }
+}

+ 55 - 0
src/Hotline.Share/Dtos/WebPortal/WebPortalDeResponse.cs

@@ -0,0 +1,55 @@
+namespace Hotline.Share.Dtos.WebPortal
+{
+    /// <summary>
+    /// 门户数据返回
+    /// </summary>
+    /// <typeparam name="TData"></typeparam>
+    public class WebPortalDeResponse<TData>
+    {
+        /// <summary>
+        /// 状态
+        /// </summary>
+        public string code { get; set; }
+
+        /// <summary>
+        /// 描述
+        /// </summary>
+        public string msg { get; set; }
+
+        /// <summary>
+        /// 数据
+        /// </summary>
+        public TData data { get; set; }
+
+        /// <summary>
+        /// 成功
+        /// </summary>
+        /// <param name="data"></param>
+        /// <param name="description"></param>
+        /// <returns></returns>
+        public static WebPortalDeResponse<TData> Success(TData data, string? description = "")
+        {
+            return new WebPortalDeResponse<TData>
+            {
+                code = "1",
+                data = data,
+                msg = (description ?? "接口调用成功!")
+            };
+        }
+
+        /// <summary>
+        /// 失败
+        /// </summary>
+        /// <param name="code"></param>
+        /// <param name="description"></param>
+        /// <returns></returns>
+        public static WebPortalDeResponse<TData> Failed(string? code = "0", string? description = "")
+        {
+            return new WebPortalDeResponse<TData>
+            {
+                code = code,
+                msg = (description ?? "接口调用失败!")
+            };
+        }
+    }
+}

+ 15 - 0
src/Hotline.Share/Dtos/WebPortal/WebPortalSendSmsModelDto.cs

@@ -0,0 +1,15 @@
+namespace Hotline.Share.Dtos.WebPortal
+{
+    public class WebPortalSendSmsModelDto
+    {
+        /// <summary>
+        /// 号码
+        /// </summary>
+        public string TelNum { get; set; }
+
+        /// <summary>
+        /// 验证码
+        /// </summary>
+        public string SmsCode { get; set; }
+    }
+}

+ 16 - 0
src/Hotline.Share/Dtos/WebPortal/WriteLettersSendSmsDto.cs

@@ -0,0 +1,16 @@
+namespace Hotline.Share.Dtos.WebPortal
+{
+    /// <summary>
+    /// 短信发送缓存设置
+    /// </summary>
+    public class WriteLettersSendSmsDto
+    {
+        public string MobileNum { get; set; }
+
+        public string SmsCode { get; set; }
+
+        public int SendCount { get; set; } = 0;
+
+        public DateTime AddTime { get; set; }
+    }
+}

+ 132 - 0
src/Hotline/WebPortal/WebFlowAccept.cs

@@ -0,0 +1,132 @@
+using SqlSugar;
+using XF.Domain.Repository;
+
+namespace Hotline.WebPortal
+{
+    /// <summary>
+    /// 门户写信
+    /// </summary>
+    public class WebFlowAccept : CreationEntity
+    {
+        /// <summary>
+        /// 门户网站注册用户
+        /// </summary>
+        [SugarColumn(ColumnDescription = "门户网站注册用户")]
+        public string? WebUserID { get; set; }
+
+        /// <summary>
+        /// 工单Id
+        /// </summary>
+        [SugarColumn(ColumnDescription = "工单Id")]
+        public string? OrderId { get; set; }
+
+        /// <summary>
+        /// 姓名
+        /// </summary>
+        [SugarColumn(ColumnDescription = "姓名")]
+        public string LKName { get; set; }
+
+        /// <summary>
+        /// 性别
+        /// </summary>
+        [SugarColumn(ColumnDescription = "性别")]
+        public string? Sex { get; set; }
+
+        /// <summary>
+        /// 是否保密
+        /// </summary>
+        [SugarColumn(ColumnDescription = "是否保密")]
+        public string? IsSecret { get; set; }
+
+        /// <summary>
+        /// 邮箱
+        /// </summary>
+        [SugarColumn(ColumnDescription = "邮箱")]
+        public string? Mail { get; set; }
+
+        /// <summary>
+        /// 身份证
+        /// </summary>
+        [SugarColumn(ColumnDescription = "身份证")]
+        public string? IDCard { get; set; }
+
+        /// <summary>
+        /// 电话
+        /// </summary>
+        [SugarColumn(ColumnDescription = "电话")]
+        public string Mobile { get; set; }
+
+        /// <summary>
+        /// 微博/微信账号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "微博/微信账号")]
+        public string? AccountNum { get; set; }
+
+        /// <summary>
+        /// 地址
+        /// </summary>
+        [SugarColumn(ColumnDescription = "地址")]
+        public string? Address { get; set; }
+
+        /// <summary>
+        /// 标题
+        /// </summary>
+        [SugarColumn(ColumnDescription = "标题")]
+        public string Title { get; set; }
+
+        /// <summary>
+        /// 信件内容
+        /// </summary>
+        [SugarColumn(ColumnDescription = "信件内容", ColumnDataType = "varchar(3000)")]
+        public string Content { get; set; }
+
+        /// <summary>
+        /// 来源方式
+        /// </summary>
+        [SugarColumn(ColumnDescription = "来源方式")]
+        public string? FromID { get; set; }
+
+        /// <summary>
+        /// 受理类型ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "受理类型ID")]
+        public string PurTypeID { get; set; }
+
+        /// <summary>
+        /// 受理类型名称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "受理类型名称")]
+        public string PurTypeName { get; set; }
+
+        /// <summary>
+        /// 热点类型ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "热点类型ID")]
+        public string? ConTypeID { get; set; }
+
+        /// <summary>
+        /// 信件编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "信件编号")]
+        public string Code { get; set; }
+
+        /// <summary>
+        /// 查询密码
+        /// </summary>
+        [SugarColumn(ColumnDescription = "查询密码")]
+        public string Pwd { get; set; }
+
+        /// <summary>
+        /// 来信IP
+        /// </summary>
+        [SugarColumn(ColumnDescription = "来信IP")]
+        public string? IP { get; set; }
+
+        /// <summary>
+        /// 微信Id
+        /// </summary>
+        [SugarColumn(ColumnDescription = "微信Id")]
+        public string? WXOpenid { get; set; }
+
+    }
+}

+ 95 - 0
src/Hotline/WebPortal/WebUserAuth.cs

@@ -0,0 +1,95 @@
+using SqlSugar;
+using XF.Domain.Repository;
+
+namespace Hotline.WebPortal
+{
+    public class WebUserAuth : CreationModificationEntity
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        public string WebGUID { get; set; }
+
+        /// <summary>
+        /// 网站用户ID
+        /// </summary>
+        [SugarColumn(ColumnDescription = "姓名")]
+        public string WebUserID { get; set; }
+
+        /// <summary>
+        /// id 号码
+        /// </summary>
+        [SugarColumn(ColumnDescription = "id 号码")]
+        public string? DataId { get; set; }
+
+        /// <summary>
+        /// 自然人
+        /// 法人
+        /// </summary>
+        [SugarColumn(ColumnDescription = "自然人,法人")]
+        public string? Type { get; set; }
+
+        /// <summary>
+        /// 地址
+        /// </summary>
+        [SugarColumn(ColumnDescription = "地址")]
+        public string? Address { get; set; }
+
+        /// <summary>
+        /// 生日
+        /// </summary>
+        [SugarColumn(ColumnDescription = "生日")]
+        public string? Birth { get; set; }
+
+        /// <summary>
+        /// 文档类型
+        /// </summary>
+        [SugarColumn(ColumnDescription = "文档类型")]
+        public string? DocumentType { get; set; }
+
+        /// <summary>
+        /// 身份证号码
+        /// </summary>
+        [SugarColumn(ColumnDescription = "身份证号码")]
+        public string? IdNumber { get; set; }
+
+        /// <summary>
+        /// 未知
+        /// </summary>
+        [SugarColumn(ColumnDescription = "未知")]
+        public string? Nationality { get; set; }
+
+        /// <summary>
+        /// 姓名
+        /// </summary>
+        [SugarColumn(ColumnDescription = "姓名")]
+        public string? Name { get; set; }
+
+        /// <summary>
+        /// 性别
+        /// 1-男
+        /// 2-女
+        /// </summary>
+        [SugarColumn(ColumnDescription = " 性别 1-男,2-女")]
+        public string? Sex { get; set; }
+
+        /// <summary>
+        /// 昵称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "昵称")]
+        public string? DisplayName { get; set; }
+
+        /// <summary>
+        /// 登陆名
+        /// </summary>
+        [SugarColumn(ColumnDescription = "登陆名")]
+        public string? Username { get; set; }
+
+        /// <summary>
+        /// 手机号码
+        /// </summary>
+        [SugarColumn(ColumnDescription = "手机号码")]
+        public string? EmployeeMobile { get; set; }
+
+    }
+}

+ 95 - 0
src/Hotline/WebPortal/WebUserRegister.cs

@@ -0,0 +1,95 @@
+using SqlSugar;
+using XF.Domain.Repository;
+
+namespace Hotline.WebPortal
+{
+    /// <summary>
+    /// 门户网站注册用户
+    /// </summary>
+    public class WebUserRegister : CreationModificationEntity
+    {
+        /// <summary>
+        /// 姓名
+        /// </summary>
+        [SugarColumn(ColumnDescription = "姓名")]
+        public string WebUserName { get; set; }
+
+        /// <summary>
+        /// 登录名称
+        /// </summary>
+        [SugarColumn(ColumnDescription = "登录名称")]
+        public string WebLoginName { get; set; }
+
+        /// <summary>
+        /// 电话号码
+        /// </summary>
+        [SugarColumn(ColumnDescription = "电话号码")]
+        public string? PhoneNum { get; set; }
+
+        /// <summary>
+        /// 登陆密码
+        /// </summary>
+        [SugarColumn(ColumnDescription = "登陆密码")]
+        public string? LoginPwd { get; set; }
+
+        /// <summary>
+        /// 性别
+        /// </summary>
+        [SugarColumn(ColumnDescription = "性别")]
+        public string? Gender { get; set; }
+
+        /// <summary>
+        /// 身份证号码
+        /// </summary>
+        [SugarColumn(ColumnDescription = "身份证号码")]
+        public string? IDCard { get; set; }
+
+        /// <summary>
+        /// 区域编号
+        /// </summary>
+        [SugarColumn(ColumnDescription = "区域编号")]
+        public string? AreaID { get; set; }
+
+        /// <summary>
+        /// 的址
+        /// </summary>
+        [SugarColumn(ColumnDescription = "的址")]
+        public string? Address { get; set; }
+
+        /// <summary>
+        /// 邮箱
+        /// </summary>
+        [SugarColumn(ColumnDescription = "邮箱")]
+        public string? Mail { get; set; }
+
+        /// <summary>
+        /// 注册IP
+        /// </summary>
+        [SugarColumn(ColumnDescription = "注册IP")]
+        public string? RegIP { get; set; }
+
+        /// <summary>
+        /// 注册时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "注册时间")]
+        public DateTime? RegDate { get; set; }
+
+        /// <summary>
+        /// 状态
+        /// </summary>
+        [SugarColumn(ColumnDescription = "状态")]
+        public string? StateFlag { get; set; }
+
+        /// <summary>
+        /// 登录IP
+        /// </summary>
+        [SugarColumn(ColumnDescription = "登录IP")]
+        public string? LastIP { get; set; }
+
+        /// <summary>
+        /// 登录时间
+        /// </summary>
+        [SugarColumn(ColumnDescription = "登录时间")]
+        public DateTime? LastDate { get; set; }
+    }
+}

+ 5 - 0
src/XF.Domain/Constants/SettingConstants.cs

@@ -216,5 +216,10 @@ namespace XF.Domain.Constants
         /// 外呼分机组号
         /// </summary>
         public const string CallOutQueueId = "CallOutQueueId";
+
+        /// <summary>
+        /// 门户网站主题颜色
+        /// </summary>
+        public const string WebSystemSettingsTheme = "WebSystemSettingsTheme";
     }
 }