250 lines
11 KiB
C#
250 lines
11 KiB
C#
using System.ComponentModel.DataAnnotations;
|
||
using DiffPlex;
|
||
using DiffPlex.DiffBuilder;
|
||
using DiffPlex.DiffBuilder.Model;
|
||
using Fake;
|
||
using Newtonsoft.Json;
|
||
using Newtonsoft.Json.Converters;
|
||
using InterfaceForward.Application.Contracts.Dtos.Interface;
|
||
using InterfaceForward.Application.Helpers;
|
||
using InterfaceForward.Domain.Shared.Dtos;
|
||
using InterfaceForward.Domain.Shared.Helpers;
|
||
using InterfaceForward.Repositories;
|
||
using InterfaceForward.Repositories.Interface.Entitys;
|
||
using InterfaceForward.Repositories.Interface.Services;
|
||
using InterfaceForward.Repositories.Interface.ValueObjects;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
|
||
namespace InterfaceForward.Application.Services.InterfaceMap;
|
||
|
||
/// <summary>
|
||
/// 接口映射发布修改
|
||
/// </summary>
|
||
[ApiExplorerSettings(GroupName = "接口映射服务")]
|
||
public class InterfaceMapPublishService : ApplicationService
|
||
{
|
||
private readonly static JsonSerializerSettings Settings = new JsonSerializerSettings
|
||
{
|
||
Formatting = Formatting.Indented, // 设置格式化输出
|
||
Converters = { new StringEnumConverter() },
|
||
ContractResolver = new CustomContractResolver() // 设置自定义的ContractResolver
|
||
};
|
||
|
||
private readonly InterfaceForwardQuery _interfaceForwardQuery;
|
||
private readonly IInterfaceMapPublishRepository _interfaceMapPublishRepository;
|
||
private readonly IInterfaceMapRepository _interfaceMapRepository;
|
||
|
||
public InterfaceMapPublishService(IInterfaceMapPublishRepository interfaceMapPublishRepository,
|
||
InterfaceForwardQuery interfaceForwardQuery, IInterfaceMapRepository interfaceMapRepository)
|
||
{
|
||
_interfaceMapPublishRepository = interfaceMapPublishRepository;
|
||
_interfaceForwardQuery = interfaceForwardQuery;
|
||
_interfaceMapRepository = interfaceMapRepository;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取接口映射 发布记录列表
|
||
/// </summary>
|
||
/// <param name="interfaceId">系统接口id</param>
|
||
/// <param name="serviceProviderId">服务商id</param>
|
||
/// <param name="pageIndex"></param>
|
||
/// <param name="pageSize"></param>
|
||
/// <returns></returns>
|
||
[HttpGet("InterfaceMap/GetPublishedList")]
|
||
public async Task<List<InterfaceMapPublishedOutPutVO>> GetPublishedListAsync([Required] int interfaceId,
|
||
[Required] int serviceProviderId, int pageIndex = 1, int pageSize = 50)
|
||
{
|
||
return await _interfaceMapPublishRepository.GetPublishedListAsync(interfaceId, serviceProviderId, pageIndex,
|
||
pageSize);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取接口映射 发布对照数据
|
||
/// </summary>
|
||
/// <param name="interfaceCode">系统接口code</param>
|
||
/// <param name="serviceProviderId">服务商id</param>
|
||
/// <param name="version">要比对的版本,如果不指定就是比对数据库最新数据</param>
|
||
/// <returns></returns>
|
||
[HttpGet("InterfaceMap/GetPublishCompare")]
|
||
public async Task<InterfaceMapPublishedDifferenceVO> GetPublishCompareAsync([Required] string interfaceCode,
|
||
[Required] int serviceProviderId, string version)
|
||
{
|
||
var systemInterface = await _interfaceForwardQuery.GetInterfaceByCodeAsync(interfaceCode);
|
||
if (systemInterface == null) throw new BusinessException(message: "系统接口不存在");
|
||
|
||
var serviceProvider = await _interfaceForwardQuery.GetServiceProviderByIdAsync(serviceProviderId);
|
||
if (serviceProvider == null) throw new BusinessException(message: "服务商不存在");
|
||
|
||
// 没有指定比对的版本,就是比对数据库最新数据
|
||
string dbContent = version.IsNullOrWhiteSpace()
|
||
? JsonConvert.SerializeObject(
|
||
await _interfaceForwardQuery.BuildForwardCoreContext(systemInterface, serviceProvider),
|
||
Settings)
|
||
: await _interfaceMapPublishRepository.GetFirstAsync(a =>
|
||
a.InterfaceId == systemInterface.Id && a.ServiceProviderId == serviceProviderId &&
|
||
a.PublishedVersion == version, a => a.PublishedContent);
|
||
|
||
var cacheKey = InterfaceForwardCommon.GetContextCacheKey(systemInterface.Code, serviceProvider.Code);
|
||
var cache = (await RedisHelper.Client.GetAsync<string>(cacheKey)).ToObject<ForwardCoreContextCache>();
|
||
string redisContent = JsonConvert.SerializeObject(cache, Settings);
|
||
|
||
|
||
var differenceContent = CompareTexts(redisContent, dbContent ?? "");
|
||
|
||
return new InterfaceMapPublishedDifferenceVO
|
||
{
|
||
PublishedContent = redisContent,
|
||
DbContent = dbContent,
|
||
DifferenceContent = differenceContent
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发布映射
|
||
/// </summary>
|
||
/// <param name="input"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="BusinessException"></exception>
|
||
[HttpPost("InterfaceMap/Publish")]
|
||
public async Task<bool> PublishAsync(InterfaceMapPublishInput input)
|
||
{
|
||
var systemInterface = await _interfaceForwardQuery.GetInterfaceByCodeAsync(input.InterfaceCode);
|
||
if (systemInterface == null) throw new BusinessException(message: "系统接口不存在");
|
||
|
||
var serviceProvider = await _interfaceForwardQuery.GetServiceProviderByIdAsync(input.ServiceProviderId);
|
||
if (serviceProvider == null) throw new BusinessException(message: "服务商不存在");
|
||
|
||
// 确认比对期间数据是否被篡改
|
||
var differ = new Differ();
|
||
var inlineBuilder = new InlineDiffBuilder(differ);
|
||
var context = await _interfaceForwardQuery.BuildForwardCoreContext(systemInterface, serviceProvider);
|
||
var content = JsonConvert.SerializeObject(context, Settings);
|
||
var diffResult = inlineBuilder.BuildDiffModel(content, input.DBContent);
|
||
if (diffResult.HasDifferences) throw new BusinessException(message: "数据库已经发生变更,请重新获取最新数据进行比对");
|
||
|
||
var mapsEntities = await _interfaceMapRepository.GetListAsync(x =>
|
||
x.UpStreamId == systemInterface.Id && x.ServiceProviderId == serviceProvider.Id);
|
||
var publishEntity = new InterfaceMapPublishedEntity
|
||
{
|
||
InterfaceId = systemInterface.Id,
|
||
ServiceProviderId = input.ServiceProviderId,
|
||
PublishedVersion = DateTime.Now.ToString("yyyyMMddHHmmss"),
|
||
PublishedContent = content,
|
||
PrePublishedVersion = mapsEntities.First().PublishedVersion,
|
||
DifferenceContent = input.DifferenceContent,
|
||
IsPublished = true,
|
||
Description = input.Description
|
||
};
|
||
|
||
|
||
using var ts = TransacationHelper.GetReadCommitted();
|
||
await _interfaceMapPublishRepository.InsertAsync(publishEntity);
|
||
|
||
// 更新版本号
|
||
mapsEntities.ForEach(x => x.PublishedVersion = publishEntity.PublishedVersion);
|
||
await _interfaceMapRepository.UpdateRangeAsync(mapsEntities, x => x.PublishedVersion);
|
||
|
||
// update cache
|
||
var redisKey = InterfaceForwardCommon.GetContextCacheKey(systemInterface.Code, serviceProvider.Code);
|
||
await RedisHelper.Client.SetAsync(redisKey, content, InterfaceForwardCommon.ContextCacheTtl);
|
||
ts.Complete();
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发布回滚
|
||
/// </summary>
|
||
/// <param name="input"></param>
|
||
/// <returns></returns>
|
||
/// <exception cref="BusinessException"></exception>
|
||
[HttpPost("InterfaceMap/PublishRollback")]
|
||
public async Task<bool> PublishRollbackAsync(InterfaceMapRollbackInput input)
|
||
{
|
||
var systemInterface = await _interfaceForwardQuery.GetInterfaceByCodeAsync(input.InterfaceCode);
|
||
if (systemInterface == null) throw new BusinessException(message: "系统接口不存在");
|
||
var serviceProvider = await _interfaceForwardQuery.GetServiceProviderByIdAsync(input.ServiceProviderId);
|
||
if (serviceProvider == null) throw new BusinessException(message: "服务商不存在");
|
||
|
||
var mapsEntities = await _interfaceMapRepository.GetListAsync(x =>
|
||
x.UpStreamId == systemInterface.Id && x.ServiceProviderId == input.ServiceProviderId);
|
||
if (mapsEntities == null || mapsEntities.Count == 0)
|
||
{
|
||
throw new BusinessException(message: "映射不存在");
|
||
}
|
||
|
||
var targetVersionContent = await _interfaceMapPublishRepository.GetFirstAsync(a =>
|
||
a.InterfaceId == systemInterface.Id && a.ServiceProviderId == input.ServiceProviderId &&
|
||
a.PublishedVersion == input.Version, a => a.PublishedContent);
|
||
// tips:空是合法的,虽然在业务上不存在
|
||
if (targetVersionContent == null) throw new BusinessException(message: "要回滚的版本不存在");
|
||
|
||
// tips:回滚是缓存覆盖行为,不考虑脏数据问题
|
||
var redisKey = InterfaceForwardCommon.GetContextCacheKey(input.InterfaceCode, serviceProvider.Code);
|
||
var cache = (await RedisHelper.Client.GetAsync<string>(redisKey)).ToObject<ForwardCoreContextCache>();
|
||
var redisContent = JsonConvert.SerializeObject(cache, Settings);
|
||
var differenceContent = CompareTexts(redisContent, targetVersionContent);
|
||
|
||
var publishEntity = new InterfaceMapPublishedEntity
|
||
{
|
||
InterfaceId = systemInterface.Id,
|
||
ServiceProviderId = input.ServiceProviderId,
|
||
PublishedVersion = DateTime.Now.ToString("yyyyMMddHHmmss"),
|
||
PublishedContent = targetVersionContent,
|
||
PrePublishedVersion = mapsEntities.First().PublishedVersion,
|
||
DifferenceContent = differenceContent,
|
||
IsPublished = false,
|
||
Description = input.Description
|
||
};
|
||
|
||
using var ts = TransacationHelper.GetReadCommitted();
|
||
|
||
await _interfaceMapPublishRepository.InsertAsync(publishEntity);
|
||
|
||
// 更新版本号
|
||
mapsEntities.ForEach(x => x.PublishedVersion = publishEntity.PublishedVersion);
|
||
await _interfaceMapRepository.UpdateRangeAsync(mapsEntities, x => x.PublishedVersion);
|
||
|
||
await RedisHelper.Client.SetAsync(redisKey, publishEntity.PublishedContent,
|
||
InterfaceForwardCommon.ContextCacheTtl);
|
||
|
||
ts.Complete();
|
||
|
||
return true;
|
||
}
|
||
|
||
private static string CompareTexts(string oldText, string newText)
|
||
{
|
||
var differ = new Differ();
|
||
var inlineBuilder = new InlineDiffBuilder(differ);
|
||
var diffResult = inlineBuilder.BuildDiffModel(oldText, newText);
|
||
|
||
var diffOutput = new List<string>();
|
||
|
||
foreach (var line in diffResult.Lines)
|
||
{
|
||
switch (line.Type)
|
||
{
|
||
case ChangeType.Inserted:
|
||
diffOutput.Add("+ " + line.Text);
|
||
break;
|
||
case ChangeType.Deleted:
|
||
diffOutput.Add("- " + line.Text);
|
||
break;
|
||
case ChangeType.Modified:
|
||
diffOutput.Add("* " + line.Text);
|
||
break;
|
||
case ChangeType.Unchanged:
|
||
diffOutput.Add(" " + line.Text);
|
||
break;
|
||
case ChangeType.Imaginary:
|
||
diffOutput.Add("? " + line.Text);
|
||
break;
|
||
default:
|
||
throw new ArgumentOutOfRangeException();
|
||
}
|
||
}
|
||
|
||
return string.Join(Environment.NewLine, diffOutput);
|
||
}
|
||
} |