using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Linq.Expressions;
|
using System.Text.RegularExpressions;
|
using System.Threading.Tasks;
|
using CommonHelper.Extension;
|
using RiskControl.NewService.Entity;
|
using RiskControl.NewService.Extension;
|
using RiskControl.NewService.Extension.Params;
|
using SqlSugar;
|
using CommonHelper;
|
using RiskControl.NewService.ViewModel;
|
|
namespace RiskControl.NewService.Service
|
{
|
public class SelfReportService: CrBaseService<CreditRatingReportSelf>
|
{
|
/// <summary>
|
/// 拼接主体名称模糊查询条件
|
/// </summary>
|
/// <typeparam name="T">CreditRatingReportSelf/CreditRatingReportOutside</typeparam>
|
/// <param name="companys">公司名称</param>
|
/// <returns></returns>
|
public async Task<Expression<Func<T, bool>>> ComposeCompanyWhere<T>(string[] companys) where T : BaseCreditRating ,new ()
|
{
|
Expression<Func<T, bool>> comLike = null;
|
foreach (var company in companys)
|
{
|
// todo 主体公司名称包含,数据完整后,判断条件应该是“=”
|
Expression<Func<T, bool>> thisLike = o => o.MainCompany.Contains(company.Trim());
|
if (comLike == null)
|
comLike = thisLike;
|
else
|
{
|
comLike = comLike.Or(thisLike);
|
}
|
}
|
|
return comLike;
|
}
|
|
/// <summary>
|
/// 获取评级报告分页
|
/// </summary>
|
/// <typeparam name="T">CreditRatingReportSelf/CreditRatingReportOutside</typeparam>
|
/// <param name="param">搜索参数</param>
|
/// <param name="companyNames">groupId、projId匹配的主体名称</param>
|
/// <returns></returns>
|
public async Task<Page<T>> GetPages<T>(ReportSearchParam param,string[] companyNames=null) where T : BaseCreditRating
|
{
|
DateTime startDateTime;
|
DateTime endDateTime;
|
|
// 1. groupId&projId匹配的主体名称
|
Expression<Func<T, bool>> comLike =null;
|
if(companyNames!=null)
|
{
|
foreach (var company in companyNames)
|
{
|
// todo 主体公司名称包含,数据完整后,判断条件应该是“=”
|
Expression<Func<T, bool>> thisLike = o => o.MainCompany.Contains(company.Trim());
|
if (comLike == null)
|
comLike = thisLike;
|
else
|
comLike = comLike.Or(thisLike);
|
}
|
}
|
// 4. 主体名称(模糊搜索)
|
if (!string.IsNullOrWhiteSpace(param.MainCompany))
|
comLike = comLike == null
|
? (o => o.MainCompany.Contains(param.MainCompany))
|
: comLike.Or(o => o.MainCompany.Contains(param.MainCompany));
|
|
var cWhere = comLike ?? (o => true);
|
|
// 2. 报告名称
|
if (!string.IsNullOrWhiteSpace(param.Title))
|
cWhere = cWhere.And(o => o.Title.Contains(param.Title));
|
// 3. 评级时间
|
if (DateTime.TryParse(param.StartDate, out startDateTime))
|
cWhere = cWhere.And(o => o.RatingDate >= startDateTime);
|
if (DateTime.TryParse(param.EndDate, out endDateTime))
|
cWhere = cWhere.And(o => o.RatingDate <= endDateTime);
|
// 5. 主体级别
|
if (!string.IsNullOrWhiteSpace(param.MainLevel))
|
cWhere = cWhere.And(o => o.MainLevel==param.MainLevel);
|
// 6. 展望
|
if (!string.IsNullOrWhiteSpace(param.Expectation))
|
cWhere = cWhere.And(o => o.Expectation.Contains(param.Expectation));
|
// 7. 债券级别
|
if (!string.IsNullOrWhiteSpace(param.DebtLevel))
|
cWhere = cWhere.And(o => o.DebtLevel==param.DebtLevel);
|
// 8. 评级公司
|
if (!string.IsNullOrWhiteSpace(param.RatingCompany))
|
cWhere = cWhere.And(o => o.RatingCompany.Contains(param.RatingCompany));
|
// 8. 评级类型
|
if (!string.IsNullOrWhiteSpace(param.RatingType))
|
cWhere = cWhere.And(o => o.RatingType == param.RatingType);
|
|
if (param.PageIndex < 1)
|
param.PageIndex = 1;
|
|
var page = await CreditRatingDb.Queryable<T>().Where(cWhere)
|
.OrderBy(param.OrderField + (param.OrderType == OrderByEnmu.Asc ? " asc" : " desc"))
|
.ToPageAsync(param.PageIndex, param.PageSize);
|
return page;
|
|
}
|
|
/// <summary>
|
/// 获取pdf的相对路径
|
/// </summary>
|
/// <param name="selfIds">自建报告</param>
|
/// <param name="outIds">外部报告</param>
|
/// <returns></returns>
|
public async Task<List<string>> GetPdfLocal(List<int> selfIds, List<int> outIds)
|
{
|
var pdfs = new List<string>();
|
if (selfIds.Any())
|
{
|
var urls = CreditRatingDb.Queryable<CreditRatingReportSelf>().Where(o => selfIds.Contains(o.Id)).Select<string>(o => o.PdfLocalPath).ToList();
|
pdfs.AddRange(urls);
|
}
|
if (outIds.Any())
|
{
|
var urls = CreditRatingDb.Queryable<CreditRatingReportOutside>().Where(o => outIds.Contains(o.Id)).Select<string>(o => o.PdfLocalPath).ToList();
|
pdfs.AddRange(urls);
|
}
|
|
return pdfs.Distinct().ToList();
|
}
|
|
/// <summary>
|
/// 获取某天的评级预警
|
/// </summary>
|
/// <param name="companyName">主体公司</param>
|
/// <param name="endRatingDate">评级日期,默认当天</param>
|
/// <returns></returns>
|
public async Task<List<RateWarningResult>> GetWarning(string companyName,DateTime? endRatingDate = null)
|
{
|
var warnList = new List<RateWarningResult>();
|
|
var endRateTime = endRatingDate ?? DateTime.Today;
|
// 1.获取主体的最新评级报告
|
var repList = await CreditRatingDb.Queryable<CreditRatingReportOutside>()
|
.Where(a => a.MainCompany == companyName)
|
.Where(a=>SqlFunc.DateIsSame(a.RatingDate,endRateTime))
|
.ToListAsync();
|
// 1.1 没有最新的评级报告,直接返回
|
if (!repList.Any())
|
return warnList;
|
foreach (var rep1 in repList)
|
{
|
var item = rep1.MapTo<RateWarningResult>();
|
item.WarningInfo.Main = await GetMainWarningLevel(rep1);
|
item.WarningInfo.Debt = await GetDebtWarningLevel(rep1);
|
warnList.Add(item);
|
}
|
return warnList;
|
}
|
/// <summary>
|
/// 获取主体级别差
|
/// </summary>
|
/// <returns></returns>
|
private async Task<RateWarningLevel> GetMainWarningLevel(CreditRatingReportOutside report)
|
{
|
List<string> Message = new List<string>();
|
var level = new RateWarningLevel();
|
var mainRateArr1 = new List<string>() {"AAA", "AA", "A", "BBB", "BB", "B", "CCC", "CC", "C"};
|
var mainRateArr2 = new List<string>() {"MQ1", "MQ2", "MQ3", "MQ4", "MQ5"};
|
var regAbc = new Regex("A|B|C|MQ");
|
// 1.规则1,主体评级有内容才去匹配
|
if (regAbc.IsMatch(report.MainLevel))
|
{
|
// 评级的主体部分 AA/B/MQ1
|
var rep1MainPart = new Regex(@"[ABC|MQ1-5]+").Match(report.MainLevel).Groups?[0]?.Value.ToUpper();
|
// 2.找每个最新(当日)报告的次新报告
|
/* 条件:1.主体一致
|
2.评级公司一致
|
3.评级标尺一致
|
4.时间=最新评级时间的上一次 */
|
// 2.1 判断评级标尺 暂定除去 A/B/C/+/-/数字,剩下的字母可判断标尺类型
|
// 如 MQ/i/pi/s/r/sf
|
report.MainLevel = report.MainLevel.Trim();
|
var mlChar = new Regex(@"A|B|C|0-9|\+|-|\s", RegexOptions.IgnoreCase).Replace(report.MainLevel, "");
|
// 2.2 次新报告
|
var rep2 = await CreditRatingDb.Queryable<CreditRatingReportOutside>()
|
.Where(a => a.MainCompany == report.MainCompany && a.RatingCompany == report.RatingCompany)
|
// 如果mlchar="",说明 MainLevel 只包含且仅包含字母abc+-
|
.WhereIF(mlChar.IsNullOrEmpty(),
|
a => a.MainLevel.Contains("[a-c]") && a.MainLevel.Contains("[+-]") &&
|
!a.MainLevel.Contains("[^a|^b|^c|^+|^-]"))
|
// MQ 开头
|
.WhereIF(mlChar.ToUpper() == "MQ", a => a.MainLevel.StartsWith("MQ"))
|
// 其它字母 是结束,ABC+-紧接mlchar,以区别 AAi 和 AApi
|
.WhereIF(!mlChar.IsNullOrEmpty() && mlChar.ToUpper() != "MQ",
|
a => SqlFunc.Replace(SqlFunc.Replace(a.MainLevel, "+", ""), "-", "")
|
.Contains($"[abc+-]{mlChar}"))
|
.Where(a => a.RatingDate < report.RatingDate)
|
.OrderBy(a => a.RatingDate, OrderByType.Desc)
|
.FirstAsync();
|
|
// 3.规则1-4:如果没有次新,就看最新的评级,在AA/AApi/AAs/MQ2/aa级别以下(不含本级),则一律为高风险
|
if (rep2 == null)
|
{
|
if (mainRateArr1.IndexOf(rep1MainPart) > mainRateArr1.IndexOf("AA") ||
|
mainRateArr2.IndexOf(rep1MainPart) > mainRateArr2.IndexOf("MQ2"))
|
{
|
level.HighNum += 1;
|
Message.Add("主体级别差");
|
}
|
}
|
|
// 4.有次新报告
|
else
|
{
|
var rep2MainPart = new Regex(@"[ABC|MQ1-5]+").Match(rep2.MainLevel).Groups?[0]?.Value.ToUpper();
|
// 4.1 规则1-3-1,升级 (索引越小,评分越高)
|
if (mainRateArr1.IndexOf(rep1MainPart) < mainRateArr1.IndexOf(rep2MainPart) ||
|
mainRateArr2.IndexOf(rep1MainPart) < mainRateArr2.IndexOf(rep2MainPart))
|
{
|
// 但是最新还在AA/MQ2以下,中风险
|
if (mainRateArr1.IndexOf(rep1MainPart) > mainRateArr1.IndexOf("AA") ||
|
mainRateArr2.IndexOf(rep1MainPart) > mainRateArr2.IndexOf("MQ2"))
|
{
|
level.MidNum += 1;
|
Message.Add("主体级别上调但级别仍然差");
|
}
|
}
|
// 4.2 规则1-3-2,降级
|
else if (mainRateArr1.IndexOf(rep1MainPart) > mainRateArr1.IndexOf(rep2MainPart) ||
|
mainRateArr2.IndexOf(rep1MainPart) > mainRateArr2.IndexOf(rep2MainPart))
|
{
|
level.HighNum += 1;
|
Message.Add("主体级别下调");
|
}
|
// 4.3 规则1-3-3,没有变动
|
else
|
{
|
// 但是最新还在AA/MQ2以下,高风险
|
if (mainRateArr1.IndexOf(rep1MainPart) > mainRateArr1.IndexOf("AA") ||
|
mainRateArr2.IndexOf(rep1MainPart) > mainRateArr2.IndexOf("MQ2"))
|
{
|
level.HighNum += 1;
|
Message.Add("主体级别差");
|
}
|
}
|
|
}
|
}
|
else
|
{
|
Message.Add("主体级别为空");
|
}
|
// 2.规则2,展望内容带有“负面”、“观察”的关键词,则直接命中此条为高风险(红色)
|
if (report.Expectation.Contains("负面") || report.Expectation.Contains("观察"))
|
{
|
level.HighNum += 1;
|
Message.Add("展望负面或列入评级观察名单");
|
}
|
|
level.Message = Message.ToArray();
|
return level;
|
}
|
|
/// <summary>
|
/// 获取主体级别差
|
/// </summary>
|
/// <returns></returns>
|
private RateWarningLevel GetMainWarningLevelCom(CreditRatingReportOutside report)
|
{
|
List<string> Message = new List<string>();
|
var level = new RateWarningLevel();
|
var mainRateArr1 = new List<string>() { "AAA", "AA", "A", "BBB", "BB", "B", "CCC", "CC", "C" };
|
var mainRateArr2 = new List<string>() { "MQ1", "MQ2", "MQ3", "MQ4", "MQ5" };
|
var regAbc = new Regex("A|B|C|MQ");
|
// 1.规则1,主体评级有内容才去匹配
|
if (regAbc.IsMatch(report.MainLevel))
|
{
|
// 评级的主体部分 AA/B/MQ1
|
var rep1MainPart = new Regex(@"[ABC|MQ1-5]+").Match(report.MainLevel).Groups?[0]?.Value.ToUpper();
|
// 2.找每个最新(当日)报告的次新报告
|
/* 条件:1.主体一致
|
2.评级公司一致
|
3.评级标尺一致
|
4.时间=最新评级时间的上一次 */
|
// 2.1 判断评级标尺 暂定除去 A/B/C/+/-/数字,剩下的字母可判断标尺类型
|
// 如 MQ/i/pi/s/r/sf
|
report.MainLevel = report.MainLevel.Trim();
|
var mlChar = new Regex(@"A|B|C|0-9|\+|-|\s", RegexOptions.IgnoreCase).Replace(report.MainLevel, "");
|
// 2.2 次新报告
|
var rep2 = CreditRatingDb.Queryable<CreditRatingReportOutside>()
|
.Where(a => a.MainCompany == report.MainCompany && a.RatingCompany == report.RatingCompany)
|
// 如果mlchar="",说明 MainLevel 只包含且仅包含字母abc+-
|
.WhereIF(mlChar.IsNullOrEmpty(),
|
a => a.MainLevel.Contains("[a-c]") && a.MainLevel.Contains("[+-]") &&
|
!a.MainLevel.Contains("[^a|^b|^c|^+|^-]"))
|
// MQ 开头
|
.WhereIF(mlChar.ToUpper() == "MQ", a => a.MainLevel.StartsWith("MQ"))
|
// 其它字母 是结束,ABC+-紧接mlchar,以区别 AAi 和 AApi
|
.WhereIF(!mlChar.IsNullOrEmpty() && mlChar.ToUpper() != "MQ",
|
a => SqlFunc.Replace(SqlFunc.Replace(a.MainLevel, "+", ""), "-", "")
|
.Contains($"[abc+-]{mlChar}"))
|
.Where(a => a.RatingDate < report.RatingDate)
|
.OrderBy(a => a.RatingDate, OrderByType.Desc)
|
.First();
|
|
// 3.规则1-4:如果没有次新,就看最新的评级,在AA/AApi/AAs/MQ2/aa级别以下(不含本级),则一律为高风险
|
if (rep2 == null)
|
{
|
if (mainRateArr1.IndexOf(rep1MainPart) > mainRateArr1.IndexOf("AA") ||
|
mainRateArr2.IndexOf(rep1MainPart) > mainRateArr2.IndexOf("MQ2"))
|
level.HighNum += 1;
|
Message.Add("主体级别差");
|
}
|
|
// 4.有次新报告
|
else
|
{
|
var rep2MainPart = new Regex(@"[ABC|MQ1-5]+").Match(rep2.MainLevel).Groups?[0]?.Value.ToUpper();
|
// 4.1 规则1-3-1,升级 (索引越小,评分越高)
|
if (mainRateArr1.IndexOf(rep1MainPart) < mainRateArr1.IndexOf(rep2MainPart) ||
|
mainRateArr2.IndexOf(rep1MainPart) < mainRateArr2.IndexOf(rep2MainPart))
|
{
|
// 但是最新还在AA/MQ2以下,中风险
|
if (mainRateArr1.IndexOf(rep1MainPart) > mainRateArr1.IndexOf("AA") ||
|
mainRateArr2.IndexOf(rep1MainPart) > mainRateArr2.IndexOf("MQ2"))
|
level.MidNum += 1;
|
Message.Add("主体级别上调但级别仍然差");
|
}
|
// 4.2 规则1-3-2,降级
|
else if (mainRateArr1.IndexOf(rep1MainPart) > mainRateArr1.IndexOf(rep2MainPart) ||
|
mainRateArr2.IndexOf(rep1MainPart) > mainRateArr2.IndexOf(rep2MainPart))
|
{
|
level.HighNum += 1;
|
Message.Add("主体级别下调");
|
}
|
// 4.3 规则1-3-3,没有变动
|
else
|
{
|
// 但是最新还在AA/MQ2以下,高风险
|
if (mainRateArr1.IndexOf(rep1MainPart) > mainRateArr1.IndexOf("AA") ||
|
mainRateArr2.IndexOf(rep1MainPart) > mainRateArr2.IndexOf("MQ2"))
|
level.HighNum += 1;
|
Message.Add("主体级别差");
|
}
|
|
}
|
}
|
else
|
{
|
Message.Add("主体级别为空");
|
}
|
// 2.规则2,展望内容带有“负面”、“观察”的关键词,则直接命中此条为高风险(红色)
|
if (report.Expectation.Contains("负面") || report.Expectation.Contains("观察"))
|
{
|
level.HighNum += 1;
|
Message.Add("展望负面或列入评级观察名单");
|
}
|
|
level.Message = Message.ToArray();
|
return level;
|
}
|
|
/// <summary>
|
/// 获取债项评级差
|
/// </summary>
|
/// <param name="report"></param>
|
/// <returns></returns>
|
private async Task<RateWarningLevel> GetDebtWarningLevel(CreditRatingReportOutside report)
|
{
|
List<string> Message = new List<string>();
|
var level = new RateWarningLevel();
|
var lowArr = new List<string>() {"AAA", "AA", "A1"};
|
// 债项评级 主体部分
|
var mainPart = new Regex(@"(A-\d)|[A-D]+").Match(report.DebtLevel).Groups[0].Value.ToUpper();
|
// 1.规则3
|
if (mainPart=="")
|
Message.Add("债项级别为空");
|
else if (!lowArr.Contains(mainPart))
|
{
|
level.HighNum += 1;
|
Message.Add("债项级别差");
|
}
|
|
// 2.规则4,展望内容带有发展中/待决/观望的关键词,则直接命中此条为中风险(黄色)
|
if (report.Expectation.Contains("发展中") || report.Expectation.Contains("待决") ||
|
report.Expectation.Contains("观望"))
|
{
|
level.MidNum += 1;
|
Message.Add("展望为发展中/待决/观望");
|
}
|
|
// 3.规则5,报告名称,如果出现以下关键词,则直接命中此条,为高风险(红色)
|
// 停止评级/撤销评级/观察/下调/调降/降级/重整/清算/违约/资产处置/业绩预亏/无法支付/无法偿还
|
var hstr = new[] { "停止评级", "撤销评级", "观察", "下调", "调降", "降级", "重整", "清算", "违约", "资产处置", "业绩预亏", "无法支付", "无法偿还" };
|
foreach (var s in hstr)
|
{
|
if (report.Title.Contains(s))
|
{
|
level.HighNum += 1;
|
Message.Add("报告/公告内容负面");
|
break;
|
}
|
}
|
level.Message = Message.ToArray();
|
return level;
|
}
|
|
/// <summary>
|
/// 获取债项评级差
|
/// </summary>
|
/// <param name="report"></param>
|
/// <returns></returns>
|
private RateWarningLevel GetDebtWarningLevelCom(CreditRatingReportOutside report)
|
{
|
List<string> Message = new List<string>();
|
var level = new RateWarningLevel();
|
var lowArr = new List<string>() { "AAA", "AA", "A1" };
|
// 债项评级 主体部分
|
var mainPart = new Regex(@"(A-\d)|[A-D]+").Match(report.DebtLevel).Groups[0].Value.ToUpper();
|
// 1.规则3
|
if (mainPart == "")
|
Message.Add("债项级别为空");
|
else if (!lowArr.Contains(mainPart))
|
{
|
level.HighNum += 1;
|
Message.Add("债项级别差");
|
}
|
|
// 2.规则4,展望内容带有发展中/待决/观望的关键词,则直接命中此条为中风险(黄色)
|
if (report.Expectation.Contains("发展中") || report.Expectation.Contains("待决") ||
|
report.Expectation.Contains("观望"))
|
{
|
level.MidNum += 1;
|
Message.Add("展望为发展中/待决/观望");
|
}
|
|
// 3.规则5,报告名称,如果出现以下关键词,则直接命中此条,为高风险(红色)
|
// 停止评级/撤销评级/观察/下调/调降/降级/重整/清算/违约/资产处置/业绩预亏/无法支付/无法偿还
|
var hstr = new[] { "停止评级", "撤销评级", "观察", "下调", "调降", "降级", "重整", "清算", "违约", "资产处置", "业绩预亏", "无法支付", "无法偿还" };
|
foreach (var s in hstr)
|
{
|
if (report.Title.Contains(s))
|
{
|
level.HighNum += 1;
|
Message.Add("报告/公告内容负面");
|
break;
|
}
|
}
|
level.Message = Message.ToArray();
|
return level;
|
}
|
/// <summary>
|
/// 获取某天的评级预警--预警
|
/// </summary>
|
/// <param name="companyName"></param>
|
/// <param name="endRatingDate"></param>
|
/// <returns></returns>
|
public List<RateWarningResult> GetWarningCom(string companyName, DateTime? endRatingDate = null)
|
{
|
var warnList = new List<RateWarningResult>();
|
|
var endRateTime = endRatingDate ?? DateTime.Today;
|
// 1.获取主体的最新评级报告
|
var repList = CreditRatingDb.Queryable<CreditRatingReportOutside>()
|
.Where(a => a.MainCompany == companyName)
|
.Where(a => a.RatingDate >= endRateTime.AddYears(-3))
|
//.Where(a => SqlFunc.DateIsSame(a.RatingDate, endRateTime))
|
.ToList();
|
// 1.1 没有最新的评级报告,直接返回
|
if (!repList.Any())
|
return warnList;
|
foreach (var rep1 in repList)
|
{
|
var item = rep1.MapTo<RateWarningResult>();
|
item.WarningInfo.Main = GetMainWarningLevelCom(rep1);
|
item.WarningInfo.Debt = GetDebtWarningLevelCom(rep1);
|
warnList.Add(item);
|
}
|
return warnList;
|
}
|
}
|
}
|