问题:SQL 硬编码物理库名(bigdata./glms_bigdata.),关联库在不同环境库名不同,换环境即失效;
且仓库所有 appsettings 均未配 bigdata 连接串,本地/单测环境一直走 Unavailable 降级。
- 新增 YLErpDAL/DataBase/DbSchema:从连接串 database= 解析物理库名(含反引号、\w+ 白名单校验、进程级缓存),
跨库 SQL 写 DbSchema.Of("bigdata").mf_fundarchives ——代码只认逻辑连接名(等价 Java @Mapper 指定数据源),
物理库名归各环境 appsettings;将来 join ERP 主库用 DbSchema.Of("ylcms") 同法插值
- FundManagerLookupService:SQL 两处库名前缀改为插值(查找逻辑/CONVERT/COLLATE 不变),Unavailable 降级语义不变
- appsettings.local.json / UnitTestProject appsettings.json 补 bigdata=192.168.2.96:3306/glms_bigdata(抄现有 96 库连接串改库名)
- 新增 FundManagerLookupServiceTest(连 96 实库):DbSchema 解析断言 + Lookup 连通性
实测 511160.SH → Unique「东财基金管理有限公司」全链路打通;161210/630006 因 mf_investadvisoroutline 仅2行无映射返回 NotFound(数据覆盖问题)
100 lines
4.1 KiB
C#
100 lines
4.1 KiB
C#
using Dapper;
|
|
using MySqlConnector;
|
|
using YieldChain.Helpers;
|
|
using YLErp.BLL;
|
|
|
|
namespace YLErp.Modules.UnderlyingModule
|
|
{
|
|
public enum FundManagerLookupStatus
|
|
{
|
|
NotFound,
|
|
Unique,
|
|
Multiple,
|
|
Unavailable
|
|
}
|
|
|
|
public sealed class FundManagerLookupResult
|
|
{
|
|
public FundManagerLookupStatus Status { get; init; }
|
|
public string InvestAdvisorName { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// 查询上游基金档案中的基金管理人。上游不可用时返回降级结果,不阻断页面编辑。
|
|
/// </summary>
|
|
public sealed class FundManagerLookupService
|
|
{
|
|
private sealed class FundManagerRow
|
|
{
|
|
public string InvestAdvisorCode { get; set; }
|
|
public string InvestAdvisorName { get; set; }
|
|
}
|
|
|
|
//物理库名由连接串 database= 决定(DbSchema 解析),各环境库名不同(如测试环境 glms_bigdata),
|
|
//SQL 不硬编码库名;连接开在 bigdata 数据源上,将来跨库 join ERP 主库时用 DbSchema.Of("ylcms") 同法插值
|
|
private string BuildLookupSql()
|
|
{
|
|
return $@"
|
|
SELECT
|
|
ia.investadvisorcode AS InvestAdvisorCode,
|
|
ia.investadvisorname AS InvestAdvisorName
|
|
FROM {DbSchema.Of("bigdata")}.mf_fundarchives AS fa
|
|
INNER JOIN {DbSchema.Of("bigdata")}.mf_investadvisoroutline AS ia
|
|
ON CONVERT(fa.investadvisorcode USING utf8mb4) COLLATE utf8mb4_unicode_ci =
|
|
CONVERT(ia.investadvisorcode USING utf8mb4) COLLATE utf8mb4_unicode_ci
|
|
WHERE CONVERT(fa.secucode USING utf8mb4) COLLATE utf8mb4_unicode_ci =
|
|
CONVERT(TRIM(SUBSTRING_INDEX(@UnderlyingCode, '.', 1)) USING utf8mb4) COLLATE utf8mb4_unicode_ci";
|
|
}
|
|
|
|
public FundManagerLookupResult Lookup(string underlyingCode)
|
|
{
|
|
var normalizedCode = NormalizeCode(underlyingCode);
|
|
if (string.IsNullOrEmpty(normalizedCode))
|
|
{
|
|
return new FundManagerLookupResult { Status = FundManagerLookupStatus.NotFound };
|
|
}
|
|
|
|
var connectionString = AppManager.GetConnectionString("bigdata");
|
|
if (string.IsNullOrWhiteSpace(connectionString))
|
|
{
|
|
return new FundManagerLookupResult { Status = FundManagerLookupStatus.Unavailable };
|
|
}
|
|
|
|
try
|
|
{
|
|
using var connection = new MySqlConnection(connectionString);
|
|
var matches = connection.Query<FundManagerRow>(BuildLookupSql(), new { UnderlyingCode = normalizedCode }, commandTimeout: 10)
|
|
.Where(row => !string.IsNullOrWhiteSpace(row.InvestAdvisorName))
|
|
.GroupBy(row => (row.InvestAdvisorCode ?? string.Empty).Trim(), StringComparer.OrdinalIgnoreCase)
|
|
.Select(group => group.Select(row => row.InvestAdvisorName.Trim()).Distinct(StringComparer.OrdinalIgnoreCase).ToArray())
|
|
.Where(names => names.Length > 0)
|
|
.ToArray();
|
|
|
|
return matches.Length switch
|
|
{
|
|
0 => new FundManagerLookupResult { Status = FundManagerLookupStatus.NotFound },
|
|
1 when matches[0].Length == 1 => new FundManagerLookupResult { Status = FundManagerLookupStatus.Unique, InvestAdvisorName = matches[0][0] },
|
|
_ => new FundManagerLookupResult { Status = FundManagerLookupStatus.Multiple }
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogFactory.GetLogger<FundManagerLookupService>().Error("查询基金管理人失败,代码:" + normalizedCode, ex);
|
|
return new FundManagerLookupResult { Status = FundManagerLookupStatus.Unavailable };
|
|
}
|
|
}
|
|
|
|
public static string NormalizeCode(string underlyingCode)
|
|
{
|
|
var trimmed = underlyingCode?.Trim();
|
|
if (string.IsNullOrEmpty(trimmed))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var dotIndex = trimmed.IndexOf('.');
|
|
return (dotIndex < 0 ? trimmed : trimmed.Substring(0, dotIndex)).TrimToNull();
|
|
}
|
|
}
|
|
}
|