239 lines
10 KiB
C#
239 lines
10 KiB
C#
using System.ComponentModel.DataAnnotations;
|
||
using DiffPlex;
|
||
using DiffPlex.DiffBuilder;
|
||
using DiffPlex.DiffBuilder.Model;
|
||
using InterfaceForward.Application.Contracts.Dtos.Interface;
|
||
using InterfaceForward.Application.Helpers;
|
||
using InterfaceForward.Repositories;
|
||
using InterfaceForward.Repositories.Interface.Entitys;
|
||
using InterfaceForward.Repositories.Interface.Services;
|
||
using InterfaceForward.Repositories.Interface.ValueObjects;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Newtonsoft.Json;
|
||
using Newtonsoft.Json.Converters;
|
||
|
||
namespace InterfaceForward.Application.Services.InterfaceMap;
|
||
|
||
/// <summary>
|
||
/// 接口映射发布修改
|
||
/// </summary>
|
||
[ApiExplorerSettings(GroupName = "接口映射服务")]
|
||
public class InterfaceMapPublishService(
|
||
IInterfaceMapPublishRepository interfaceMapPublishRepository,
|
||
InterfaceForwardQuery interfaceForwardQuery,
|
||
IInterfaceMapRepository interfaceMapRepository)
|
||
: ApplicationService
|
||
{
|
||
private static readonly JsonSerializerSettings Settings = new()
|
||
{
|
||
Formatting = Formatting.Indented, // 设置格式化输出
|
||
Converters = { new StringEnumConverter() },
|
||
ContractResolver = new CustomContractResolver() // 设置自定义的ContractResolver
|
||
};
|
||
|
||
/// <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("系统接口不存在");
|
||
|
||
var serviceProvider = await interfaceForwardQuery.GetServiceProviderByIdAsync(serviceProviderId);
|
||
if (serviceProvider == null) throw new BusinessException("服务商不存在");
|
||
|
||
// 没有指定比对的版本,就是比对数据库最新数据
|
||
var 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?? String.Empty);
|
||
|
||
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("系统接口不存在");
|
||
|
||
var serviceProvider = await interfaceForwardQuery.GetServiceProviderByIdAsync(input.ServiceProviderId);
|
||
if (serviceProvider == null) throw new BusinessException("服务商不存在");
|
||
|
||
// 确认比对期间数据是否被篡改
|
||
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("数据库已经发生变更,请重新获取最新数据进行比对");
|
||
|
||
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.DefaultCacheTtl);
|
||
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("系统接口不存在");
|
||
var serviceProvider = await interfaceForwardQuery.GetServiceProviderByIdAsync(input.ServiceProviderId);
|
||
if (serviceProvider == null) throw new BusinessException("服务商不存在");
|
||
|
||
var mapsEntities = await interfaceMapRepository.GetListAsync(x =>
|
||
x.UpStreamId == systemInterface.Id && x.ServiceProviderId == input.ServiceProviderId);
|
||
if (mapsEntities == null || mapsEntities.Count == 0)
|
||
{
|
||
throw new BusinessException("映射不存在");
|
||
}
|
||
|
||
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("要回滚的版本不存在");
|
||
|
||
// 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.DefaultCacheTtl);
|
||
|
||
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);
|
||
}
|
||
} |