itfx/src/InterfaceForward.Application/Services/InterfaceMap/InterfaceMapPublishService.cs
2026-06-12 14:15:54 +08:00

239 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}