This commit is contained in:
朱小炯 2025-06-28 09:51:49 +08:00
commit d5bf18d324
535 changed files with 37355 additions and 0 deletions

5
.gitattributes vendored Normal file
View File

@ -0,0 +1,5 @@
# 2010
*.txt -crlf
# 2020
*.txt text eol=lf

258
.gitignore vendored Normal file
View File

@ -0,0 +1,258 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
*.DS_Store
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
logs/
[Bb]in/
[Oo]bj/
results/
# Visual Studio 2015 cache/options directory
.vs/
.vscode/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
site/wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config
# Windows Store app package directory
AppPackages/
BundleArtifacts/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
!idsrv3test.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
# FAKE - F# Make
.fake/
!tools/packages.config
tools/
# MacOS
.DS_Store
# Ocelot acceptance test config
test/Ocelot.AcceptanceTests/ocelot.json
# Read the docstates
_build/
_static/
_templates/
# JetBrains Rider
.idea/
# Test Results
*.trx

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# zxd.webapi

25
code/.dockerignore Normal file
View File

@ -0,0 +1,25 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

View File

@ -0,0 +1,71 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DG.Core" Version="1.1.3" />
<PackageReference Include="DG.Redis" Version="1.0.17" />
<PackageReference Include="DG.Tool" Version="1.0.9" />
<PackageReference Include="DG.Kafka" Version="1.0.2" />
<PackageReference Include="DG.Kafka.Worker" Version="1.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.2" />
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Sinks.Exceptionless" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Exceptionless" Version="6.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Zxd.Core.Shared\Zxd.Core.Shared.csproj" />
<ProjectReference Include="..\Zxd.EntityFramework\Zxd.EntityFramework.csproj" />
<ProjectReference Include="..\Zxd.Entity\Zxd.Entity.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="System.Text.Json" />
<Using Include="System.Text.Json.Serialization" />
<Using Include="Microsoft.EntityFrameworkCore" />
<Using Include="Microsoft.Extensions.Configuration" />
<Using Include="System.Text.Json" />
<Using Include="System.Text.Json.Serialization" />
<Using Include="DG.Tool" />
<Using Include="DG.Core" />
<Using Include="CommonWorker.Workers" />
<Using Include="CommonWorker.Dto" />
<Using Include="DG.Kafka.Worker" />
<Using Include="Microsoft.Extensions.Configuration" />
<Using Include="Microsoft.Extensions.DependencyInjection" />
<Using Include="Microsoft.Extensions.Logging" />
<Using Include="Serilog" />
<Using Include="DG.EntityFramework" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.Disaster.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.Production.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Serilog.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Serilog.Production.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CommonWorker.Config
{
public class SystemConfig
{
public string zxdCoreApi { get; set; }
public string nodeWebApi { get; set; }
public string crmCoreApi { get; set; }
}
}

View File

@ -0,0 +1,23 @@
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["CommonWorker/CommonWorker.csproj", "CommonWorker/"]
COPY ["Zxd.Entity/Zxd.Entity.csproj", "Zxd.Entity/"]
COPY ["Zxd.Core.Shared/Zxd.Core.Shared.csproj", "Zxd.Core.Shared/"]
COPY ["Zxd.EntityFramework/Zxd.EntityFramework.csproj", "Zxd.EntityFramework/"]
RUN dotnet restore "CommonWorker/CommonWorker.csproj"
COPY . .
WORKDIR "/src/CommonWorker"
RUN dotnet build "CommonWorker.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "CommonWorker.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "CommonWorker.dll"]

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CommonWorker.Dto
{
public class ResPassTimeDto
{
public string ResId { get; set; }
}
public class CompanyBussiness
{
public string[] soft { get; set; }
public string[] xinmeiti { get; set; }
public int? ProtectTime { get; set; } = 14;
public decimal limitPrice { get; set; }
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CommonWorker.Dto
{
public class UserModuleApiModel
{
public int ret { get; set; }
public string? message { get; set; }
public Dictionary<string, List<UserModuleModel>> moduelData { get; set; }
}
public class UserModuleModel
{
public string? orderid { get; set; }
public string? productid { get; set; }
public long? start { get; set; }
public long? end { get; set; }
}
}

View File

@ -0,0 +1,85 @@
using CommonWorker.Config;
using CommonWorker.Dto;
using CommonWorker.Workers;
using DG.Kafka.Worker;
using Exceptionless;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using Serilog;
using Zxd.EntityFramework;
try
{
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
Console.WriteLine($"Env: {env}");
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env ?? "Production"}.json", true)
.AddJsonFile("Serilog.json")
.AddJsonFile($"Serilog.{env ?? "Production"}.json", true)
.Build();
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(config)
.WriteTo.Exceptionless(config.GetValue<string>("Exceptionless:ApiKey"), config.GetValue<string>("Exceptionless:ServerUrl"), new string[] { "WeworkUserWorker" })
.CreateLogger();
Log.Logger = logger;
Log.Information("Starting WeworkUserWorker");
IServiceCollection services = new ServiceCollection();
services.AddLogging(logging =>
{
logging.ClearProviders();
logging.AddSerilog();
});
services.AddSingleton(config);
services.AddOptions()
.Configure<SystemConfig>(e => config.GetSection("SystemConfig").Bind(e));
ExceptionlessClient.Default.Startup(config.GetValue<string>("Exceptionless:ApiKey"));
ExceptionlessClient.Default.Configuration.ServerUrl = config.GetValue<string>("Exceptionless:ServerUrl");
//services.AddRedis(config);
services.AddDGEntityFramework<ZxdDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("zxdcrm"), ServerVersion.AutoDetect(config.GetConnectionString("zxdcrm")));
});
services.AddDGEntityFramework<DncmsbaseDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("dncmsbase"), ServerVersion.AutoDetect(config.GetConnectionString("dncmsbase")));
});
services.AddDGEntityFramework<UserCenterDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("usercenter"), ServerVersion.AutoDetect(config.GetConnectionString("usercenter")));
});
services.AddDGEntityFramework<DncmsDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("dncms"), ServerVersion.AutoDetect(config.GetConnectionString("dncms")));
});
services.AddDGEntityFramework<CrmDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("crm"), ServerVersion.AutoDetect(config.GetConnectionString("crm")));
});
services.AddDGEntityFramework<CompanyBaseConfDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("companyBaseConf"), ServerVersion.AutoDetect(config.GetConnectionString("companyBaseConf")));
});
services.AddKafkaWorker(config);
services.AddDGHttpClient();
services.AddRegisterWorker<CustomerPassTimeWorker, ResPassTimeDto>();
//构建容器
IServiceProvider serviceProvider = services.BuildServiceProvider();
var workerManager = serviceProvider.GetRequiredService<IKafkaWorkerManager>();
await workerManager.RegisterWorker<CustomerPassTimeWorker, ResPassTimeDto>("ResPassTime");
await workerManager.RegisterWorker<CustomerPassTimeWorker, ResPassTimeDto>("ResPassTime");
await workerManager.RegisterWorker<CustomerPassTimeWorker, ResPassTimeDto>("ResPassTime");
await workerManager.RegisterWorker<CustomerPassTimeWorker, ResPassTimeDto>("ResPassTime");
await workerManager.RegisterWorker<CustomerPassTimeWorker, ResPassTimeDto>("ResPassTime");
var builder = new HostBuilder();
await builder.RunConsoleAsync();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}

View File

@ -0,0 +1,16 @@
{
"profiles": {
"CommonWorker": {
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker (1)": {
"commandName": "Docker",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,33 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information",
"Microsoft.EntityFrameworkCore": "Warning",
"System.Net.Http.HttpClient": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"path": "logs/log.log",
"rollingInterval": "3",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}

View File

@ -0,0 +1,33 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information",
"Microsoft.EntityFrameworkCore": "Information",
"System.Net.Http.HttpClient": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"path": "logs/log.log",
"rollingInterval": "3",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}",
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}

View File

@ -0,0 +1,251 @@
using CommonWorker.Config;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Zxd.Entity.Crm;
using Zxd.Entity.Dncms;
using Zxd.Entity.Zxd;
using Zxd.Entity.Zxd.Order;
using Zxd.EntityFramework;
namespace CommonWorker.Workers
{
internal class CustomerPassTimeWorker : KafkaWorkerBase<ResPassTimeDto>
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<CustomerPassTimeWorker> _logger;
private readonly IHttpClient _httpClient;
private readonly IOptionsSnapshot<SystemConfig> _systemConfig;
public CustomerPassTimeWorker(
IServiceProvider serviceProvider,
IOptionsSnapshot<SystemConfig> systemConfig,
ILogger<CustomerPassTimeWorker> logger,
IHttpClient httpClient
) : base(logger)
{
_serviceProvider = serviceProvider;
_httpClient = httpClient;
_logger = logger;
_systemConfig = systemConfig;
}
/// <summary>
/// 算出统计表
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
protected override async Task DoWorkAsync(ResPassTimeDto model)
{
var resList = model.ResId.Split(",");
//Log.Information($"批量计算客户过期时间{model.ResId}");
foreach (var item in resList)
{
try
{
Log.Information($"{item}开始计算客户过期时间");
//需要新注册实体
using var scope = _serviceProvider.CreateAsyncScope();
var _repository = scope.ServiceProvider.GetRequiredService<IBaseRepository<ZxdDbContext>>();
var _crmRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
//目前只支持一个resid
var allresid = item.Split(",").ToList();
//订单信息
allresid = allresid.Where(n => !string.IsNullOrWhiteSpace(n)).ToList();
var cust = await _repository.GetRepository<RES_CUSTOMER>().Query().FirstOrDefaultAsync(n => n.RESID == item || n.UMID == item);
allresid = await _repository.GetRepository<RES_CUSTOMER>().Query().Where(n => n.CUSTOMERID == cust.CUSTOMERID && !string.IsNullOrWhiteSpace(n.RESID))
.Select(n => n.RESID).Distinct().ToListAsync();
var customerList = await _repository.GetRepository<RES_CUSTOMER>().Query().Where(n => allresid.Contains(n.RESID)).ToListAsync();
var _orderRepository = _repository.GetRepository<WX_SZZYORDER>();
var orderList = await _orderRepository.Query().Where(n => allresid.Contains(n.RESID)).ToListAsync();
//退款信息 按实际退款
var refundOrderList = await _repository.GetRepository<WX_SzzyOrderRefund>().Query()
.Where(n => allresid.Contains(n.resid) && n.auditstatus == 1 && n.isacturalrefund == 1).ToListAsync();
//到账信息
var orderDepositList = await _repository.GetRepository<WX_SZZYORDERDEPOSIT>().Query()
.Where(n => allresid.Contains(n.resid) && n.auditstatus == 1).ToListAsync();
var _userRepository = _repository.GetRepository<SOFT_USER>();
Log.Information($"实际计算客户过期时间{string.Join(",", allresid)}");
var allsoft = _userRepository.Query().Where(m => allresid.Contains(m.RESID)).OrderBy(m => m.REGDATE).ToList();//查出所有的soft
//过期时间
var usernameList = allsoft.Select(n => n.USERNAME).ToList();
if (allsoft.Count == 0)
{
usernameList = orderList.Select(n => n.SOFTUSERNAME).Distinct().ToList();
}
List<UserModuleModel> passTimeList = new List<UserModuleModel>();
Log.Information($"{item}GetSoftName【{string.Join(",", usernameList)}】");
foreach (var username in usernameList)
{
passTimeList.AddRange(await GetRootPassTime(username));
}
var orderidList = passTimeList.Select(n => n.orderid).ToList();
//剔除免费订单
var freeOrderList = await _repository.GetRepository<L2_SOFT_ORDER>().Query().Where(n => orderidList.Contains(n.WEBORDERID)).ToListAsync();
var freeorderids = freeOrderList.Where(n => !n.MainOrderId.HasValue).Select(n => n.WEBORDERID).ToList();
passTimeList = passTimeList.Where(n => !freeorderids.Contains(n.orderid)).ToList(); //不包括免费产品
var deptCodes = orderList.GroupBy(n => new { n.Deptid, n.GroupId, n.DeptName }).Select(n => new { deptid = n.Key.Deptid, groupid = n.Key.GroupId, deptname = n.Key.DeptName }).ToList();
var depositCodes = orderDepositList.GroupBy(n => new { n.deptid, n.groupid, n.deptName }).Select(n => new { deptid = n.Key.deptid, groupid = n.Key.groupid, deptname = n.Key.deptName }).ToList();
var refundCodes = refundOrderList.GroupBy(n => new { n.deptid, n.groupid, n.deptName }).Select(n => new { deptid = n.Key.deptid, groupid = n.Key.groupid, deptname = n.Key.deptName }).ToList();
deptCodes.AddRange(depositCodes);
deptCodes.AddRange(refundCodes);
deptCodes = deptCodes.Distinct().ToList();
var protectDay = 14; //默认14天
var softbusiness = await _repository.GetRepository<BAS_PARAMETER>().Query().FirstOrDefaultAsync(n => n.PARAKEY == "SoftBusiness");
//Log.Information($"获取配置{softbusiness.ToJson()}");
decimal limitPrice = 500; //默认500
if (softbusiness != null)
{
CompanyBussiness bussiness = JsonHelper.FromJson<CompanyBussiness>(softbusiness.PARAVALUE);
protectDay = bussiness.ProtectTime.Value;
limitPrice = bussiness.limitPrice;
}
foreach (var code in deptCodes)
{
var deptorders = orderList.Where(n => n.Deptid == code.deptid).ToList();
var deptFreeOrder = freeOrderList.Where(n => n.MainOrderId.HasValue && n.deptid == code.deptid).ToList();
//算出 余额 = 取流水剩余金额 + 已支付 已提交支付的订单到账金额。
var deptDepositList = orderDepositList.Where(n => n.deptid == code.deptid).ToList();
var lastPrice = deptDepositList.Sum(n => n.lastprice);
var unOpenOrderStatus = new List<string> { "200", "190" };
var orderUsePrice = deptorders.Where(n => unOpenOrderStatus.Contains(n.ORDERSTATUS)).Sum(n => n.ARRIVALPAY);
lastPrice += orderUsePrice;
Log.Information($"{item} lastPrice:【{lastPrice}】,orderUsePrice:【{orderUsePrice}】");
//退款信息
var deptrefundOrderList = refundOrderList.Where(n => n.deptid == code.deptid);
//Log.Information($"查询出事业部【{code.deptid}_{code.deptname}】订单信息【{JsonHelper.ToJson(deptorders)}】流水信息{JsonHelper.ToJson(deptDepositList)}退款信息{JsonHelper.ToJson(deptrefundOrderList)}免费订单信息{JsonHelper.ToJson(deptFreeOrder)}");
var info = await _crmRepository.GetRepository<ResCutomerPassTime>().Query().FirstOrDefaultAsync(n => n.RESID == item && n.Deptid == code.deptid);
var isInsert = false;
var customer = customerList.FirstOrDefault(n => n.RESID == item);
if (info == null)
{
isInsert = true;
info = new ResCutomerPassTime
{
Deptid = code.deptid,
GroupId = code.groupid,
DeptName = code.deptname,
RESID = item
};
}
if (string.IsNullOrWhiteSpace(info.UMID))
{
info.UMID = customer?.UMID;
}
info.balancepay = lastPrice;
var orderPassTime = passTimeList.Where(n => deptorders.Select(n => n.SZZYORDERID.ToString()).Contains(n.orderid) || deptFreeOrder.Select(n => n.WEBORDERID).Contains(n.orderid)).ToList();
Log.Information($"{item}过期时间计算【{JsonHelper.ToJson(orderPassTime)}】【{JsonHelper.ToJson(info)}】");
info.arrivalpay = deptDepositList.Sum(n => n.payprice);
info.refundpay = deptrefundOrderList.Sum(n => n.refundprice);
var paydeptDepositList = deptDepositList.Where(n => n.lastprice > 0 || n.useprice > 0).ToList();
info.firstPayTime = paydeptDepositList.Min(n => n.audittime);
info.inpay = info.arrivalpay - info.refundpay;
if (orderPassTime.Count > 0)
{
var longtime = orderPassTime.Max(n => n.end);
info.orderpasstime = JavaLongToDateTime(longtime.Value);
}
if (info.balancepay >= limitPrice)
{
info.protecttime = Convert.ToDateTime("2050-01-01");
}
else if (orderPassTime.Count > 0)
{
info.protecttime = info.orderpasstime.Value.AddDays(protectDay);
}
else
{
info.protecttime = null;
}
//如果净金额小于500 不予保护
if (info.inpay < 500)
{
info.protecttime = null;
}
try
{
if (isInsert)
{
info.Ctime = DateTime.Now;
await _crmRepository.GetRepository<ResCutomerPassTime>().InsertAsync(info);
}
else
{
info.utime = DateTime.Now;
await _crmRepository.GetRepository<ResCutomerPassTime>().UpdateAsync(info);
}
Log.Information($"{item}计算结果【{JsonHelper.ToJson(info)}】");
}
catch (Exception ex)
{
Log.Error($"计算客户过期时间错误{ex.Message}");
}
}
//删除无关的数据
var existCode = deptCodes.Select(n => n.deptid).ToList();
var existItem = await _crmRepository.GetRepository<ResCutomerPassTime>().Query().Where(n => n.RESID == item && !existCode.Contains(n.Deptid)).ToListAsync();
if (existItem.Count > 0)
{
Log.Information($"{item}待删除{string.Join(",", existItem.Select(n => n.Deptid))}");
await _crmRepository.GetRepository<ResCutomerPassTime>().BatchDeleteAsync(existItem);
}
}
catch (Exception ex)
{
Log.Error($"{item}获取过期时间失败{ex.Message}");
}
}
}
private async Task<List<UserModuleModel>> GetRootPassTime(string username)
{
List<UserModuleModel> res = new List<UserModuleModel>();
var url = $"{_systemConfig.Value.nodeWebApi.Trim('/')}/order/doGetUserPerssion";
var postJson = new
{
username = username
};
try
{
var data = await _httpClient.PostAsync<UserModuleApiModel>(url, postJson);
if (data.ret == 0)
{
foreach (var item in data.moduelData)
{
try
{
res.AddRange(item.Value);
}
catch (Exception ex)
{
Log.Error($"【{username}】接口获取权限数据失败{ex.Message}【{item.Value}】");
}
}
}
}
catch (Exception ex)
{
Log.Error($"【{username}】接口获取权限数据失败{ex.Message}");
}
return res;
}
/// <summary>
/// </summary>
/// <param name="timeJavaLong">java长整型日期毫秒为单位</param>
/// <returns></returns>
private DateTime JavaLongToDateTime(long timeJavaLong)
{
var dt1970 = new DateTime(1970, 1, 1, 0, 0, 0);
var tricks1970 = dt1970.Ticks;//1970年1月1日刻度
var timeTricks = tricks1970 + timeJavaLong * 10000;//日志日期刻度
return new DateTime(timeTricks).AddHours(8);//转化为DateTime
}
}
}

View File

@ -0,0 +1,30 @@
{
"ConnectionStrings": {
"zxdcrm": "Data Source=10.22.15.61;Port=3306;Initial Catalog=zxdcrm;user id=qianbenjie;password=Hcqianbenjie@123;Old Guids=true;SslMode=None",
"dncmsbase": "Data Source=10.22.15.68;Port=3306;Initial Catalog=dncmsbase;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"usercenter": "Data Source=10.22.15.68;Port=3306;Initial Catalog=usercenter;user id=hguser;password=nH5L$&Hxxco;SslMode=None",
"dncms": "Data Source=10.22.15.68;Port=3306;Initial Catalog=dncms;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"companyBaseConf": "Data Source=10.22.15.68;Port=3306;Initial Catalog=db_company_base_conf;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None"
},
"Consumers": [
{
"Host": "172.18.11.77:9092,172.18.11.76:9092",
"GroupId": "crm",
"Topic": "ResPassTime"
}
],
"TaskConfig": {
"TaskName": "Zxd.WeworkUserWorker",
"TaskRemarks": "Zxd.WeworkUserWorker",
"Enable": true,
"SecondsDelay": 5
},
"Exceptionless": {
"ServerUrl": "http://10.22.11.9:5000",
"ApiKey": "X5P60jU2Iemg8YaKtp71O4YUhbFqmqgCVLfDAQRy"
},
"SystemConfig": {
"zxdCoreApi": "http://10.22.15.163:8089",
"nodeWebApi": "http://10.22.15.5:8080"
}
}

View File

@ -0,0 +1,30 @@
{
"ConnectionStrings": {
"zxdcrm": "Data Source=mysql98ff96c3dffa.rds.ivolces.com;Port=3306;Initial Catalog=zxdcrm;user id=qianbenjie;password=Hcqianbenjie@123;Old Guids=true;SslMode=None",
"dncmsbase": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=dncmsbase;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"usercenter": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=usercenter;user id=hguser;password=nH5L$&Hxxco;SslMode=None",
"dncms": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=dncms;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"companyBaseConf": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=db_company_base_conf;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None"
},
"Consumers": [
{
"Host": "172.18.11.77:9092,172.18.11.76:9092",
"GroupId": "crm",
"Topic": "ResPassTime"
}
],
"TaskConfig": {
"TaskName": "Zxd.WeworkUserWorker",
"TaskRemarks": "Zxd.WeworkUserWorker",
"Enable": true,
"SecondsDelay": 5
},
"Exceptionless": {
"ServerUrl": "http://10.22.11.9:5000",
"ApiKey": "X5P60jU2Iemg8YaKtp71O4YUhbFqmqgCVLfDAQRy"
},
"SystemConfig": {
"zxdCoreApi": "http://120.77.165.155:8089",
"nodeWebApi": "https://r2.soft.dn8188.com/"
}
}

View File

@ -0,0 +1,31 @@
{
"ConnectionStrings": {
"zxdcrm": "Server=192.168.11.141;Database=zxdcrm;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"dncmsbase": "Server=192.168.11.41;Database=dncmsbase;UserId=root;Password=sa123456.;port=3306;",
"usercenter": "Server=192.168.11.41;Database=usercenter;UserId=root;Password=sa123456.;port=3306;",
"dncms": "Server=192.168.11.41;Database=dncms;UserId=root;Password=sa123456.;port=3306;",
"companyBaseConf": "Server=192.168.11.141;Database=db_company_base_conf;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"crm": "Server=192.168.11.141;Database=db_crm;UserId=tafadmin;Password=tafadmin2017;port=3306;"
},
"Consumers": [
{
"Host": "192.168.11.104:9092,192.168.11.104:9092",
"GroupId": "crm",
"Topic": "ResPassTime"
}
],
"TaskConfig": {
"TaskName": "DG.Worker.Sample",
"TaskRemarks": "DG.Worker.Sample",
"Enable": true,
"SecondsDelay": 5
},
"Exceptionless": {
"ServerUrl": "http://10.22.12.9:5000",
"ApiKey": "tmrp0GmwyTMe6UxJIw7LXAcIWYKEUgy2kprMiybd"
},
"SystemConfig": {
"zxdCoreApi": "http://192.168.11.81:8089",
"nodeWebApi": "http://120.238.224.24:10034"
}
}

View File

@ -0,0 +1,67 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Kafka.Worker
{
public abstract class BatchKafkaWorkerBase<T>
{
private readonly ILogger<BatchKafkaWorkerBase<T>> _logger;
private TaskConfig _taskConfig;
private int _milliSecondsDelay;
public BatchKafkaWorkerBase(ILogger<BatchKafkaWorkerBase<T>> logger)
{
_logger = logger;
_taskConfig = KafkaClient.Default.ConfigurationManager.GetSection("TaskConfig").Get<TaskConfig>();
if (_taskConfig == null)
{
throw new ArgumentNullException(nameof(_taskConfig));
}
_milliSecondsDelay = _taskConfig.MilliSecondsDelay;
}
public async Task Start(List<T> t)
{
Stopwatch watch = new Stopwatch();
watch.Reset();
watch.Start();
try
{
if (_milliSecondsDelay <= 0)
{
_logger.LogWarning($"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}] 任务定时时间不能少于0");
return;
}
if (!_taskConfig.Enable)
{
await Task.Delay(_milliSecondsDelay);
_logger.LogWarning($"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}] 任务停止启动!");
return;
}
await DoWorkAsync(t);
}
catch (Exception ex)
{
_logger.LogError(ex, $"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}]");
}
watch.Stop();
double costtime = watch.ElapsedMilliseconds;
_logger.LogDebug($"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}] 任务执行结束,用时:{costtime}ms");
}
protected virtual async Task DoWorkAsync(List<T> t)
{
}
public virtual async Task ShopAsync()
{
}
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Version>1.0.21</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DG.Kafka\DG.Kafka.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Kafka.Worker
{
public interface IKafkaWorkerManager
{
Task RegisterWorker<TWorker, T>(string? topic) where TWorker : KafkaWorkerBase<T>;
Task RegisterBatchWorker<TWorker, T>(string? topic, int batchsize = 1000) where TWorker : BatchKafkaWorkerBase<T>;
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Kafka.Worker
{
public interface IWorkerManager
{
void DoWork(Action doWork);
Task DoWorkAsync(Func<Task> doWork);
}
}

View File

@ -0,0 +1,70 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace DG.Kafka.Worker
{
public abstract class KafkaWorkerBase<T>
{
private readonly ILogger<KafkaWorkerBase<T>> _logger;
private TaskConfig _taskConfig;
private int _milliSecondsDelay;
public KafkaWorkerBase(ILogger<KafkaWorkerBase<T>> logger)
{
_logger = logger;
_taskConfig = KafkaClient.Default.ConfigurationManager.GetSection("TaskConfig").Get<TaskConfig>();
if (_taskConfig == null)
{
throw new ArgumentNullException(nameof(_taskConfig));
}
_milliSecondsDelay = _taskConfig.MilliSecondsDelay;
}
public async Task Start(T t)
{
Stopwatch watch = new Stopwatch();
watch.Reset();
watch.Start();
_logger.LogDebug($"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}] 任务开始执行1");
try
{
if (_milliSecondsDelay <= 0)
{
_logger.LogWarning($"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}] 任务定时时间不能少于0");
return;
}
if (!_taskConfig.Enable)
{
await Task.Delay(_milliSecondsDelay);
_logger.LogWarning($"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}] 任务停止启动!");
return;
}
await DoWorkAsync(t);
}
catch (Exception ex)
{
_logger.LogError(ex, $"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}]");
}
watch.Stop();
double costtime = watch.ElapsedMilliseconds;
_logger.LogDebug($"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}] 任务执行结束,用时:{costtime}ms");
}
protected virtual async Task DoWorkAsync(T t)
{
}
public virtual async Task ShopAsync()
{
}
}
}

View File

@ -0,0 +1,63 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Kafka.Worker
{
internal class KafkaWorkerManager : IKafkaWorkerManager
{
private TaskConfig _taskConfig;
private List<Consumer> _consumers;
private readonly ILogger<KafkaWorkerManager> _logger;
private readonly IServiceProvider _serviceProvider;
public KafkaWorkerManager(ILogger<KafkaWorkerManager> logger, IServiceProvider serviceProvider)
{
_logger = logger;
// 读取任务配置
_taskConfig = KafkaClient.Default.ConfigurationManager.GetSection("TaskConfig").Get<TaskConfig>();
_consumers = KafkaClient.Default.ConfigurationManager.GetSection("Consumers").Get<List<Consumer>>();
if (_taskConfig == null)
{
throw new ArgumentNullException(nameof(_taskConfig));
}
if (_consumers == null)
{
throw new ArgumentNullException(nameof(_consumers));
}
_serviceProvider = serviceProvider;
}
public async Task RegisterWorker<TWorker, T>(string? topic)
where TWorker : KafkaWorkerBase<T>
{
var t = _serviceProvider.GetRequiredService<TWorker>();
_logger.LogDebug($"注册worker: {typeof(TWorker).Name}");
var consumer = _consumers.FirstOrDefault(c => c.Topic == topic);
if (consumer == null)
{
throw new ArgumentNullException(nameof(consumer));
}
await KafkaClient.Builder<T>(consumer, t.Start, t.ShopAsync);
}
public async Task RegisterBatchWorker<TWorker, T>(string? topic, int batchsize = 1000)
where TWorker : BatchKafkaWorkerBase<T>
{
var t = _serviceProvider.GetRequiredService<TWorker>();
_logger.LogDebug($"注册worker: {typeof(TWorker).Name}");
var consumer = _consumers.FirstOrDefault(c => c.Topic == topic);
if (consumer == null)
{
throw new ArgumentNullException(nameof(consumer));
}
await KafkaClient.BatchBuilder<T>(consumer, t.Start, t.ShopAsync, batchsize);
}
}
}

View File

@ -0,0 +1,45 @@
using DG.Kafka;
using DG.Kafka.Worker;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// Extensions method
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// Redis service registered
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <returns></returns>
public static IServiceCollection AddKafkaWorker(this IServiceCollection services, IConfiguration configuration)
{
services.AddKafka(configuration);
services.AddSingleton<IWorkerManager, WorkerManager>();
services.AddSingleton<IKafkaWorkerManager, KafkaWorkerManager>();
return services;
}
public static IServiceCollection AddRegisterWorker<TWorker, T>(this IServiceCollection services) where TWorker : KafkaWorkerBase<T>
{
services.AddSingleton<TWorker>();
return services;
}
public static IServiceCollection AddRegisterBatchWorker<TWorker, T>(this IServiceCollection services) where TWorker : BatchKafkaWorkerBase<T>
{
services.AddSingleton<TWorker>();
return services;
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Kafka.Worker
{
internal class TaskConfig
{
/// <summary>
/// 任务名称
/// </summary>
public string? TaskName { get; set; }
/// <summary>
/// 任务备注
/// </summary>
public string? TaskRemarks { get; set; }
/// <summary>
/// 是否启用
/// </summary>
public bool Enable { get; set; }
/// <summary>
/// 停止毫秒数
/// </summary>
public int MilliSecondsDelay { get; set; }
}
}

View File

@ -0,0 +1,69 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
namespace DG.Kafka.Worker
{
internal class WorkerManager : IWorkerManager
{
private TaskConfig _taskConfig;
private int _milliSecondsDelay;
private readonly ILogger<WorkerManager> _logger;
public WorkerManager(ILogger<WorkerManager> logger)
{
_logger = logger;
// 读取任务配置
_taskConfig = KafkaClient.Default.ConfigurationManager.GetSection("TaskConfig").Get<TaskConfig>();
if (_taskConfig == null)
{
throw new ArgumentNullException(nameof(_taskConfig));
}
_milliSecondsDelay = _taskConfig.MilliSecondsDelay;
}
public void DoWork(Action doWork)
{
try
{
doWork();
}
catch (Exception ex)
{
_logger.LogError(ex, $"[{DateTimeOffset.Now}] [{_taskConfig?.TaskName}] 获取配置失败!");
return;
}
}
public async Task DoWorkAsync(Func<Task> doWork)
{
while(true)
{
_logger.LogDebug($"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}] 任务开始执行11");
try
{
if (_milliSecondsDelay <= 0)
{
_logger.LogWarning($"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}] 任务定时时间不能少于0");
continue;
}
if (!_taskConfig.Enable)
{
await Task.Delay(_milliSecondsDelay);
_logger.LogWarning($"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}] 任务停止启动!");
continue;
}
// 执行主逻辑
await doWork();
}
catch (Exception ex)
{
_logger.LogError(ex, $"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}]");
}
_logger.LogDebug($"[{DateTimeOffset.Now}] [{_taskConfig.TaskName}] 任务执行结束");
await Task.Delay(_milliSecondsDelay);
}
}
}
}

18
code/DG.Kafka/Consumer.cs Normal file
View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Kafka
{
public class Consumer
{
public string? Host { get; set; }
public string? GroupId { get; set; }
public string Topic { get; set; }
public string? TypeName { get; set; }
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Version>1.0.18</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Confluent.Kafka" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DG.Kafka
{
public interface IKafkaProducer
{
Task ProduceAsync<TMessage>(string? topic, TMessage message);
}
}

View File

@ -0,0 +1,220 @@
using Confluent.Kafka;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace DG.Kafka
{
public class KafkaClient
{
/// <summary>
/// 是否停止服务
/// </summary>
public static bool Shop { get; set; } = false;
public void ReadFromConfiguration(IConfiguration configuration)
{
ConfigurationManager = configuration;
}
public IConfiguration ConfigurationManager { get; private set; }
private static readonly Lazy<KafkaClient> _defaultClient = new(() => new KafkaClient());
public static KafkaClient Default
{
get { return _defaultClient.Value; }
}
private static List<Consumer> _consumers { get; set; } = new List<Consumer>();
public static List<Consumer> GetConsumers()
{
_consumers = Default.ConfigurationManager.GetSection("Consumers").Get<List<Consumer>>();
return _consumers;
}
public static async Task Builder<T>(Consumer consumer, Func<T, Task> received, Func<Task> shoped)
{
await Task.Run(() =>
{
var tasks = new List<Task>();
//var receivedDelegate = new ReceivedDelegate<T>(ReceivedAsync);
ThreadPool.QueueUserWorkItem(async task =>
{
await ReceivedAsync(consumer, received, shoped);
});
});
}
public static async Task BatchBuilder<T>(Consumer consumer, Func<List<T>, Task> received, Func<Task> shoped, int batchsize = 1000)
{
await Task.Run(() =>
{
var tasks = new List<Task>();
//var receivedDelegate = new BatchReceivedDelegate<T>(BatchReceivedAsync);
ThreadPool.QueueUserWorkItem(async task =>
{
await BatchReceivedAsync(consumer, received, shoped, batchsize);
});
});
}
public delegate Task ReceivedDelegate<T>(Consumer consumer, Func<T, Task> received);
public delegate Task BatchReceivedDelegate<T>(Consumer consumer, Func<List<T>, Task> received, int batchsize = 1000);
public static async Task ReceivedAsync<T>(Consumer consumer, Func<T, Task> received, Func<Task> shoped)
{
try {
var consumerConfig = new ConsumerConfig
{
BootstrapServers = consumer.Host,
GroupId = consumer.GroupId,
AutoOffsetReset = AutoOffsetReset.Earliest,
EnableAutoCommit = false
};
var cancel = false;
using var consumerBuilder = new ConsumerBuilder<Ignore, string>(consumerConfig).Build();
Console.WriteLine($"Kafka alone 连接成功,配置: {JsonSerializer.Serialize(consumerConfig)}");
var topic = consumer.Topic;
consumerBuilder.Subscribe(topic);
while (!cancel)
{
try
{
if (Shop)
{
await shoped();
return;
}
var consumeResult = consumerBuilder.Consume(CancellationToken.None);
Console.WriteLine($"Consumer message: {consumeResult.Message.Value} topic: {consumeResult.Topic} Partition: {consumeResult.Partition}");
var message = JsonSerializer.Deserialize<T>(consumeResult.Message.Value);
if (message != null)
{
await received(message);
}
try
{
consumerBuilder.Commit(consumeResult);
}
catch (KafkaException e)
{
Console.WriteLine(e.Message);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
await Task.Delay(1);
}
consumerBuilder.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
public static async Task BatchReceivedAsync<T>(Consumer consumer, Func<List<T>, Task> received, Func<Task> shoped, int batchsize = 1000)
{
try
{
var consumerConfig = new ConsumerConfig
{
BootstrapServers = consumer.Host,
GroupId = consumer.GroupId,
AutoOffsetReset = AutoOffsetReset.Earliest,
EnableAutoCommit = false
};
var cancel = false;
using var consumerBuilder = new ConsumerBuilder<Ignore, string>(consumerConfig).Build();
Console.WriteLine($"Kafka batch 连接成功,配置: {JsonSerializer.Serialize(consumerConfig)}");
var topic = consumer.Topic;
consumerBuilder.Subscribe(topic);
while (!cancel)
{
try
{
if (Shop)
{
await shoped();
return;
}
var batchRecords = new List<T>();
var consumeResults = new List<ConsumeResult<Ignore, string>>();
while (batchRecords.Count < batchsize)
{
var consumeResult = consumerBuilder.Consume(CancellationToken.None);
Console.WriteLine($"Consumer message: {consumeResult.Message.Value} topic: {consumeResult.Topic} Partition: {consumeResult.Partition}");
var message = JsonSerializer.Deserialize<T>(consumeResult.Message.Value);
if (message == null)
break; // 贤有更多的消启可供消裁
consumeResults.Add(consumeResult);
batchRecords.Add(message);
}
await received(batchRecords);
foreach (var consumeResult in consumeResults)
{
consumerBuilder.Commit(consumeResult);
}
}
catch (KafkaException e)
{
Console.WriteLine("KafkaException:" + e.Message);
}
catch (Exception ex)
{
Console.WriteLine("KafkaException:" + ex.ToString());
}
await Task.Delay(1);
}
consumerBuilder.Close();
}
catch (Exception ex)
{
Console.WriteLine("KafkaException:" + ex.ToString());
}
}
public static async Task SendMessage<TMessage>(Consumer consumer, TMessage message)
{
var config = new ProducerConfig
{
BootstrapServers = consumer.Host,
};
var topic = consumer.Topic;
Action<DeliveryReport<Null, string>> handler = r =>
Console.WriteLine(!r.Error.IsError
? $"Delivered message to {r.TopicPartitionOffset}"
: $"Delivery Error: {r.Error.Reason}");
using (var p = new ProducerBuilder<Null, string>(config).Build())
{
try
{
p.Produce(topic, new Message<Null, string> { Value = JsonSerializer.Serialize(message) });
p.Flush(TimeSpan.FromSeconds(10));
}
catch (ProduceException<Null, string> e)
{
Console.WriteLine($"Delivery failed: {e.Error.Reason}");
}
}
}
}
}

View File

@ -0,0 +1,47 @@
using Confluent.Kafka;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using static Confluent.Kafka.ConfigPropertyNames;
namespace DG.Kafka
{
internal class KafkaProducer : IKafkaProducer
{
private static List<Consumer> _consumers { get; set; } = new List<Consumer>();
private Dictionary<string, IProducer<Null, string>> _producers;
public KafkaProducer()
{
_consumers = KafkaClient.Default.ConfigurationManager.GetSection("Consumers").Get<List<Consumer>>();
_producers = new Dictionary<string, IProducer<Null, string>>();
foreach (var consumer in _consumers)
{
var config = new ProducerConfig
{
BootstrapServers = consumer.Host,
};
var topic = consumer.Topic;
Action<DeliveryReport<Null, string>> handler = r =>
Console.WriteLine(!r.Error.IsError
? $"Delivered message to {r.TopicPartitionOffset}"
: $"Delivery Error: {r.Error.Reason}");
_producers.Add(consumer.Topic, new ProducerBuilder<Null, string>(config).Build());
}
}
public async Task ProduceAsync<TMessage>(string? topic, TMessage message)
{
var producer = _producers.First().Value;
if (!string.IsNullOrEmpty(topic))
{
producer = _producers.GetValueOrDefault(topic);
}
await producer.ProduceAsync(topic, new Message<Null, string> { Value = JsonSerializer.Serialize(message) });
producer.Flush();
}
}
}

View File

@ -0,0 +1,25 @@
using DG.Kafka;
using Microsoft.Extensions.Configuration;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// Extensions method
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// Redis service registered
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <returns></returns>
public static IServiceCollection AddKafka(this IServiceCollection services, IConfiguration configuration)
{
KafkaClient.Default.ReadFromConfiguration(configuration);
services = services.AddSingleton<IKafkaProducer, KafkaProducer>();
return services;
}
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EmployeeDepartmentDetailServices.Config
{
public class SystemConfig
{
public string zxdCoreApi { get; set; }
public string nodeWebApi { get; set; }
public string crmCoreApi { get; set; }
public int clearEDDHour { get; set; }
}
}

View File

@ -0,0 +1,57 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Update="appsettings.Production.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.PreProduction.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Serilog.Production.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Serilog.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Serilog.PreProduction.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="2.0.143" />
<PackageReference Include="DG.Core" Version="1.1.2" />
<PackageReference Include="DG.Tool" Version="1.0.11" />
<PackageReference Include="DG.Worker" Version="1.0.1" />
<PackageReference Include="Exceptionless" Version="6.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.18.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
<PackageReference Include="Quartz" Version="3.7.0" />
<PackageReference Include="Serilog" Version="3.0.1" />
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.Exceptionless" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Zxd.Core.Shared\Zxd.Core.Shared.csproj" />
<ProjectReference Include="..\Zxd.EntityFramework\Zxd.EntityFramework.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,91 @@
using DG.EntityFramework;
using EmployeeDepartmentDetailServices.Config;
using Exceptionless;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Quartz.Impl;
using Quartz.Spi;
using Quartz;
using Serilog;
using Zxd.EntityFramework;
using DG.Core;
using EmployeeDepartmentDetailServices.Worker;
try
{
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
Console.WriteLine($"Env: {env}");
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env ?? "Production"}.json", true)
.AddJsonFile("Serilog.json")
.AddJsonFile($"Serilog.{env ?? "Production"}.json", true)
.Build();
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(config)
.WriteTo.Exceptionless(config.GetValue<string>("Exceptionless:ApiKey"), config.GetValue<string>("Exceptionless:ServerUrl"), new string[] { "WeworkUserWorker" })
.CreateLogger();
Log.Logger = logger;
Log.Information("Starting ResourceFlowWorker");
IServiceCollection services = new ServiceCollection();
services.AddLogging(logging =>
{
logging.ClearProviders();
logging.AddSerilog();
});
services.AddSingleton<IConfiguration>(config);
services.AddOptions()
.Configure<SystemConfig>(e => config.GetSection("SystemConfig").Bind(e));
ExceptionlessClient.Default.Startup(config.GetValue<string>("Exceptionless:ApiKey"));
ExceptionlessClient.Default.Configuration.ServerUrl = config.GetValue<string>("Exceptionless:ServerUrl");
ExceptionlessClient.Default.Configuration.DefaultTags.Add("zxd-ResourceFlowWorker");
//services.AddRedis(config);
services.AddDGEntityFramework<ZxdDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("zxdcrm"), ServerVersion.AutoDetect(config.GetConnectionString("zxdcrm")));
});
services.AddDGEntityFramework<DncmsbaseDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("dncmsbase"), ServerVersion.AutoDetect(config.GetConnectionString("dncmsbase")));
});
services.AddDGEntityFramework<UserCenterDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("usercenter"), ServerVersion.AutoDetect(config.GetConnectionString("usercenter")));
});
services.AddDGEntityFramework<DncmsDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("dncms"), ServerVersion.AutoDetect(config.GetConnectionString("dncms")));
});
services.AddDGEntityFramework<CompanyBaseConfDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("companyBaseConf"), ServerVersion.AutoDetect(config.GetConnectionString("companyBaseConf")));
});
//services.AddDGEntityFramework<HgActionDbContext>(options =>
//{
// options.UseMySql(config.GetConnectionString("hgaction"), ServerVersion.AutoDetect(config.GetConnectionString("hgaction")));
//});
services.AddDGEntityFramework<CrmCloudDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("crmcloud"), ServerVersion.AutoDetect(config.GetConnectionString("crmcloud")));
});
services.AddWorker(config);
services.AddDGHttpClient();
services.AddRegisterWorker<SynchronousWorker>();
var builder = new HostBuilder();
await builder.RunConsoleAsync();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}

View File

@ -0,0 +1,16 @@
{
"profiles": {
"WeworkUserWorker": {
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker": {
"commandName": "Docker",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,33 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information",
"Microsoft.EntityFrameworkCore": "Information",
"System.Net.Http.HttpClient": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"path": "logs/log.log",
"rollingInterval": "3",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}",
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}

View File

@ -0,0 +1,33 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information",
"Microsoft.EntityFrameworkCore": "Information",
"System.Net.Http.HttpClient": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"path": "logs/log.log",
"rollingInterval": "3",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}",
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}

View File

@ -0,0 +1,33 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information",
"Microsoft.EntityFrameworkCore": "Information",
"System.Net.Http.HttpClient": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"path": "logs/log.log",
"rollingInterval": "3",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}",
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}

View File

@ -0,0 +1,452 @@
using DG.Core;
using DG.EntityFramework;
using DG.Worker;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Zxd.Entity.Dncms;
using Zxd.Entity;
using Zxd.EntityFramework;
using EmployeeDepartmentDetailServices.Config;
using Microsoft.EntityFrameworkCore;
using static System.Formats.Asn1.AsnWriter;
using Zxd.Entity.CompanyBaseConf;
using Microsoft.EntityFrameworkCore.Internal;
using DG.Tool;
using Zxd.Core.Shared.Dto;
using Zxd.Entity.Zxd;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Engineering;
namespace EmployeeDepartmentDetailServices.Worker
{
internal class SynchronousWorker : WorkerBase, IDisposable
{
private readonly IConfiguration _configuration;
private readonly IServiceProvider _serviceProvider;
private readonly IHttpClient _httpClient;
private readonly ILogger<SynchronousWorker> _logger;
private readonly SystemConfig? _systemConfig;
private static bool IsFrist = true;
public SynchronousWorker(ILogger<SynchronousWorker> logger,
IServiceProvider serviceProvider,
IConfiguration configuration,
IHttpClient httpClient
) : base(logger)
{
_logger = logger;
_configuration = configuration;
_serviceProvider = serviceProvider;
_systemConfig = configuration.GetSection("SystemConfig").Get<SystemConfig>();
_httpClient = httpClient;
}
/// <summary>
///
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
protected override async Task DoWorkAsync()
{
try
{
List<Zxd.Entity.Zxd.EmployeeDepartmentDetail> changeEdd = new List<Zxd.Entity.Zxd.EmployeeDepartmentDetail>();
List<Zxd.Entity.Zxd.EmployeeDepartmentFull> changeEd = new List<Zxd.Entity.Zxd.EmployeeDepartmentFull>();
List<Zxd.Entity.Zxd.ZXDDepartment> changeDepart = new List<Zxd.Entity.Zxd.ZXDDepartment>();
using var scope = _serviceProvider.CreateAsyncScope();
var companyBaseConfRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<CompanyBaseConfDbContext>>();
var zxdRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<ZxdDbContext>>();
var crmCloudRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<CrmCloudDbContext>>();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
//从员工系统同步数据
var employeedepartmentdetail = companyBaseConfRepository.GetRepository<Zxd.Entity.CompanyBaseConf.EmployeeDepartmentDetail>().Query();
var applicationpartment = companyBaseConfRepository.GetRepository<Zxd.Entity.CompanyBaseConf.ApplicationDepartment>().Query();
var application = companyBaseConfRepository.GetRepository<Zxd.Entity.CompanyBaseConf.Application>().Query();
var department = companyBaseConfRepository.GetRepository<Zxd.Entity.CompanyBaseConf.Department>().Query();
var employ = companyBaseConfRepository.GetRepository<Zxd.Entity.CompanyBaseConf.Employee>().Query();
var deptment = await _httpClient.GetAsync<ApiResult<List<DeptmentDto>>>($"{_systemConfig.zxdCoreApi}/Api/Deptment/Depts");//员工系统部门关心
if (DateTime.Now.Hour == _systemConfig.clearEDDHour)
{ //固定时间
//全量清空重新同步
Log.Error("清空历史数据:"+ DateTime.Now.Hour);
await zxdRepository.ExecuteSqlCommandNonQueryAsync(System.Data.CommandType.Text, "truncate table employee_department_detail");
await crmCloudRepository.ExecuteSqlCommandNonQueryAsync(System.Data.CommandType.Text, "truncate table employee_department_detail");
await dncmsbaseRepository.ExecuteSqlCommandNonQueryAsync(System.Data.CommandType.Text, "truncate table employee_department_detail");
}
#region
var zxdEdd = zxdRepository.GetRepository<Zxd.Entity.Zxd.EmployeeDepartmentDetail>().Query();
DateTime eddMaxUpDate = zxdEdd.Any() ? zxdEdd.Max(m => m.update_time) : new DateTime();
var newEmp = employeedepartmentdetail.Where(m => m.update_time > eddMaxUpDate).ToList();//需要入库的部分
var eidDic = employ.ToDictionary(m => m.id, n => n.employee_id);
var statusDic = employ.ToDictionary(m => m.id, n => n.status);
var deptDic = department.ToDictionary(m => m.Id, n => n.DepartmentName);
foreach (var item in newEmp)
{
Zxd.Entity.Zxd.EmployeeDepartmentDetail eddItem = new Zxd.Entity.Zxd.EmployeeDepartmentDetail();
eddItem.id = item.id;
eddItem.eid = 0;
eddItem.empStatus = 4;
if (eidDic.ContainsKey(item.emp_id))
{
eddItem.eid = eidDic[item.emp_id];//匹配eid
eddItem.empStatus = statusDic[item.emp_id] ?? 0;
}
else
{ //找不到工号
continue;
}
if (deptDic.ContainsKey(item.department_id))
{
eddItem.department_name = deptDic[item.department_id];
}
eddItem.department_id = item.department_id;
eddItem.department_type = item.department_type;
eddItem.level = item.level;
eddItem.is_deleted = item.is_deleted;
eddItem.create_time = item.create_time;
eddItem.update_time = item.update_time;
changeEdd.Add(eddItem);
}
var zxdEddIdList = zxdEdd.Select(m => m.id).ToList();
var updateEdd = changeEdd.Where(m => zxdEddIdList.Contains(m.id)).ToList();
var insertEdd = changeEdd.Where(m => !zxdEddIdList.Contains(m.id)).ToList();
#endregion
#region
var zxdDepart = zxdRepository.GetRepository<Zxd.Entity.Zxd.ZXDDepartment>().Query();
DateTime DepartMaxUpDate = zxdDepart.Any() ? zxdDepart.Max(m => m.Update_Time) : new DateTime();
var newDepart = department.Where(m => m.Update_Time > eddMaxUpDate).ToList();//需要入库的部分
foreach (var item in newDepart)
{
Zxd.Entity.Zxd.ZXDDepartment depart = new Zxd.Entity.Zxd.ZXDDepartment();
depart.Id = item.Id;
depart.ParentId = item.ParentId;
depart.DepartmentName = item.DepartmentName;
depart.DepartmentCode = item.DepartmentCode;
depart.DepartmentType = item.DepartmentType;
depart.Status = item.Status;
depart.IsDeleted = item.IsDeleted;
depart.Create_Time = item.Create_Time;
depart.Update_Time = item.Update_Time;
changeDepart.Add(depart);
}
var zxDepart = zxdDepart.Select(m => m.Id).ToList();
var updateDepart = changeDepart.Where(m => zxDepart.Contains(m.Id)).ToList();
var insertDepart = changeDepart.Where(m => !zxDepart.Contains(m.Id)).ToList();
#endregion
#region
var zxdEDF = zxdRepository.GetRepository<Zxd.Entity.Zxd.EmployeeDepartmentFull>().Query().ToList();
List<Zxd.Entity.Zxd.EmployeeDepartmentFull> deleteEdf = new List<Zxd.Entity.Zxd.EmployeeDepartmentFull>();
List<Zxd.Entity.Zxd.EmployeeDepartmentFull> insertEdf = new List<Zxd.Entity.Zxd.EmployeeDepartmentFull>();
List<Zxd.Entity.Zxd.EmployeeDepartmentFull> allEdf = new List<Zxd.Entity.Zxd.EmployeeDepartmentFull>();
var edList = await companyBaseConfRepository.ExecuteSqlToListAsync<EmpAppItem>(@"select a.emp_id,concat(c.app_id,'') app_id,c.application_name
from employee_department_detail a
left join application_department b on a.department_id=b.department_id
left join application c on b.application_id=c.id
where a.is_deleted =0 and b.is_deleted =0 and c.is_deleted =0
group by a.emp_id,c.app_id");
//拼接数据
foreach (var item in edList)
{
Zxd.Entity.Zxd.EmployeeDepartmentFull edf = new Zxd.Entity.Zxd.EmployeeDepartmentFull();
if (eidDic.ContainsKey(item.emp_id))
{
edf.eid = eidDic[item.emp_id];//匹配eid
}
else
{ //找不到工号
continue;
}
edf.appid = item.app_id;
edf.app_name = item.application_name;
edf.create_time = DateTime.Now;
if (!zxdEDF.Any(n => n.appid == edf.appid && n.eid == edf.eid))
{//不存在
insertEdf.Add(edf);//新增
}
allEdf.Add(edf);
}
foreach (var item in zxdEDF)
{
if (!allEdf.Any(n => n.appid == item.appid && n.eid == item.eid))//全量数据中 不存在的旧数据
{
deleteEdf.Add(item);//新增
}
}
#endregion
#region zxd
using var transaction = await zxdRepository.BeginTransactionAsync();
try
{
await zxdRepository.GetRepository<Zxd.Entity.Zxd.EmployeeDepartmentDetail>().BatchUpdateAsync(updateEdd);
await zxdRepository.GetRepository<Zxd.Entity.Zxd.EmployeeDepartmentDetail>().BatchInsertAsync(insertEdd);
await zxdRepository.GetRepository<Zxd.Entity.Zxd.EmployeeDepartmentFull>().BatchDeleteAsync(deleteEdf);
await zxdRepository.GetRepository<Zxd.Entity.Zxd.EmployeeDepartmentFull>().BatchInsertAsync(insertEdf);
await zxdRepository.GetRepository<Zxd.Entity.Zxd.ZXDDepartment>().BatchUpdateAsync(updateDepart);
await zxdRepository.GetRepository<Zxd.Entity.Zxd.ZXDDepartment>().BatchInsertAsync(insertDepart);
await transaction.CommitAsync();
}
catch (Exception ex)
{
Log.Error(ex, "同步失败!");
await transaction.RollbackAsync();
await transaction.DisposeAsync();
}
#endregion zxd
#region crm_cloud
using var hgtransaction = await crmCloudRepository.BeginTransactionAsync();
try
{
await crmCloudRepository.GetRepository<Zxd.Entity.HgAction.EmployeeDepartmentDetail>().BatchUpdateAsync(Mapping(updateEdd));
await crmCloudRepository.GetRepository<Zxd.Entity.HgAction.EmployeeDepartmentDetail>().BatchInsertAsync(Mapping(insertEdd));
await crmCloudRepository.GetRepository<Zxd.Entity.HgAction.EmployeeDepartmentFull>().BatchDeleteAsync(Mapping(deleteEdf));
await crmCloudRepository.GetRepository<Zxd.Entity.HgAction.EmployeeDepartmentFull>().BatchInsertAsync(Mapping(insertEdf));
await crmCloudRepository.GetRepository<Zxd.Entity.HgAction.HGDepartment>().BatchUpdateAsync(Mapping(updateDepart));
await crmCloudRepository.GetRepository<Zxd.Entity.HgAction.HGDepartment>().BatchInsertAsync(Mapping(insertDepart));
await hgtransaction.CommitAsync();
}
catch (Exception ex)
{
Log.Error(ex, "同步失败!");
await hgtransaction.RollbackAsync();
await hgtransaction.DisposeAsync();
}
#endregion hgaction
#region hncmsbase
using var dncmsbase = await dncmsbaseRepository.BeginTransactionAsync();
try
{
await dncmsbaseRepository.GetRepository<Zxd.Entity.Dncms.EmployeeDepartmentDetail>().BatchUpdateAsync(MappingToDncms(updateEdd));
await dncmsbaseRepository.GetRepository<Zxd.Entity.Dncms.EmployeeDepartmentDetail>().BatchInsertAsync(MappingToDncms(insertEdd));
await dncmsbaseRepository.GetRepository<Zxd.Entity.Dncms.EmployeeDepartmentFull>().BatchDeleteAsync(MappingToDncms(deleteEdf));
await dncmsbaseRepository.GetRepository<Zxd.Entity.Dncms.EmployeeDepartmentFull>().BatchInsertAsync(MappingToDncms(insertEdf));
await dncmsbaseRepository.GetRepository<Zxd.Entity.Dncms.Department>().BatchUpdateAsync(MappingToDncms(updateDepart));
await dncmsbaseRepository.GetRepository<Zxd.Entity.Dncms.Department>().BatchInsertAsync(MappingToDncms(insertDepart));
await dncmsbase.CommitAsync();
}
catch (Exception ex)
{
Log.Error(ex, "同步失败!");
await dncmsbase.RollbackAsync();
await dncmsbase.DisposeAsync();
}
#endregion hncmsbase
#region
//var eddGup = changeEdd.GroupBy(m => m.department_id);
//foreach (var item in eddGup)
//{
// try
// {
// int psize = 10;
// var dept = deptment.Data.FirstOrDefault(m => m.DepartmentId == item.Key);
// if (dept != null)
// {
// var url = $"{_systemConfig?.crmCoreApi}/Api/Customer/SynchronousCustomer";
// if (item.Count() < psize)
// {
// var data = await _httpClient.PostAsync<ApiResult<bool>>(url, new { eddList = item.ToList() }, dept.Appid);//
// }
// else
// {
// //分页推送
// int maxIndex = (item.Count() % psize) + 1;
// int pindex = 1;
// while (pindex <= maxIndex)
// {
// var list = item.ToList().Skip((pindex - 1) * psize).Take(psize).ToList();
// var res = await _httpClient.PostAsync<ApiResult<bool>>(url, new { eddList = list }, dept.Appid);//
// pindex++;
// }
// }
// }
// }
// catch (Exception ex)
// {
// Log.Error(ex, "推送失败!");
// continue;
// }
//}
#endregion
}
catch (Exception ex)
{
Log.Error(ex, "同步失败!");
}
}
private List<Zxd.Entity.HgAction.EmployeeDepartmentDetail> Mapping(List<Zxd.Entity.Zxd.EmployeeDepartmentDetail> list)
{
List<Zxd.Entity.HgAction.EmployeeDepartmentDetail> res = new List<Zxd.Entity.HgAction.EmployeeDepartmentDetail>();
foreach (var item in list)
{
Zxd.Entity.HgAction.EmployeeDepartmentDetail i = new Zxd.Entity.HgAction.EmployeeDepartmentDetail();
i.id = item.id;
i.eid = item.eid;
i.department_id = item.department_id;
i.department_name = item.department_name;
i.department_type = item.department_type;
i.empStatus = item.empStatus;
i.level = item.level;
i.is_deleted = item.is_deleted;
i.create_time = item.create_time;
i.update_time = item.update_time;
res.Add(i);
}
return res;
}
private List<Zxd.Entity.HgAction.EmployeeDepartmentFull> Mapping(List<Zxd.Entity.Zxd.EmployeeDepartmentFull> list)
{
List<Zxd.Entity.HgAction.EmployeeDepartmentFull> res = new List<Zxd.Entity.HgAction.EmployeeDepartmentFull>();
foreach (var item in list)
{
Zxd.Entity.HgAction.EmployeeDepartmentFull i = new Zxd.Entity.HgAction.EmployeeDepartmentFull();
i.id = item.id;
i.eid = item.eid;
i.appid = item.appid;
i.app_name = item.app_name;
i.create_time = item.create_time;
res.Add(i);
}
return res;
}
private List<Zxd.Entity.HgAction.HGDepartment> Mapping(List<Zxd.Entity.Zxd.ZXDDepartment> list)
{
List<Zxd.Entity.HgAction.HGDepartment> res = new List<Zxd.Entity.HgAction.HGDepartment>();
foreach (var item in list)
{
Zxd.Entity.HgAction.HGDepartment i = new Zxd.Entity.HgAction.HGDepartment();
i.Id = item.Id;
i.ParentId = item.ParentId;
i.DepartmentName = item.DepartmentName;
i.DepartmentType = item.DepartmentType;
i.DepartmentCode = item.DepartmentCode;
i.IsDeleted = item.IsDeleted;
i.Status = item.Status;
i.Create_Time = item.Create_Time;
i.Update_Time = item.Update_Time;
res.Add(i);
}
return res;
}
private List<Zxd.Entity.Dncms.EmployeeDepartmentDetail> MappingToDncms(List<Zxd.Entity.Zxd.EmployeeDepartmentDetail> list)
{
List<Zxd.Entity.Dncms.EmployeeDepartmentDetail> res = new List<Zxd.Entity.Dncms.EmployeeDepartmentDetail>();
foreach (var item in list)
{
Zxd.Entity.Dncms.EmployeeDepartmentDetail i = new Zxd.Entity.Dncms.EmployeeDepartmentDetail();
i.id = item.id;
i.eid = item.eid;
i.department_id = item.department_id;
i.department_name = item.department_name;
i.department_type = item.department_type;
i.empStatus = item.empStatus;
i.level = item.level;
i.is_deleted = item.is_deleted;
i.create_time = item.create_time;
i.update_time = item.update_time;
res.Add(i);
}
return res;
}
private List<Zxd.Entity.Dncms.EmployeeDepartmentFull> MappingToDncms(List<Zxd.Entity.Zxd.EmployeeDepartmentFull> list)
{
List<Zxd.Entity.Dncms.EmployeeDepartmentFull> res = new List<Zxd.Entity.Dncms.EmployeeDepartmentFull>();
foreach (var item in list)
{
Zxd.Entity.Dncms.EmployeeDepartmentFull i = new Zxd.Entity.Dncms.EmployeeDepartmentFull();
i.id = item.id;
i.eid = item.eid;
i.appid = item.appid;
i.app_name = item.app_name;
i.create_time = item.create_time;
res.Add(i);
}
return res;
}
private List<Zxd.Entity.Dncms.Department> MappingToDncms(List<Zxd.Entity.Zxd.ZXDDepartment> list)
{
List<Zxd.Entity.Dncms.Department> res = new List<Zxd.Entity.Dncms.Department>();
foreach (var item in list)
{
Zxd.Entity.Dncms.Department i = new Zxd.Entity.Dncms.Department();
i.Id = item.Id;
i.ParentId = item.ParentId;
i.DepartmentName = item.DepartmentName;
i.DepartmentType = item.DepartmentType;
i.DepartmentCode = item.DepartmentCode;
i.IsDeleted = item.IsDeleted;
i.Status = item.Status;
i.Create_Time = item.Create_Time;
i.Update_Time = item.Update_Time;
res.Add(i);
}
return res;
}
public void Dispose()
{
_logger.LogInformation("任务正在关闭");
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsDbContext>>();
var configs = dncmsRepository.GetRepository<ResourceFlowConfig>().Query()
.Where(x => x.Status == ResourceConfigStatus.).ToList();
configs.ForEach(x => x.Status = ResourceConfigStatus.);
dncmsRepository.GetRepository<ResourceFlowConfig>().BatchUpdateAsync(configs, x => new { x.Status });
}
}
public class EmpAppItem
{
public int emp_id { get; set; }
public string? app_id { get; set; }
public string? application_name { get; set; }
}
}

View File

@ -0,0 +1,27 @@
{
"ConnectionStrings": {
"zxdcrm": "Server=192.168.11.141;Database=zxdcrm;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"dncmsbase": "Server=192.168.11.41;Database=dncmsbase;UserId=root;Password=sa123456.;port=3306;",
"usercenter": "Server=192.168.11.41;Database=usercenter;UserId=root;Password=sa123456.;port=3306;",
"dncms": "Server=192.168.11.41;Database=dncms;UserId=root;Password=sa123456.;port=3306;",
"companyBaseConf": "Server=192.168.11.141;Database=db_company_base_conf;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"crm": "Server=192.168.11.141;Database=db_crm;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"crmcloud": "Server=192.168.11.141;Database=crm_cloud;UserId=tafadmin;Password=tafadmin2017;port=3306;"
},
"TaskConfig": {
"TaskName": "DG.SynchronousWorker",
"TaskRemarks": "DG.SynchronousWorker",
"Enable": true,
"SecondsDelay": 90
},
"Exceptionless": {
"ServerUrl": "http://10.22.12.9:5000",
"ApiKey": "tmrp0GmwyTMe6UxJIw7LXAcIWYKEUgy2kprMiybd"
},
"SystemConfig": {
"zxdCoreApi": "http://192.168.11.81:8089",
"nodeWebApi": "http://120.238.224.24:10034",
"crmCoreApi": "http://192.168.11.81:8088",
"clearEDDHour": "1"
}
}

View File

@ -0,0 +1,27 @@
{
"ConnectionStrings": {
"zxdcrm": "Data Source=mysql98ff96c3dffa.rds.ivolces.com;Port=3306;Initial Catalog=zxdcrm;user id=qianbenjie;password=Hcqianbenjie@123;Old Guids=true;SslMode=None",
"dncmsbase": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=dncmsbase;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"usercenter": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=usercenter;user id=hguser;password=nH5L$&Hxxco;SslMode=None",
"dncms": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=dncms;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"companyBaseConf": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=db_company_base_conf;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"crm": "Server=mysql98ff96c3dffa.rds.ivolces.com;Database=db_crm;UserId=qianbenjie;Password=Hcqianbenjie@123;port=3306;",
"crmcloud": "Data Source=dgbigdata.mysql.rds.aliyuncs.com;Port=3306;Initial Catalog=crm_cloud;user id=crm_rd;password=K#RQ1TYx9my;SslMode=None;"
},
"TaskConfig": {
"TaskName": "DG.SynchronousWorker",
"TaskRemarks": "DG.SynchronousWorker",
"Enable": true,
"SecondsDelay": 3600
},
"Exceptionless": {
"ServerUrl": "http://10.22.12.9:5000",
"ApiKey": "tmrp0GmwyTMe6UxJIw7LXAcIWYKEUgy2kprMiybd"
},
"SystemConfig": {
"zxdCoreApi": "http://120.77.165.155:8089",
"nodeWebApi": "http://120.238.224.24:10034",
"crmCoreApi": "http://api.crm.tcfortune.com:8282",
"clearEDDHour": "1"
}
}

View File

@ -0,0 +1,28 @@
{
"ConnectionStrings": {
"zxdcrm": "Server=192.168.11.141;Database=zxdcrm;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"dncmsbase": "Server=192.168.11.41;Database=dncmsbase;UserId=root;Password=sa123456.;port=3306;",
"usercenter": "Server=192.168.11.41;Database=usercenter;UserId=root;Password=sa123456.;port=3306;",
"dncms": "Server=192.168.11.41;Database=dncms;UserId=root;Password=sa123456.;port=3306;",
"companyBaseConf": "Server=192.168.11.141;Database=db_company_base_conf;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"crm": "Server=192.168.11.141;Database=db_crm;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"crmcloud": "Server=192.168.11.141;Database=crm_cloud;UserId=tafadmin;Password=tafadmin2017;port=3306;"
},
"TaskConfig": {
"TaskName": "DG.SynchronousWorker",
"TaskRemarks": "DG.SynchronousWorker",
"Enable": true,
"SecondsDelay": 60
},
"Exceptionless": {
"ServerUrl": "http://10.22.12.9:5000",
"ApiKey": "tmrp0GmwyTMe6UxJIw7LXAcIWYKEUgy2kprMiybd"
},
"SystemConfig": {
"zxdCoreApi": "http://192.168.11.81:8089",
"nodeWebApi": "http://120.238.224.24:10034",
//"crmCoreApi": "https://localhost:7234"
"crmCoreApi": "http://192.168.11.80:8088",
"clearEDDHour": "1"
}
}

6
code/NuGet.Config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>

View File

@ -0,0 +1,16 @@
{
"profiles": {
"ResourceFlowWorker": {
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker": {
"commandName": "Docker",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ResourceFlowWorker.Config
{
public class SystemConfig
{
public string zxdCoreApi { get; set; }
}
}

View File

@ -0,0 +1,23 @@
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["ResourceFlowWorker/ResourceFlowWorker.csproj", "ResourceFlowWorker/"]
COPY ["Zxd.Core.Shared/Zxd.Core.Shared.csproj", "Zxd.Core.Shared/"]
COPY ["Zxd.EntityFramework/Zxd.EntityFramework.csproj", "Zxd.EntityFramework/"]
COPY ["Zxd.Entity/Zxd.Entity.csproj", "Zxd.Entity/"]
RUN dotnet restore "ResourceFlowWorker/ResourceFlowWorker.csproj"
COPY . .
WORKDIR "/src/ResourceFlowWorker"
RUN dotnet build "ResourceFlowWorker.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "ResourceFlowWorker.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ResourceFlowWorker.dll"]

View File

@ -0,0 +1,23 @@
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["WeworkUserWorker/WeworkUserWorker.csproj", "WeworkUserWorker/"]
COPY ["Zxd.Core.Shared/Zxd.Core.Shared.csproj", "Zxd.Core.Shared/"]
COPY ["Zxd.EntityFramework/Zxd.EntityFramework.csproj", "Zxd.EntityFramework/"]
COPY ["Zxd.Entity/Zxd.Entity.csproj", "Zxd.Entity/"]
RUN dotnet restore "WeworkUserWorker/WeworkUserWorker.csproj"
COPY . .
WORKDIR "/src/WeworkUserWorker"
RUN dotnet build "WeworkUserWorker.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "WeworkUserWorker.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WeworkUserWorker.dll"]

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ResourceFlowWorker.Dto
{
internal class LivePageDto<TTableData>
{
/// <summary>
/// 当前页码
/// </summary>
[JsonPropertyName("currentPage")]
public int? CurrentPage { get; set; }
/// <summary>
/// 每页记录数
/// </summary>
[JsonPropertyName("pageSize")]
public int? PageSize { get; set; }
/// <summary>
/// 列表数据
/// </summary>
[JsonPropertyName("tableData")]
public List<TTableData> TableData { get; set; }
/// <summary>
/// 总记录数
/// </summary>
[JsonPropertyName("total")]
public long Total { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ResourceFlowWorker.Dto
{
internal class ResourceFlowCustomerAndUserDto
{
public string? Appid { get; set; }
public string? Userid { get; set; }
}
}

View File

@ -0,0 +1,72 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Zxd.Entity.Dncms;
namespace ResourceFlowWorker.Dto
{
internal class ResourceFlowWorkerDto
{
}
internal class ResourceFlowFromDto
{
public ResourceFlowFromDto(ResourceFlowConfig? config, ResourceFlowConfigFrom? from, List<ResourceCountReturnModel> sourceResources)
{
Config = config;
From = from;
SourceResources = sourceResources;
OperateResources = JsonHelper.FromJson<List<ResourceCountReturnModel>>(sourceResources.ToJson());
AssignedCount = 0;
}
public ResourceFlowConfig Config { get; set; }
public ResourceFlowConfigFrom From { get; set; }
/// <summary>
/// 源人群包信息
/// </summary>
public List<ResourceCountReturnModel> SourceResources { get; set; }
/// <summary>
/// 操作人群包信息
/// </summary>
public List<ResourceCountReturnModel> OperateResources { get; set; }
/// <summary>
/// 已分配数量
/// </summary>
public int AssignedCount { get; set; }
/// <summary>
/// 权重
/// </summary>
public decimal Weight
{
get
{
return SourceResources.Count == 0 ? 0 : (SourceResources.Count - OperateResources.Count) / SourceResources.Count;
}
}
}
internal class ResourceCountQueryDto
{
public string groupids { get; set; }
public string userids { get; set; }
public string appid { get; set; }
public int page { get; set; } = 1;
public int limit { get; set; } = 100000;
}
internal class ResourceCountReturnModel
{
public string? _nickname { get; set; }
public string? _appuserid { get; set; }
public string? _headimgurl { get; set; }
}
}

View File

@ -0,0 +1,67 @@
using Microsoft.Extensions.Configuration;
try
{
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
Console.WriteLine($"Env: {env}");
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env ?? "Production"}.json", true)
.AddJsonFile("Serilog.json")
.AddJsonFile($"Serilog.{env ?? "Production"}.json", true)
.Build();
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(config)
.WriteTo.Exceptionless(config.GetValue<string>("Exceptionless:ApiKey"), config.GetValue<string>("Exceptionless:ServerUrl"), new string[] { "WeworkUserWorker" })
.CreateLogger();
Log.Logger = logger;
Log.Information("Starting ResourceFlowWorker");
IServiceCollection services = new ServiceCollection();
services.AddLogging(logging =>
{
logging.ClearProviders();
logging.AddSerilog();
});
services.AddSingleton<IConfiguration>(config);
services.AddOptions()
.Configure<SystemConfig>(e => config.GetSection("SystemConfig").Bind(e));
ExceptionlessClient.Default.Startup(config.GetValue<string>("Exceptionless:ApiKey"));
ExceptionlessClient.Default.Configuration.ServerUrl = config.GetValue<string>("Exceptionless:ServerUrl");
ExceptionlessClient.Default.Configuration.DefaultTags.Add("zxd-ResourceFlowWorker");
//services.AddRedis(config);
services.AddDGEntityFramework<ZxdDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("zxdcrm"), ServerVersion.AutoDetect(config.GetConnectionString("zxdcrm")));
});
services.AddDGEntityFramework<DncmsbaseDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("dncmsbase"), ServerVersion.AutoDetect(config.GetConnectionString("dncmsbase")));
});
services.AddDGEntityFramework<UserCenterDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("usercenter"), ServerVersion.AutoDetect(config.GetConnectionString("usercenter")));
});
services.AddDGEntityFramework<DncmsDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("dncms"), ServerVersion.AutoDetect(config.GetConnectionString("dncms")));
});
services.AddDGEntityFramework<CompanyBaseConfDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("companyBaseConf"), ServerVersion.AutoDetect(config.GetConnectionString("companyBaseConf")));
});
services.AddWorker(config);
services.AddDGHttpClient();
services.AddRegisterWorker<ResourceWorker>();
var builder = new HostBuilder();
await builder.RunConsoleAsync();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}

View File

@ -0,0 +1,16 @@
{
"profiles": {
"ResourceFlowWorker": {
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker": {
"commandName": "Docker",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,82 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<None Include="..\.dockerignore" Link=".dockerignore">
<DependentUpon>$(DockerDefaultDockerfile)</DependentUpon>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="DG.Core" Version="1.0.25" />
<PackageReference Include="DG.Redis" Version="1.0.17" />
<PackageReference Include="DG.Tool" Version="1.0.9" />
<PackageReference Include="DG.Worker" Version="1.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.2" />
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Sinks.Exceptionless" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Exceptionless" Version="6.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Zxd.Core.Shared\Zxd.Core.Shared.csproj" />
<ProjectReference Include="..\Zxd.EntityFramework\Zxd.EntityFramework.csproj" />
<ProjectReference Include="..\Zxd.Entity\Zxd.Entity.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="System.Text.Json" />
<Using Include="System.Text.Json.Serialization" />
<Using Include="Microsoft.EntityFrameworkCore" />
<Using Include="Microsoft.Extensions.Configuration" />
<Using Include="System.Text.Json" />
<Using Include="System.Text.Json.Serialization" />
<Using Include="DG.Tool" />
<Using Include="DG.Core" />
<Using Include="ResourceFlowWorker.Workers" />
<Using Include="ResourceFlowWorker.Dto" />
<Using Include="DG.Worker" />
<Using Include="Microsoft.Extensions.Configuration" />
<Using Include="Microsoft.Extensions.DependencyInjection" />
<Using Include="Microsoft.Extensions.Logging" />
<Using Include="Serilog" />
<Using Include="DG.EntityFramework" />
<Using Include="Zxd.Entity" />
<Using Include="ResourceFlowWorker.Config" />
<Using Include="Exceptionless" />
<Using Include="Microsoft.Extensions.Hosting" />
<Using Include="Zxd.EntityFramework" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.Disaster.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.Production.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Serilog.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Serilog.Production.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,33 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information",
"Microsoft.EntityFrameworkCore": "Warning",
"System.Net.Http.HttpClient": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"path": "logs/log.log",
"rollingInterval": "3",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}

View File

@ -0,0 +1,33 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information",
"Microsoft.EntityFrameworkCore": "Information",
"System.Net.Http.HttpClient": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"path": "logs/log.log",
"rollingInterval": "3",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}",
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}

View File

@ -0,0 +1,306 @@
using Exceptionless.Utility;
using Microsoft.Extensions.Configuration;
using ResourceFlowWorker.Config;
using ResourceFlowWorker.Dto;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Zxd.Entity.Dncms;
using Zxd.EntityFramework;
namespace ResourceFlowWorker.Workers
{
internal class ResourceWorker : WorkerBase, IDisposable
{
private readonly IConfiguration _configuration;
private readonly IServiceProvider _serviceProvider;
private readonly IHttpClient _httpClient;
private readonly ILogger<ResourceWorker> _logger;
private readonly SystemConfig? _systemConfig;
private static bool IsFrist = true;
public ResourceWorker(ILogger<ResourceWorker> logger,
IServiceProvider serviceProvider,
IConfiguration configuration,
IHttpClient httpClient
) : base(logger)
{
_logger = logger;
_configuration = configuration;
_serviceProvider = serviceProvider;
_systemConfig = configuration.GetSection("SystemConfig").Get<SystemConfig>();
_httpClient = httpClient;
}
/// <summary>
///
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
protected override async Task DoWorkAsync()
{
var status = new List<ResourceConfigStatus> { ResourceConfigStatus. };
if (IsFrist)
{
IsFrist = !IsFrist;
status.Add(ResourceConfigStatus.);
status.Add(ResourceConfigStatus.);
}
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsDbContext>>();
var configs = await dncmsRepository.GetRepository<ResourceFlowConfig>().Query()
.Where(x => status.Contains(x.Status)).ToListAsync();
if (configs.Any())
{
configs.ForEach(x => x.Status = ResourceConfigStatus.);
await dncmsRepository.GetRepository<ResourceFlowConfig>().BatchUpdateAsync(configs, x => new { x.Status });
foreach (var config in configs)
{
var fromList = await dncmsRepository.GetRepository<ResourceFlowConfigFrom>().Query()
.Where(x => x.ConfigId == config.Id).ToListAsync();
var toList = await dncmsRepository.GetRepository<ResourceFlowConfigTo>().Query()
.Where(x => x.ConfigId == config.Id).ToListAsync();
await DoDistribute(config, fromList, toList);
}
}
}
/// <summary>
/// 执行分配
/// </summary>
/// <returns></returns>
private async Task DoDistribute(ResourceFlowConfig config, List<ResourceFlowConfigFrom> froms, List<ResourceFlowConfigTo> tos)
{
var flowFroms = new List<ResourceFlowFromDto>();
foreach (var from in froms)
{
var sourceResourceResult = await _httpClient.GetAsync<ApiResult<LivePageDto<ResourceCountReturnModel>>>($"{_systemConfig.zxdCoreApi}/Api/WeWorkResource/KFResourceCount?groupids={config.GroupId}&userids={from.Userid}&appid={config.Appid}&page=1&limit=10000");
if (sourceResourceResult.Code == 0)
{
var sourceResources = sourceResourceResult.Data.TableData;
// 判断是否离职
if (!sourceResources.Any())
{
sourceResourceResult = await _httpClient.GetAsync<ApiResult<LivePageDto<ResourceCountReturnModel>>>($"{_systemConfig.zxdCoreApi}/Api/WeWorkResource/KFResourceCount?groupids={config.GroupId}&userids={from.Userid}&appid={config.Appid}&page=1&limit=10000&status=0&subscribe=-4");
if (sourceResourceResult.Code == 0)
{
sourceResources = sourceResourceResult.Data.TableData;
}
}
flowFroms.Add(new ResourceFlowFromDto(config, from, sourceResources));
}
}
var logs = new List<ResourceFlowLog>();
// 离职员工分配逻辑
// 目标数量
var flowCount = config.FlowCount;
var assignedCount = 0;
var fromUserids = froms.Select(x => x.Userid).ToList();
var toUserids = tos.Select(n => n.Userid).ToList();
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsDbContext>>();
var dimissiones = await dncmsRepository.GetRepository<ResourceFlowDimission>().Query()
.Where(x => x.Appid == config.Appid && fromUserids.Contains(x.FromUserid)).ToListAsync();
if (dimissiones.Count > 0)
{
Log.Information("处理离职数据");
Log.Information($"处理离职数据{dimissiones.Count}条");
foreach (var flowFrom in flowFroms)
{
var list = dimissiones.Where(x => x.FromUserid == flowFrom.From.Userid).ToList();
foreach (var item in list)
{
if (flowFrom.OperateResources.Any(y => y._appuserid == item.Appuserid))
{
var resource = flowFrom.OperateResources.Where(y => y._appuserid == item.Appuserid).First();
logs.Add(new ResourceFlowLog
{
Deptid = config.Deptid,
Appid = config.Appid,
Appuserid = item.Appuserid,
FromUserid = flowFrom.From.Userid,
ConfigId = config.Id,
FlowTime = DateTime.Now,
Status = ResourceFlowStatus.,
IsDimission = true,
});
flowFrom.AssignedCount++;
assignedCount++;
flowFrom.From.TransferCount++;
flowFrom.OperateResources.Remove(resource);
if (flowCount == assignedCount) break;
}
}
if (flowCount == assignedCount) break;
}
}
// 相同人群包 已分配日志
var allOldFromLog = await dncmsRepository.GetRepository<ResourceFlowLog>().Query().Where(x => x.Appid == config.Appid && fromUserids.Contains(x.FromUserid) && x.Status != ResourceFlowStatus.).ToListAsync();
var oldFromLog = new List<ResourceFlowLog>();
if (config.GroupId > 0)
{
oldFromLog = allOldFromLog.Where(x => x.GroupId == config.GroupId).ToList();
}
//最近一天的 不让重新分配
var notPassFromLog = allOldFromLog.Where(n => n.FlowTime >= DateTime.Now.AddDays(-1)).ToList();
oldFromLog.AddRange(notPassFromLog);
var oldToLog = await dncmsRepository.GetRepository<ResourceFlowLog>().Query()
.Where(x => x.Appid == config.Appid && toUserids.Contains(x.ToUserid) && x.GroupId == config.GroupId).ToListAsync();
while (flowCount > assignedCount)
{
// 如果队列已经不够好友,跳出循环
if (!flowFroms.Any()) { Log.Information("队列已经不够好友,跳出循环"); break; }
// 确定权重
var first = flowFroms.OrderByDescending(x => x.Weight).First();
// 如果客服无可分配资源,移除分配队列,重新选择客服
if (first.OperateResources.Count == 0)
{
Log.Information($"客服{first.From.Ename}无可分配资源");
flowFroms.Remove(first);
continue;
}
// 随机指定用户
var n = new Random().Next(first.OperateResources.Count);
// 获取随机用户
var resource = first.OperateResources[n];
if (logs.Any(x => x.Appid == config.Appid && x.Appuserid == resource._appuserid))
{
Log.Information($"资源已分配,移除队列后重新分配");
first.OperateResources.Remove(resource);
continue;
}
if (oldFromLog.Any(x => x.Appuserid == resource._appuserid && x.FromUserid == first.From.Userid))
{
Log.Information($"同一个人群包内,已转移过的资源不可再次发起转移{config.Id}【{resource._appuserid}】");
first.OperateResources.Remove(resource);
continue;
}
logs.Add(new ResourceFlowLog
{
Deptid = config.Deptid,
Appid = config.Appid,
Appuserid = resource._appuserid,
FromUserid = first.From.Userid,
ConfigId = config.Id,
FlowTime = DateTime.Now,
Status = ResourceFlowStatus.,
IsDimission = false,
GroupId = config.GroupId,
});
// 移除其他客服的相同好友
foreach (var from in flowFroms)
{
var sameResource = from.OperateResources.FirstOrDefault(x => x._appuserid == resource._appuserid);
if (sameResource != null)
{
if (from.From.Id != first.From.Id)
{
if (oldFromLog.Any(x => x.Appuserid == resource._appuserid && x.FromUserid == from.From.Userid))
{
Log.Information($"同一个人群包内,已转移过的资源不可再次发起转移{config.Id}【{resource._appuserid}】");
first.OperateResources.Remove(sameResource);
continue;
}
logs.Add(new ResourceFlowLog
{
Deptid = config.Deptid,
Appid = config.Appid,
Appuserid = resource._appuserid,
FromUserid = from.From.Userid,
ConfigId = config.Id,
FlowTime = DateTime.Now,
Status = ResourceFlowStatus.,
IsDimission = false,
GroupId = config.GroupId,
});
from.AssignedCount++;
from.From.TransferCount++;
}
from.OperateResources.Remove(sameResource);
}
}
first.AssignedCount++;
first.From.TransferCount++;
assignedCount++;
}
//remove时候刷新不对
/* foreach (var flowFrom in flowFroms)
{
flowFrom.From.TransferCount = flowFrom.SourceResources.Count - flowFrom.OperateResources.Count;
Log.Information($"客服{flowFrom.From.Ename}转移数量{flowFrom.From.TransferCount}");
}*/
// 分配新客服
var toAssignedCount = 0;
Log.Information($"分配新客服");
//历史客户有转移的日志 则直接分配给目标人
var besidesUser = oldToLog.Select(n => n.Appuserid).ToList();
foreach (var to in tos)
{
//客户存在 转移历史 则直接把他装给历史转移对象 资源归一
var oldToUser = oldToLog.Where(x => x.ToUserid == to.Userid).Select(n => n.Appuserid).ToList();
var toLog = logs.Where(n => oldToUser.Contains(n.Appuserid)).Select(n => n.Appuserid).Distinct().ToList();
var toflowCount = to.FlowCount;
List<string> appuserList = new List<string>();
if (toLog.Count > 0)
{
appuserList.AddRange(toLog);
toflowCount = toLog.Count > toflowCount ? 0 : toflowCount - toLog.Count;
}
var otheruserid = logs.Where(n => !besidesUser.Contains(n.Appuserid))
.Select(n => n.Appuserid).Distinct().Take(toflowCount).ToList();
appuserList.AddRange(otheruserid);
var resources = logs.Where(n => appuserList.Contains(n.Appuserid)).ToList();
if (!resources.Any())
{
Log.Error("分配数量不足,跳出!");
continue;
}
resources.ForEach(x =>
{
x.ToUserid = to.Userid;
});
Log.Information($"{to.Userid}_get_{string.Join(",", resources.Select(n => n.Appuserid))}");
var allocationUser = resources.Select(n => n.Appuserid).Distinct().ToList();
besidesUser.AddRange(appuserList);
besidesUser = besidesUser.Distinct().ToList();
}
config.Status = ResourceConfigStatus.;
using var transaction = await dncmsRepository.BeginTransactionAsync();
try
{
await dncmsRepository.GetRepository<ResourceFlowConfig>().UpdateAsync(config, x => new { x.Status });
await dncmsRepository.GetRepository<ResourceFlowConfigFrom>().BatchUpdateAsync(froms, x => new { x.TransferCount });
await dncmsRepository.GetRepository<ResourceFlowLog>().BatchInsertAsync(logs);
await transaction.CommitAsync();
}
catch (Exception ex)
{
Log.Error(ex, "执行分配服务报错!");
await transaction.RollbackAsync();
await transaction.DisposeAsync();
}
}
public void Dispose()
{
_logger.LogInformation("任务正在关闭");
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsDbContext>>();
var configs = dncmsRepository.GetRepository<ResourceFlowConfig>().Query()
.Where(x => x.Status == ResourceConfigStatus.).ToList();
configs.ForEach(x => x.Status = ResourceConfigStatus.);
dncmsRepository.GetRepository<ResourceFlowConfig>().BatchUpdateAsync(configs, x => new { x.Status });
}
}
}

View File

@ -0,0 +1,23 @@
{
"ConnectionStrings": {
"zxdcrm": "Data Source=10.22.15.61;Port=3306;Initial Catalog=zxdcrm;user id=qianbenjie;password=Hcqianbenjie@123;Old Guids=true;SslMode=None",
"dncmsbase": "Data Source=10.22.15.68;Port=3306;Initial Catalog=dncmsbase;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"usercenter": "Data Source=10.22.15.68;Port=3306;Initial Catalog=usercenter;user id=hguser;password=nH5L$&Hxxco;SslMode=None",
"dncms": "Data Source=10.22.15.68;Port=3306;Initial Catalog=dncms;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"companyBaseConf": "Data Source=10.22.15.68;Port=3306;Initial Catalog=db_company_base_conf;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None"
},
"TaskConfig": {
"TaskName": "Zxd-ResourceWorker",
"TaskRemarks": "Zxd-ResourceWorker",
"Enable": true,
"SecondsDelay": 5
},
"Exceptionless": {
"ServerUrl": "http://10.22.11.9:5000",
"ApiKey": "UdbvVSdTs1i8lGsaO7hF7MkOil5ArONoTLaEJePn"
},
"SystemConfig": {
"zxdCoreApi": "http://120.77.165.155:8089",
"nodeWebApi": "http://10.22.15.5:8080"
}
}

View File

@ -0,0 +1,22 @@
{
"ConnectionStrings": {
"zxdcrm": "Data Source=mysql98ff96c3dffa.rds.ivolces.com;Port=3306;Initial Catalog=zxdcrm;user id=qianbenjie;password=Hcqianbenjie@123;Old Guids=true;SslMode=None",
"dncmsbase": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=dncmsbase;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"usercenter": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=usercenter;user id=hguser;password=nH5L$&Hxxco;SslMode=None",
"dncms": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=dncms;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"companyBaseConf": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=db_company_base_conf;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None"
},
"TaskConfig": {
"TaskName": "Zxd-ResourceWorker",
"TaskRemarks": "Zxd-ResourceWorker",
"Enable": true,
"SecondsDelay": 5
},
"Exceptionless": {
"ServerUrl": "http://10.22.11.9:5000",
"ApiKey": "UdbvVSdTs1i8lGsaO7hF7MkOil5ArONoTLaEJePn"
},
"SystemConfig": {
"zxdCoreApi": "http://120.77.165.155:8089"
}
}

View File

@ -0,0 +1,22 @@
{
"ConnectionStrings": {
"zxdcrm": "Server=192.168.11.141;Database=zxdcrm;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"dncmsbase": "Server=192.168.11.41;Database=dncmsbase;UserId=root;Password=sa123456.;port=3306;",
"usercenter": "Server=192.168.11.41;Database=usercenter;UserId=root;Password=sa123456.;port=3306;",
"dncms": "Server=192.168.11.41;Database=dncms;UserId=root;Password=sa123456.;port=3306;",
"companyBaseConf": "Server=192.168.11.141;Database=db_company_base_conf;UserId=tafadmin;Password=tafadmin2017;port=3306;"
},
"TaskConfig": {
"TaskName": "Zxd-ResourceWorker",
"TaskRemarks": "Zxd-ResourceWorker",
"Enable": true,
"SecondsDelay": 5
},
"Exceptionless": {
"ServerUrl": "http://10.22.12.9:5000",
"ApiKey": "AukFjLDBeABakHa1jS67DXoRFPh5J6TdVjDqFMwx"
},
"SystemConfig": {
"zxdCoreApi": "http://192.168.11.81:8089"
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ToDoWorker.Config
{
public class SystemConfig
{
public string zxdCoreApi { get; set; }
public string BdMarkting { get; set; }
}
}

View File

@ -0,0 +1,23 @@
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["ResourceFlowWorker/ResourceFlowWorker.csproj", "ResourceFlowWorker/"]
COPY ["Zxd.Core.Shared/Zxd.Core.Shared.csproj", "Zxd.Core.Shared/"]
COPY ["Zxd.EntityFramework/Zxd.EntityFramework.csproj", "Zxd.EntityFramework/"]
COPY ["Zxd.Entity/Zxd.Entity.csproj", "Zxd.Entity/"]
RUN dotnet restore "ResourceFlowWorker/ResourceFlowWorker.csproj"
COPY . .
WORKDIR "/src/ResourceFlowWorker"
RUN dotnet build "ResourceFlowWorker.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "ResourceFlowWorker.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ResourceFlowWorker.dll"]

View File

@ -0,0 +1,495 @@
using DG.EntityFramework;
using DG.Redis;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using ToDoWorker.Helper;
using Zxd.Core.Shared;
using Zxd.Core.Shared.Helpers;
using Zxd.Entity.Action;
using Zxd.Entity.dim;
using Zxd.Entity.Zxd;
using Zxd.EntityFramework;
namespace ToDoWorker.Domain
{
public class EventDomain : IEventDomain
{
private readonly IBaseRepository<ZxdDbContext> _repository;
private readonly IBaseRepository<CrmCloudDbContext> _actRepository;
private readonly IConfiguration _configuration;
private readonly SystemConfig? _systemConfig;
private readonly IHttpClient _httpClient;
private readonly IRedisManager _redisManager;
private static string _queueskey = "queue:todoItemQueues";
private static string _cachekey = "queue:cacheItem";
private readonly IMapper _mapper;
public EventDomain(
IBaseRepository<ZxdDbContext> zxdRepository, IBaseRepository<CrmCloudDbContext> actRepository, IMapper mapper,
IConfiguration configuration, IHttpClient httpClient, IRedisManager redisManager)
{
_repository = zxdRepository;
_mapper = mapper;
_actRepository = actRepository;
_configuration = configuration;
_systemConfig = _configuration.GetSection("SystemConfig").Get<SystemConfig>();
_httpClient = httpClient;
_redisManager = redisManager;
}
public async Task<bool> RunEvent(EventDto eventDto)
{
if (!eventDto.deptid.HasValue)
{
Log.Error($"无部门不处理{eventDto.ToJson()}");
return false;
}
try
{
var deptSet = await GetSetting(eventDto);
if (deptSet == null)
{
Log.Error($"没找到相关的业务线配置{eventDto.ToJson()}");
return false;
}
CustomerBehaviorLog newModel = new CustomerBehaviorLog
{
act_date = Convert.ToDateTime(eventDto.act_date),
appid = eventDto.appid,
umid = eventDto.umid,
appuserid = eventDto.appuserid,
resid = eventDto.resid,
uid = eventDto.uid,
unionid = eventDto.unionid,
customerid = eventDto.customerid,
user_type = eventDto.user_type,
eventid = eventDto.eventid,
eventname = eventDto.eventname,
eventmemo = eventDto.eventmemo,
others = eventDto.others,
scenetype = eventDto.scenetype,
scenetypename = eventDto.scenetypename,
sceneid = eventDto.sceneid,
sceneidname = eventDto.sceneidname,
scenename = eventDto.scenename,
sceneext = eventDto.sceneext,
ip = eventDto.ip,
channel = eventDto.channel,
deptid = eventDto.deptid.GetValueOrDefault(),
productplat = eventDto.productplat,
productplatname = eventDto.productplatname,
sinkClickhouseTable = eventDto.sinkClickhouseTable,
updatetime = eventDto.update_time,
ctime = DateTime.Now,
eventtypename = deptSet.memo,
neweventid = deptSet.neweventid,
neweventname = deptSet.remark,
};
if (eventDto.act_time.HasValue)
{
newModel.act_time = ConvertHelper.JavaLongToDateTime(eventDto.act_time.Value);
}
if (eventDto.leavetime.HasValue)
{
newModel.leavetime = ConvertHelper.JavaLongToDateTime(eventDto.leavetime.Value);
}
newModel.Content = GetNoticeContent(newModel, deptSet);
//请求大数据接口获取用户昵称和头像
// 新增优品信息
using var transaction = await _actRepository.BeginTransactionAsync();
var url = $"{_systemConfig.BdMarkting}/user/relations?uid={newModel.uid}";
var empData = await _httpClient.GetAsync<CommonApiResult<List<CustomerEmployeeMap>>>(url);
if (string.IsNullOrWhiteSpace(newModel.umid))
{
Log.Information($"bigdata: {newModel.uid} {empData.ToJson()}");
//var resid = empData.data.FirstOrDefault(n => !string.IsNullOrWhiteSpace(n.resid))?.resid;
var umid = empData.data.FirstOrDefault(n => !string.IsNullOrWhiteSpace(n.umid))?.umid;
//newModel.resid = resid;
newModel.umid = umid;
if (string.IsNullOrWhiteSpace(newModel.umid))
{
// 任务重试入队
// 只重试两次
if (eventDto.RetryCount <= 1)
{
var setting = await GetDelaySec();
var delaySec = setting == null ? 120 : setting.delaytime.Value;
eventDto.RetryCount++;
eventDto.TaskTime = DateTime.Now.AddSeconds(delaySec);
await _redisManager.EnqueueAsync(_queueskey, eventDto);
Log.Information($"reTry{eventDto.uid}【{umid}】");
return true;
}
}
}
//插入行为日志
await _actRepository.GetRepository<CustomerBehaviorLog>().InsertAsync(newModel);
List<EmployeeTodoitem> todoItemList = new List<EmployeeTodoitem>();
if (empData != null && empData.code == 0)
{
var customerEmps = empData.data.Where(n => n.deptid == newModel.deptid).ToList();
if (!deptSet.allNotice)
{
if (deptSet.firstCtime.HasValue && deptSet.firstCtime.Value)
{
var firemp = customerEmps.OrderBy(n => n.create_time).FirstOrDefault();
if (firemp != null)
{
customerEmps = new List<CustomerEmployeeMap> { firemp };
}
}
}
if (customerEmps.Count == 0)
{
if (deptSet.isforce.Value)
{
EmployeeTodoitem todoitem = new EmployeeTodoitem
{
logid = newModel.id,
done = false,
deptid = newModel.deptid,
ctime = DateTime.Now,
utime = DateTime.Now,
act_date = Convert.ToDateTime(newModel.act_date),
act_time = newModel.act_time,
appid = newModel.appid,
appuserid = newModel.appuserid,
resid = newModel.resid,
umid = newModel.umid,
unionid = newModel.unionid,
customerid = newModel.customerid,
uid = newModel.uid,
user_type = newModel.user_type,
eventid = newModel.eventid,
eventname = newModel.eventname,
eventtypename = newModel.eventtypename,
eventmemo = newModel.eventmemo,
others = newModel.others,
scenetype = newModel.scenetype,
scenetypename = newModel.scenetypename,
sceneid = newModel.sceneid,
sceneidname = newModel.sceneidname,
scenename = newModel.scenename,
sceneext = newModel.sceneext,
ip = newModel.ip,
channel = newModel.channel,
productplat = newModel.productplat,
productplatname = newModel.productplatname,
leavetime = newModel.leavetime,
neweventid = newModel.neweventid,
neweventname = newModel.neweventname,
content = newModel.Content,
isread = 0
};
await _actRepository.GetRepository<EmployeeTodoitem>().InsertAsync(todoitem);
}
await transaction.CommitAsync();
return true;
}
//只通知 企微好友 归属关系
List<CustomerEmployeeMap> resultEmp = new List<CustomerEmployeeMap>();
if (deptSet.is_wework)
{
resultEmp.AddRange(customerEmps.Where(n => n.is_wework == 1).ToList());
}
if (deptSet.is_mobile)
{
resultEmp.AddRange(customerEmps.Where(n => n.is_mobile == 1).ToList());
}
if (deptSet.is_belong)
{
resultEmp.AddRange(customerEmps.Where(n => n.is_belong == 1).ToList());
}
resultEmp = resultEmp.Distinct().ToList();
foreach (var item in resultEmp)
{
EmployeeTodoitem todoitem = new EmployeeTodoitem
{
logid = newModel.id,
eid = item.eid.Value,
ename = item.employee_name,
done = false,
deptid = newModel.deptid,
ctime = DateTime.Now,
utime = DateTime.Now,
Nickname = !string.IsNullOrWhiteSpace(item.Username) ? item.Username : item.Nickname,
Headimgurl = item.Headimgurl,
act_date = Convert.ToDateTime(newModel.act_date),
act_time = newModel.act_time,
appid = newModel.appid,
appuserid = newModel.appuserid,
resid = newModel.resid,
unionid = newModel.unionid,
umid = newModel.umid,
customerid = newModel.customerid,
uid = newModel.uid,
user_type = newModel.user_type,
eventid = newModel.eventid,
eventname = newModel.eventname,
eventtypename = newModel.eventtypename,
eventmemo = newModel.eventmemo,
others = newModel.others,
scenetype = newModel.scenetype,
scenetypename = newModel.scenetypename,
sceneid = newModel.sceneid,
sceneidname = newModel.sceneidname,
scenename = newModel.scenename,
sceneext = newModel.sceneext,
ip = newModel.ip,
channel = newModel.channel,
productplat = newModel.productplat,
productplatname = newModel.productplatname,
leavetime = newModel.leavetime,
neweventid = newModel.neweventid,
neweventname = newModel.neweventname,
content = newModel.Content,
isread = 0
};
todoItemList.Add(todoitem);
}
if (todoItemList.Count > 0)
{
await _actRepository.GetRepository<EmployeeTodoitem>().BatchInsertAsync(todoItemList);
}
await transaction.CommitAsync();
}
}
catch (Exception ex)
{
Log.Error($"处理待办事项出错{ex.Message}【{eventDto.ToJson()}】");
}
return true;
}
public async Task<ToDoItemDeptMap> GetSetting(EventDto eventDto)
{
var cacheKey = "ToDoItemSetting";
var set = await _redisManager.GetAsync<ToDoItemSetting>(cacheKey);
if (set == null)
{
var setting = await _repository.GetRepository<BAS_PARAMETER>().Query().FirstOrDefaultAsync(n => n.PARAKEY == "ToDoItemSetting");
if (setting != null && !string.IsNullOrWhiteSpace(setting.PARAVALUE))
{
set = JsonHelper.FromJson<ToDoItemSetting>(setting.PARAVALUE);
await _redisManager.SetAsync<ToDoItemSetting>(cacheKey, set);
}
}
var deptSetList = set.setting.Where(n => n.eventid == eventDto.eventid).ToList();
var deptSet = deptSetList.FirstOrDefault();
foreach (var item in deptSetList)
{
deptSet = null;
if (!string.IsNullOrWhiteSpace(item.source) && !item.source.Contains(eventDto.sinkClickhouseTable))
{
continue;
}
if (item.scenetype.Count > 0 && !item.scenetype.Contains(eventDto.scenetype))
{
continue;
}
if (item.sceneid.Count > 0 && !item.sceneid.Contains(eventDto.sceneid))
{
continue;
}
if (item.deptids.Count > 0 && !item.deptids.Contains(eventDto.deptid.Value))
{
continue;
}
if (item.appids.Count > 0 && !item.appids.Contains(eventDto.appid))
{
continue;
}
//找到了 看是否配置了场景
deptSet = item;
var sceneItem = deptSetList.FirstOrDefault(n => n.scenetype.Contains(eventDto.scenetype));
if (sceneItem != null)
{
deptSet = sceneItem;
}
break;
}
return deptSet;
}
public async Task<ToDoItemSetting> GetDelaySec()
{
var cacheKey = "ToDoItemSetting";
var set = await _redisManager.GetAsync<ToDoItemSetting>(cacheKey);
if (set == null)
{
var setting = await _repository.GetRepository<BAS_PARAMETER>().Query().FirstOrDefaultAsync(n => n.PARAKEY == "ToDoItemSetting");
if (setting != null && !string.IsNullOrWhiteSpace(setting.PARAVALUE))
{
set = JsonHelper.FromJson<ToDoItemSetting>(setting.PARAVALUE);
await _redisManager.SetAsync(cacheKey, set);
}
}
return set;
}
private string GetNoticeContent(CustomerBehaviorLog log, ToDoItemDeptMap set)
{
var content = set.template;
Type type = typeof(CustomerBehaviorLog);
PropertyInfo[] propertyInfo = type.GetProperties();
foreach (var item in propertyInfo)
{
var value = item.GetValue(log);
var setValue = value == null ? "" : value.ToString();
var name = "{" + item.Name + "}";
if (name == "{act_date}" && content.Contains(name))
{
content = content.Replace(name, Convert.ToDateTime(setValue).ToString("yyyy-MM-dd"));
continue;
}
if (content.Contains(name))
{
content = content.Replace(name, setValue);
}
}
content = content.Replace("----", "-").Replace("---", "-").Replace("--", "-").Trim('-');
return content;
}
/// <summary>
/// 过滤登录事件
/// </summary>
/// <param name="eventDto"></param>
/// <returns></returns>
public async Task<bool> FilterLogEvent(EventDto eventDto, ToDoItemDeptMap setting)
{
List<string> redisKey = new List<string>();
var residkey = setting.residkey;
var useridkey = setting.key;
if (string.IsNullOrWhiteSpace(useridkey))
{
return true;
}
redisKey.Add(useridkey);
if (!string.IsNullOrWhiteSpace(residkey))
{
redisKey.Add(residkey);
}
if (eventDto.eventid == (int)EventEnum. && eventDto.sceneid == "soft.login")
{
return false;
}
if (string.IsNullOrWhiteSpace(eventDto.appuserid))
{
Log.Information($"appuserid为空{eventDto.uid}");
return false;
}
foreach (var rkey in redisKey)
{
//resid缓存 resid不等于空才生效
if (rkey.Contains("resid") && string.IsNullOrWhiteSpace(eventDto.resid))
{
continue;
}
var key = rkey.ToString();
Type type = typeof(EventDto);
PropertyInfo[] propertyInfo = type.GetProperties();
foreach (var item in propertyInfo)
{
var value = item.GetValue(eventDto);
var setValue = value == null ? "" : value.ToString();
var name = "{" + item.Name + "}";
if (key.Contains(name))
{
key = key.Replace(name, setValue);
}
}
if (key.Contains("date_"))
{
var replaceStr = key.Split("date_").LastOrDefault();
var date = DateTime.Now.ToString(replaceStr.Trim('}').ToString());
var dateStr = "{date_" + replaceStr;
key = key.Replace(dateStr, date);
}
//如果redis存在对应的键值 则只出现一次通知
if (await _redisManager.ExistsAsync(key))
{
return false;
}
else
{
await _redisManager.SetAsync(key, 1, TimeSpan.FromDays(1));
}
}
return true;
}
public async Task<bool> Repairdata()
{
var sql = @"select * from eventdwd";
var data = await _actRepository.ExecuteSqlToListAsync<Eventdwd>(sql);
foreach (var item in data)
{
EventDto eventDto = new EventDto()
{
act_date = item.act_date.ToString(),
appid = item.appid,
appuserid = item.appuserid,
resid = item.resid,
unionid = item.unionid,
channel = item.channel,
customerid = item.customerid,
deptid = item.deptid,
eventid = item.eventid,
eventmemo = item.eventmemo,
eventname = item.eventname,
ip = item.ip,
others = item.others,
productplat = item.productplat,
productplatname = item.productplatname,
sceneext = item.sceneext,
sceneid = item.sceneid,
sceneidname = item.sceneidname,
scenetype = item.scenetype,
scenetypename = item.scenetypename,
sinkClickhouseTable = "手工推送",
uid = item.uid,
user_type = item.user_type,
with_eid = item.with_eid,
with_groupid = item.with_groupid,
with_orgid = item.with_orgid
};
if (item.act_time.HasValue)
{
eventDto.act_time = ConvertDataTimeLong(item.act_time.Value);
}
if (item.leavetime.HasValue)
{
eventDto.leavetime = ConvertDataTimeLong(item.leavetime.Value);
}
await RunEvent(eventDto);
}
return true;
}
/// <summary>
/// 将DateTime类型转换为long类型
/// </summary>
/// <param name="dt">时间</param>
/// <returns></returns>
public static long ConvertDataTimeLong(DateTime dt)
{
//dateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000
DateTime dtBase = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
TimeSpan toNow = dt.ToUniversalTime().Subtract(dtBase);
long timeStamp = toNow.Ticks / 10000;
return timeStamp;
}
}
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ToDoWorker.Domain
{
public interface IEventDomain : IScopedDependency
{
/// <summary>
/// 获取活动名称
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
Task<bool> RunEvent(EventDto request);
/// <summary>
/// 修复数据
/// </summary>
/// <returns></returns>
Task<bool> Repairdata();
Task<ToDoItemDeptMap> GetSetting(EventDto eventDto);
Task<ToDoItemSetting> GetDelaySec();
/// <summary>
/// 过滤登录事件
/// </summary>
/// <param name="eventDto"></param>
/// <returns></returns>
Task<bool> FilterLogEvent(EventDto eventDto, ToDoItemDeptMap setting);
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ToDoWorker.Dto
{
public class CustomerEmployeeMap
{
public int? customerid { get; set; }
public int? eid { get; set; }
public int? deptid { get; set; }
public int? groupid { get; set; }
public string? resid { get; set; }
public string? umid { get; set; }
public string? employee_name { get; set; }
public string? create_time { get; set; }
public int? is_wework { get; set; }
public int? is_mobile { get; set; }
public int? is_belong { get; set; }
public string? Nickname { get; set; }
public string? Username { get; set; }
public string? Headimgurl { get; set; }
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ToDoWorker.Dto
{
public class KafkaEvent
{
public string? data { get; set; }
public string? tableName { get; set; }
}
public class TestDto
{
public string? act_date { get; set; }
}
public class EventDto
{
public string? act_date { get; set; }
public long? act_time { get; set; }
public string? appid { get; set; }
public string? appuserid { get; set; }
public int? channel { get; set; }
public int? customerid { get; set; }
public int? deptid { get; set; }
public string? env { get; set; }
public int eventid { get; set; }
public string? eventmemo { get; set; }
public string? eventname { get; set; }
public string? ip { get; set; }
public string? others { get; set; }
public string? productplat { get; set; }
public string? productplatname { get; set; }
public string? resid { get; set; }
public string? umid { get; set; }
public string? sceneext { get; set; }
public string? sceneid { get; set; }
public string? sceneidname { get; set; }
public string? scenename { get; set; }
public int? scenetype { get; set; }
public string? scenetypename { get; set; }
public string? sinkClickhouseTable { get; set; }
public int? uid { get; set; }
public string? unionid { get; set; }
public int? user_type { get; set; }
public int? with_eid { get; set; }
public int? with_groupid { get; set; }
public int? with_orgid { get; set; }
public long? leavetime { get; set; }
public DateTime? update_time { get; set; }
public DateTime TaskTime { get; set; }
public int RetryCount { get; set; }
}
}

View File

@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ToDoWorker.Dto
{
public class ToDoItemSetting
{
/// <summary>
/// 延迟时间 默认120秒
/// </summary>
public int? delaytime { get; set; }
public int? batchSize { get; set; } = 100;
public List<ToDoItemDeptMap> setting { get; set; }
}
public class ToDoItemDeptMap
{
public int? neweventid { get; set; }
/// <summary>
/// 事件id
/// </summary>
public int? eventid { get; set; }
/// <summary>
/// 大类
/// </summary>
public string? remark { get; set; }
/// <summary>
/// 小类
/// </summary>
public string? memo { get; set; }
/// <summary>
/// 模板
/// </summary>
public string? template { get; set; }
/// <summary>
/// 场景
/// </summary>
public List<int?> scenetype { get; set; } = new List<int?>();
/// <summary>
/// 具体场景
/// </summary>
public List<string?> sceneid { get; set; } = new List<string?>();
/// <summary>
/// 是否通知所有好友
/// </summary>
public bool allNotice { set; get; } = true;
/// <summary>
/// 用户类型
/// </summary>
public List<SByte> userType { get; set; }
/// <summary>
/// 只通知最早的那个客服
/// </summary>
public bool? firstCtime { get; set; } = true;
public List<int> deptids { get; set; } = new List<int>();
/// <summary>
/// 通知好友关系
/// </summary>
public bool is_wework { get; set; } = true;
/// <summary>
/// 通知归属关系
/// </summary>
public bool is_mobile { get; set; } = true;
/// <summary>
/// 通知成交关系
/// </summary>
public bool is_belong { get; set; } = true;
/// <summary>
/// Appid
/// </summary>
public List<string> appids { get; set; } = new List<string>();
public string? source { get; set; }
/// <summary>
/// 当日唯一健值
/// </summary>
public string? key { get; set; }
/// <summary>
/// 当日唯一健值 (resid)
/// </summary>
public string? residkey { get; set; }
/// <summary>
/// 没有归属强制插入
/// </summary>
public bool? isforce { get; set; } = false;
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ToDoWorker.Helper
{
public enum EventEnum : int
{
[Description("点击")]
= 1,
[Description("报名")]
= 2,
[Description("登录")]
= 6,
[Description("关注")]
= 7,
[Description("取关")]
= 8,
[Description("听课")]
= 17,
[Description("聊天")]
= 20,
[Description("互动精选")]
= 45,
}
}

View File

@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.Text;
using System.Threading.Tasks;
using Zxd.SqlSugar;
namespace ToDoWorker.Helper
{
public class SeqIdGen
{
//起始的时间戳 2020-01-01 00 00 00 开始
private static long START_STMP = 1577808000000L;
//每一部分占用的位数
private static int SEQUENCE_BIT = 12; //序列号占用的位数
private static int MACHINE_BIT = 6; //机器标识占用的位数
private static int DATACENTER_BIT = 3;//数据中心占用的位数
//每一部分的最大值
private static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
private static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
private static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
//每一部分向左的位移
private static int MACHINE_LEFT = SEQUENCE_BIT;
private static int DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
private static int TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
private long datacenterId = 1; //数据中心
private long machineId = 1; //机器标识
private long sequence = 0L; //序列号
private long lastStmp = -1L;//上一次时间戳
private static Random r = new Random();
private static readonly object _locker = new object();
private static readonly object _Idlocker = new object();
private static SeqIdGen _instance = (SeqIdGen)null;
public static SeqIdGen Instance
{
get
{
if (_instance == null)
{
lock (_locker)
{
if (_instance == null)
_instance = new SeqIdGen();
}
}
return SeqIdGen._instance;
}
}
private SeqIdGen()
{
// SeqIdGenInitRedis();
SeqIdGenInitConfig(); //从appsetting取
}
public void SeqIdGenInitConfig()
{
var _machineId = long.Parse(InitConfiguration.GetSection("machineId").Value.ToString());
var _datacenterId = long.Parse(InitConfiguration.GetSection("datacenterId").Value.ToString());
SeqIdGenInit(_machineId, _datacenterId);
}
/* public void SeqIdGenInitRedis()
{
var cpukey = getCpu();
var rdc = RedisManager.GetRedisClient2();
var mechineSeq = rdc.GetValueFromHash("Num:MechineSeq", cpukey);
if (string.IsNullOrEmpty(mechineSeq))
{
mechineSeq = rdc.IncrementValue("Num:MSeq").ToString();
rdc.SetEntryInHash("Num:MechineSeq", cpukey, mechineSeq);
}
SeqIdGenInit(long.Parse(mechineSeq), datacenterId);
}*/
public string GetDiskVolumeSerialNumber()
{
ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
ManagementObject disk = new ManagementObject("win32_LogHelpericaldisk.deviceid=\"c:\"");
disk.Get();
return disk.GetPropertyValue("VolumeSerialNumber").ToString();
}
//获得CPU的序列号
public string getCpu()
{
string strCpu = null;
ManagementClass myCpu = new ManagementClass("win32_Processor");
ManagementObjectCollection myCpuConnection = myCpu.GetInstances();
foreach (ManagementObject myObject in myCpuConnection)
{
strCpu = myObject.Properties["Processorid"].Value.ToString();
break;
}
return strCpu;
}
public void SeqIdGenInit(long cid, long mid)
{
if (cid > MAX_DATACENTER_NUM || cid < 0) throw new Exception($"中心Id应在(0,{MAX_DATACENTER_NUM})之间");
if (mid > MAX_MACHINE_NUM || mid < 0) throw new Exception($"机器Id应在(0,{MAX_MACHINE_NUM})之间");
datacenterId = cid;
machineId = mid;
}
/// <summary>
/// 产生下一个ID
/// </summary>
/// <returns></returns>
public long nextId()
{
lock (_Idlocker)
{
long currStmp = getNewstmp();
if (currStmp < lastStmp) throw new Exception("时钟倒退Id生成失败");
if (currStmp == lastStmp)
{
//相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
//同一毫秒的序列数已经达到最大
if (sequence == 0L) currStmp = getNextMill();
}
else
{
//不同毫秒内序列号置为0
sequence = 0L;
}
lastStmp = currStmp;
return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
| datacenterId << DATACENTER_LEFT //数据中心部分
| machineId << MACHINE_LEFT //机器标识部分
| sequence; //序列号部分
}
}
private long getNextMill()
{
long mill = getNewstmp();
while (mill <= lastStmp)
{
mill = getNewstmp();
}
return mill;
}
private long getNewstmp()
{
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
}
}
}

View File

@ -0,0 +1,79 @@
using ClickHouse.EntityFrameworkCore.Extensions;
using DG.EntityFramework;
using DG.Kafka;
using Exceptionless;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using ToDoWorker;
using Zxd.EntityFramework;
try
{
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
Console.WriteLine($"Env: {env}");
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env ?? "Production"}.json", true)
.AddJsonFile("Serilog.json")
.AddJsonFile($"Serilog.{env ?? "Production"}.json", true)
.Build();
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(config)
.WriteTo.Exceptionless(config.GetValue<string>("Exceptionless:ApiKey"), config.GetValue<string>("Exceptionless:ServerUrl"), new string[] { "WeworkUserWorker" })
.CreateLogger();
Log.Logger = logger;
Log.Information("Starting ToDoWorker");
IServiceCollection services = new ServiceCollection();
services.AddLogging(logging =>
{
logging.ClearProviders();
logging.AddSerilog();
});
services.AddSingleton<IConfiguration>(config);
services.AddOptions()
.Configure<SystemConfig>(e => config.GetSection("SystemConfig").Bind(e));
ExceptionlessClient.Default.Startup(config.GetValue<string>("Exceptionless:ApiKey"));
ExceptionlessClient.Default.Configuration.ServerUrl = config.GetValue<string>("Exceptionless:ServerUrl");
ExceptionlessClient.Default.Configuration.DefaultTags.Add("Zxd-ToDoWorker");
services.AddDGEntityFramework<ZxdDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("zxdcrm"), ServerVersion.AutoDetect(config.GetConnectionString("zxdcrm")));
});
services.AddDGEntityFramework<CrmCloudDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("crmcloud"), ServerVersion.AutoDetect(config.GetConnectionString("hgaction")));
});
/* services.AddDGEntityFramework<DimDbContext>(options =>
{
options.UseClickHouse(config.GetConnectionString("dim"));
});*/
services.AddAutoIoc(typeof(IScopedDependency), LifeCycle.Scoped)
.AddAutoIoc(typeof(ISingletonDependency), LifeCycle.Singleton)
.AddAutoIoc(typeof(ITransientDependency), LifeCycle.Transient)
.AddMapper();
services.AddKafkaWorker(config);
services.AddWorker(config);
services.AddRedis(config);
services.AddDGHttpClient();
services.AddRegisterWorker<ActionConsumeWorker>();
//services.AddRegisterWorker<RepairDataWorker>();
services.AddRegisterBatchWorker<ToDoWorker.Workers.ToDoWorker, KafkaEvent>();
//构建容器
IServiceProvider serviceProvider = services.BuildServiceProvider();
var workerManager = serviceProvider.GetRequiredService<IKafkaWorkerManager>();
var consumers = config.GetSection("Consumers").Get<List<Consumer>>(); ;
await workerManager.RegisterBatchWorker<ToDoWorker.Workers.ToDoWorker, KafkaEvent>(consumers?.FirstOrDefault()?.Topic, 1);
var builder = new HostBuilder();
await builder.RunConsoleAsync();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}

View File

@ -0,0 +1,16 @@
{
"profiles": {
"ToDoWorker": {
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker": {
"commandName": "Docker",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,33 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information",
"Microsoft.EntityFrameworkCore": "Warning",
"System.Net.Http.HttpClient": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"path": "logs/log.log",
"rollingInterval": "3",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}

View File

@ -0,0 +1,33 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information",
"Microsoft.EntityFrameworkCore": "Information",
"System.Net.Http.HttpClient": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"path": "logs/log.log",
"rollingInterval": "3",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}",
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,75 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Include="..\.dockerignore" Link=".dockerignore">
<DependentUpon>$(DockerDefaultDockerfile)</DependentUpon>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="DG.Core" Version="1.1.3" />
<PackageReference Include="DG.Redis" Version="1.0.17" />
<PackageReference Include="DG.Tool" Version="1.0.11" />
<PackageReference Include="DG.Kafka.Worker" Version="1.0.12" />
<PackageReference Include="DG.Worker" Version="1.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.2" />
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Sinks.Exceptionless" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Exceptionless" Version="6.0.1" />
<PackageReference Include="System.Management" Version="7.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Zxd.Core.Shared\Zxd.Core.Shared.csproj" />
<ProjectReference Include="..\Zxd.EntityFramework\Zxd.EntityFramework.csproj" />
<ProjectReference Include="..\Zxd.SqlSugar\Zxd.SqlSugar.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="System.Text.Json" />
<Using Include="System.Text.Json.Serialization" />
<Using Include="Microsoft.Extensions.Configuration" />
<Using Include="System.Text.Json" />
<Using Include="System.Text.Json.Serialization" />
<Using Include="DG.Tool" />
<Using Include="DG.Core" />
<Using Include="ToDoWorker.Workers" />
<Using Include="ToDoWorker.Dto" />
<Using Include="ToDoWorker.Config" />
<Using Include="DG.Kafka.Worker" />
<Using Include="Microsoft.Extensions.Configuration" />
<Using Include="Microsoft.Extensions.DependencyInjection" />
<Using Include="Microsoft.Extensions.Logging" />
<Using Include="Serilog" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.Production.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Serilog.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Serilog.Production.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="Event\" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,157 @@
using DG.EntityFramework;
using DG.Redis;
using DG.Worker;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ToDoWorker.Domain;
using ToDoWorker.Dto;
using ToDoWorker.Helper;
using Zxd.Core.Shared.Helpers;
using Zxd.Entity.Action;
using Zxd.Entity.dim;
using Zxd.Entity.Zxd;
using Zxd.EntityFramework;
using Zxd.SqlSugar;
namespace ToDoWorker.Workers
{
/// <summary>
/// 消费队列
/// </summary>
internal class ActionConsumeWorker : WorkerBase
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<ActionConsumeWorker> _logger;
private readonly IRedisManager _redisManager;
private static string _oldQueueskey = "queue:todoqueues";
private static string _queueskey = "queue:todoItemQueues";
private static string _cachekey = "queue:cacheItem";
public ActionConsumeWorker(ILogger<ActionConsumeWorker> logger,
IServiceProvider serviceProvider,
IConfiguration configuration,
IHttpClient httpClient,
IRedisManager redisManager
) : base(logger)
{
_logger = logger;
_serviceProvider = serviceProvider;
_redisManager = redisManager;
}
/// <summary>
///
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
protected override async Task DoWorkAsync()
{
//
Stopwatch stopwatch = Stopwatch.StartNew();
using var scope = _serviceProvider.CreateAsyncScope();
var setting = await scope.ServiceProvider.GetRequiredService<IEventDomain>().GetDelaySec();
var batchSize = setting == null ? 100 : setting.batchSize.Value;
for (var i = 0; i < batchSize; i++)
{
//ToDoEventSingleton.Instance.TryDequeue(delaySec, out EventDto? kafkaEvent);
var kafkaEvent = await Dequeue();
if (kafkaEvent != null)
{
//需要新注册实体
await scope.ServiceProvider.GetRequiredService<IEventDomain>().RunEvent(kafkaEvent);
// 任务处理完,移除缓存数据
await RemoveCache();
}
else
{
//如果拿出为null 则跳出
break;
}
}
stopwatch.Stop();
}
private async Task test()
{
var ss = "{\"act_date\":\"2023-08-18\",\"act_time\":1692351466000,\"appid\":\"wx1d0e531a680d947b\",\"appuserid\":\"olxOo6KlSg_QP5PL42skLpozzelI\",\"channel\":0,\"customerid\":7729244,\"deptid\":40,\"env\":\"prod\",\"eventid\":17,\"eventmemo\":\"六维共振\",\"eventname\":\"听课\",\"ip\":\"223.106.130.63\",\"others\":\"86\",\"productplat\":\"\",\"productplatname\":null,\"resid\":\"436799775507061617\",\"sceneext\":\"\",\"sceneid\":\"156\",\"sceneidname\":null,\"scenename\":null,\"scenetype\":6,\"scenetypename\":\"直播间\",\"sinkClickhouseTable\":\"act_action_event\",\"uid\":7750620,\"unionid\":\"o5bj2wWFzxBwWj4eIepDE8OISFs0\",\"user_type\":1,\"with_eid\":0,\"with_groupid\":0,\"with_orgid\":0,\"leavetime\":null,\"update_time\":null,\"TaskTime\":\"2023-08-18T17:39:50.3985639+08:00\",\"RetryCount\":0}";
var kafkaEvent = JsonHelper.FromJson<EventDto>(ss);
using var scope = _serviceProvider.CreateAsyncScope();
await scope.ServiceProvider.GetRequiredService<IEventDomain>().RunEvent(kafkaEvent);
//await _redisManager.EnqueueAsync(_queueskey, kafkaEvent);
}
/// <summary>
/// 任务出队
/// </summary>
/// <returns></returns>
private async Task<EventDto?> Dequeue()
{
var now = DateTime.Now;
var kafkaEvent = default(EventDto?);
// 是否存旧队列数据(兼容旧版)
if (await _redisManager.ExistsAsync(_oldQueueskey))
{
var data = await _redisManager.GetListAsync<EventDto>(_oldQueueskey);
data = data.OrderByDescending(x => x.TaskTime).ToList();
foreach (var item in data)
{
// 任务入队
item.TaskTime = DateTime.Now.AddSeconds(120);
item.RetryCount = 0;
await _redisManager.EnqueueAsync(_queueskey, item);
}
await _redisManager.RemoveAsync(_oldQueueskey);
}
// 判断是否存在缓存处理数据
if (await _redisManager.ExistsAsync(_cachekey))
{
kafkaEvent = await _redisManager.GetAsync<EventDto>(_cachekey);
if (kafkaEvent.TaskTime >= now)
{
return null;
}
//Log.Information($"任务出队处理, uid: [{kafkaEvent.uid}], resid: [{kafkaEvent.resid}]");
return kafkaEvent;
}
// 判断是否存在队列数据
if (await _redisManager.ExistsAsync(_queueskey))
{
kafkaEvent = await _redisManager.BlockingDequeueAsync<EventDto>(_queueskey);
if (kafkaEvent != null)
{
// 写入处理缓存
await _redisManager.SetAsync(_cachekey, kafkaEvent);
if (kafkaEvent.TaskTime >= now)
{
return null;
}
return kafkaEvent;
}
}
return kafkaEvent;
}
/// <summary>
/// 移除缓存处理数据
/// </summary>
/// <returns></returns>
private async Task RemoveCache()
{
if (await _redisManager.ExistsAsync(_cachekey))
{
var kafkaEvent = await _redisManager.GetAsync<EventDto>(_cachekey);
await _redisManager.RemoveAsync(_cachekey);
}
}
}
}

View File

@ -0,0 +1,76 @@
using DG.EntityFramework;
using DG.Redis;
using DG.Worker;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ToDoWorker.Domain;
using ToDoWorker.Dto;
using ToDoWorker.Helper;
using Zxd.Core.Shared.Helpers;
using Zxd.Entity.Action;
using Zxd.Entity.dim;
using Zxd.Entity.Zxd;
using Zxd.EntityFramework;
using Zxd.SqlSugar;
namespace ToDoWorker.Workers
{
/// <summary>
/// 消费队列
/// </summary>
internal class RepairDataWorker : WorkerBase
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<ActionConsumeWorker> _logger;
private readonly IRedisManager _redisManager;
private static string _oldQueueskey = "queue:todoqueues";
private static string _queueskey = "queue:todoItemQueues";
private static string _cachekey = "queue:cacheItem";
public RepairDataWorker(ILogger<ActionConsumeWorker> logger,
IServiceProvider serviceProvider,
IConfiguration configuration,
IHttpClient httpClient,
IRedisManager redisManager
) : base(logger)
{
_logger = logger;
_serviceProvider = serviceProvider;
_redisManager = redisManager;
}
/// <summary>
///
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
protected override async Task DoWorkAsync()
{
//
Stopwatch stopwatch = Stopwatch.StartNew();
using var scope = _serviceProvider.CreateAsyncScope();
var isRepairData = await _redisManager.GetAsync<int>("RepairData");
if (isRepairData != 1)
{
await _redisManager.SetAsync("RepairData", 1);
try
{
await scope.ServiceProvider.GetRequiredService<IEventDomain>().Repairdata();
stopwatch.Stop();
}
catch (Exception ex)
{
Log.Error($"初始化数据出错{ex.Message}");
}
}
await Task.Delay(100000);
}
}
}

View File

@ -0,0 +1,84 @@
using DG.Redis;
using DG.Worker;
using Exceptionless.Utility;
using Microsoft.Extensions.Configuration;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ToDoWorker.Domain;
using ToDoWorker.Helper;
using Zxd.Entity.Dncms;
namespace ToDoWorker.Workers
{
internal class ToDoWorker : BatchKafkaWorkerBase<KafkaEvent>
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<ToDoWorker> _logger;
private readonly IRedisManager _redisManager;
private static string _queueskey = "queue:todoItemQueues";
private static string _cachekey = "queue:cacheItem";
public ToDoWorker(ILogger<ToDoWorker> logger,
IServiceProvider serviceProvider,
IRedisManager redisManager
) : base(logger)
{
_logger = logger;
_serviceProvider = serviceProvider;
_redisManager = redisManager;
}
/// <summary>
///
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
protected override async Task DoWorkAsync(List<KafkaEvent> eventDtos)
{
foreach (var eventDto in eventDtos)
{
//Log.Information($"eventDtos:{eventDto.ToJson()}");
if (eventDto.tableName == "act_action_event" && !string.IsNullOrEmpty(eventDto.data))
{
try
{
EventDto model = JsonHelper.FromJson<EventDto>(eventDto.data);
//需要新注册实体
using var scope = _serviceProvider.CreateAsyncScope();
var eventService = scope.ServiceProvider.GetRequiredService<IEventDomain>();
var setting = await scope.ServiceProvider.GetRequiredService<IEventDomain>().GetDelaySec();
var delaySec = setting == null ? 120 : setting.delaytime.Value;
var deptSet = await eventService.GetSetting(model);
if (deptSet != null && model.uid > 0)
{
//登录特殊处理 一天同一个人只出现一次
if (!await eventService.FilterLogEvent(model, deptSet))
{
return;
}
if (model != null)
{
// 任务入队
model.TaskTime = DateTime.Now.AddSeconds(delaySec);
await _redisManager.EnqueueAsync(_queueskey, model);
}
}
/* else
{
Log.Information($"Event跳过{model.eventname}【{model.sceneid}】{model.uid}");
}*/
}
catch (Exception ex)
{
Log.Error($"DoWorkAsync处理失败{eventDto.ToJson()},{ex.Message}");
}
}
}
}
}
}

View File

@ -0,0 +1,43 @@
{
"ConnectionStrings": {
"zxdcrm": "Data Source=mysql98ff96c3dffa.rds.ivolces.com;Port=3306;Initial Catalog=zxdcrm;user id=qianbenjie;password=Hcqianbenjie@123;Old Guids=true;SslMode=None",
"dncmsbase": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=dncmsbase;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"usercenter": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=usercenter;user id=hguser;password=nH5L$&Hxxco;SslMode=None",
"dncms": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=dncms;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"companyBaseConf": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=db_company_base_conf;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"hgaction": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=hgaction;user id=hguser;password=nH5L$&Hxxco;SslMode=None",
"crmcloud": "Server=dgbigdata.mysql.rds.aliyuncs.com;Database=crm_cloud;UserId=crm_rd;Password=K#RQ1TYx9my;port=3306;"
},
"TaskConfig": {
"TaskName": "ToDoWorker",
"TaskRemarks": "ToDoWorker",
"Enable": true,
"MilliSecondsDelay": 200
},
"Exceptionless": {
"ServerUrl": "http://10.22.11.9:5000",
"ApiKey": "h6SHsDDLmWnC9E9WeEnRU7Sm3Em5RUuSKFVbSUWT"
},
"machineId": 1,
"datacenterId": 1,
"Consumers": [
{
"Host": "172.18.11.77:9092,172.18.11.76:9092",
"GroupId": "crm",
"Topic": "dwd_act_user_event_alert"
}
],
"SystemConfig": {
"zxdCoreApi": "http://120.77.165.155:8089",
"BdMarkting": "http://172.18.11.55:28065"
},
"Redis": [
{
"Name": "Hg",
"HostName": "172.18.11.63",
"Port": "6379",
"Password": "sa123456.",
"Defaultdatabase": "0"
}
]
}

View File

@ -0,0 +1,50 @@
{
"ConnectionStrings": {
"zxdcrm": "Server=192.168.11.141;Database=zxdcrm;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"dncmsbase": "Server=192.168.11.41;Database=dncmsbase;UserId=root;Password=sa123456.;port=3306;",
"usercenter": "Server=192.168.11.41;Database=usercenter;UserId=root;Password=sa123456.;port=3306;",
"dncms": "Server=192.168.11.41;Database=dncms;UserId=root;Password=sa123456.;port=3306;",
"companyBaseConf": "Server=192.168.11.141;Database=db_company_base_conf;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"hgaction": "Server=192.168.11.141;Database=hgaction;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"crmcloud": "Server=192.168.11.141;Database=crm_cloud;UserId=tafadmin;Password=tafadmin2017;port=3306;"
},
"TaskConfig": {
"TaskName": "ToDoWorker",
"TaskRemarks": "ToDoWorker",
"Enable": true,
"MilliSecondsDelay": 500
},
"machineId": 1,
"datacenterId": 1,
"Consumers": [
{
"Host": "192.168.11.104:9092,192.168.11.104:9092",
"GroupId": "crm",
"Topic": "dwd_act_user_alert_test1"
}
],
"Exceptionless": {
"ServerUrl": "http://10.22.12.9:5000",
"ApiKey": "AukFjLDBeABakHa1jS67DXoRFPh5J6TdVjDqFMwx"
},
"SystemConfig": {
"zxdCoreApi": "http://192.168.11.81:8089",
"BdMarkting": "http://bd-markting.soft.dn8188.com"
},
"Redis": [
{
"Name": "ZXD",
"HostName": "192.168.11.81",
"Port": "6379",
"Password": "Abc@123456",
"Defaultdatabase": "1"
},
{
"Name": "UserCenter",
"HostName": "192.168.11.103",
"Port": "6379",
"Password": "123",
"Defaultdatabase": "0"
}
]
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WeworkUserWorker.Config
{
public class SystemConfig
{
public string zxdCoreApi { get; set; }
public string nodeWebApi { get; set; }
}
}

View File

@ -0,0 +1,23 @@
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["WeworkUserWorker/WeworkUserWorker.csproj", "WeworkUserWorker/"]
COPY ["Zxd.Core.Shared/Zxd.Core.Shared.csproj", "Zxd.Core.Shared/"]
COPY ["Zxd.EntityFramework/Zxd.EntityFramework.csproj", "Zxd.EntityFramework/"]
COPY ["Zxd.Entity/Zxd.Entity.csproj", "Zxd.Entity/"]
RUN dotnet restore "WeworkUserWorker/WeworkUserWorker.csproj"
COPY . .
WORKDIR "/src/WeworkUserWorker"
RUN dotnet build "WeworkUserWorker.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "WeworkUserWorker.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WeworkUserWorker.dll"]

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WeworkUserWorker.Dto
{
public class ZXDApiResult<T> where T : class
{
public int code { get; set; }
public string? message { get; set; }
public T data { get; set; }
}
}

View File

@ -0,0 +1,89 @@
using DG.EntityFramework;
using DG.Kafka.Worker;
using Exceptionless;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using WeworkUserWorker.Config;
using Zxd.Core.Shared.Dto;
using Zxd.EntityFramework;
try
{
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
Console.WriteLine($"Env: {env}");
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env ?? "Production"}.json", true)
.AddJsonFile("Serilog.json")
.AddJsonFile($"Serilog.{env ?? "Production"}.json", true)
.Build();
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(config)
.WriteTo.Exceptionless(config.GetValue<string>("Exceptionless:ApiKey"), config.GetValue<string>("Exceptionless:ServerUrl"), new string[] { "WeworkUserWorker" })
.CreateLogger();
Log.Logger = logger;
Log.Information("Starting WeworkUserWorker");
IServiceCollection services = new ServiceCollection();
services.AddLogging(logging =>
{
logging.ClearProviders();
logging.AddSerilog();
});
services.AddSingleton(config);
services.AddOptions()
.Configure<SystemConfig>(e => config.GetSection("SystemConfig").Bind(e));
ExceptionlessClient.Default.Startup(config.GetValue<string>("Exceptionless:ApiKey"));
ExceptionlessClient.Default.Configuration.ServerUrl = config.GetValue<string>("Exceptionless:ServerUrl");
//services.AddRedis(config);
services.AddDGEntityFramework<ZxdDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("zxdcrm"), ServerVersion.AutoDetect(config.GetConnectionString("zxdcrm")));
});
services.AddDGEntityFramework<DncmsbaseDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("dncmsbase"), ServerVersion.AutoDetect(config.GetConnectionString("dncmsbase")));
});
services.AddDGEntityFramework<UserCenterDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("usercenter"), ServerVersion.AutoDetect(config.GetConnectionString("usercenter")));
});
services.AddDGEntityFramework<DncmsDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("dncms"), ServerVersion.AutoDetect(config.GetConnectionString("dncms")));
});
services.AddDGEntityFramework<CrmDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("crm"), ServerVersion.AutoDetect(config.GetConnectionString("crm")));
});
services.AddDGEntityFramework<CompanyBaseConfDbContext>(options =>
{
options.UseMySql(config.GetConnectionString("companyBaseConf"), ServerVersion.AutoDetect(config.GetConnectionString("companyBaseConf")));
});
services.AddKafkaWorker(config);
services.AddRedis(config);
services.AddDGHttpClient();
services.AddRegisterWorker<WeworkWorker, WeworkWorkerDto>();
//构建容器
IServiceProvider serviceProvider = services.BuildServiceProvider();
var workerManager = serviceProvider.GetRequiredService<IKafkaWorkerManager>();
await workerManager.RegisterWorker<WeworkWorker, WeworkWorkerDto>("crm-topic");
await workerManager.RegisterWorker<WeworkWorker, WeworkWorkerDto>("crm-topic");
await workerManager.RegisterWorker<WeworkWorker, WeworkWorkerDto>("crm-topic");
await workerManager.RegisterWorker<WeworkWorker, WeworkWorkerDto>("crm-topic");
await workerManager.RegisterWorker<WeworkWorker, WeworkWorkerDto>("crm-topic");
var builder = new HostBuilder();
await builder.RunConsoleAsync();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}

View File

@ -0,0 +1,16 @@
{
"profiles": {
"WeworkUserWorker": {
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker": {
"commandName": "Docker",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,33 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information",
"Microsoft.EntityFrameworkCore": "Warning",
"System.Net.Http.HttpClient": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"path": "logs/log.log",
"rollingInterval": "3",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}

View File

@ -0,0 +1,33 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information",
"Microsoft.EntityFrameworkCore": "Information",
"System.Net.Http.HttpClient": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"encoding": "System.Text.Encoding::UTF8",
"path": "logs/log.log",
"rollingInterval": "3",
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] <{ThreadId}> [{Level:u3}] {Message:lj}{NewLine}{Exception}",
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}

View File

@ -0,0 +1,71 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DG.Core" Version="1.0.25" />
<PackageReference Include="DG.Redis" Version="1.0.17" />
<PackageReference Include="DG.Tool" Version="1.0.9" />
<PackageReference Include="DG.Kafka" Version="1.0.3" />
<PackageReference Include="DG.Kafka.Worker" Version="1.0.3" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.2" />
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Sinks.Exceptionless" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Exceptionless" Version="6.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Zxd.Core.Shared\Zxd.Core.Shared.csproj" />
<ProjectReference Include="..\Zxd.EntityFramework\Zxd.EntityFramework.csproj" />
<ProjectReference Include="..\Zxd.Entity\Zxd.Entity.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="System.Text.Json" />
<Using Include="System.Text.Json.Serialization" />
<Using Include="Microsoft.EntityFrameworkCore" />
<Using Include="Microsoft.Extensions.Configuration" />
<Using Include="System.Text.Json" />
<Using Include="System.Text.Json.Serialization" />
<Using Include="DG.Tool" />
<Using Include="DG.Core" />
<Using Include="WeworkUserWorker.Workers" />
<Using Include="WeworkUserWorker.Dto" />
<Using Include="DG.Kafka.Worker" />
<Using Include="Microsoft.Extensions.Configuration" />
<Using Include="Microsoft.Extensions.DependencyInjection" />
<Using Include="Microsoft.Extensions.Logging" />
<Using Include="Serilog" />
<Using Include="DG.EntityFramework" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.Disaster.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.Production.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Serilog.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Serilog.Production.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,723 @@
using DG.EntityFramework;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WeworkUserWorker.Config;
using Zxd.Entity.Dncms;
using Zxd.Entity.UserCenter;
using Zxd.EntityFramework;
using Microsoft.Extensions.Options;
using DG.Kafka;
using MySqlConnector;
using System.Diagnostics;
using Zxd.Core.Shared.Dto;
using DG.Redis;
using Zxd.Entity.Zxd;
namespace WeworkUserWorker.Workers
{
internal class WeworkTagWorker : KafkaWorkerBase<WeWorkTagDto>
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<WeworkTagWorker> _logger;
private readonly IOptionsSnapshot<SystemConfig> _systemConfig;
private readonly IHttpClient _httpClient;
private readonly IRedisManager _redisManager;
public WeworkTagWorker(
IServiceProvider serviceProvider,
IOptionsSnapshot<SystemConfig> systemConfig,
ILogger<WeworkTagWorker> logger,
IHttpClient httpClient, IRedisManager redisManager
) : base(logger)
{
_serviceProvider = serviceProvider;
_httpClient = httpClient;
_systemConfig = systemConfig;
_logger = logger;
_redisManager = redisManager;
}
/// <summary>
/// 处理好友最终关系
/// 数据逻辑
/// 1、通过appid和appuserid去usercenter.userinfo中找出customerid并通过customerid查出userinfo中所有用户数据
/// 2、假如数据中没有resid、忽略不处理。
/// 3、假如有resid去dncms.weworkexternaluser中查找数据subscribe = 1筛选数据用subscribetime去 res_resid_weworkuser
/// 表中appid、appuserid数据取firsttime和最早的subscribetime 对比有变动就修正假如res_resid_weworkuser 中的在刚刚查找出来没有关注数据了,
/// 将status改成0 。假如res_resid_weworkuser 没有数据,插入一条数据。
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
protected override async Task DoWorkAsync(WeWorkTagDto t)
{
IList<ResResidWeworkUser> resResids = new List<ResResidWeworkUser>();
IList<UserInfoReq> users = new List<UserInfoReq>();
List<ResResidWeworkUser> rrWeworkUser = new List<ResResidWeworkUser>();
List<WeworkExternalUser> mainExternalUsers = new List<WeworkExternalUser>();
List<WeworkExternalUser> secExternalUsers = new List<WeworkExternalUser>();
try
{
await EventLog(t);
switch (t.type?.ToLower())
{
case "subscribe"://关注
case "unsubscribe"://取关
_logger.LogWarning("开始处理取关!");
users = await GetUser(t.appid, t.appuserid); //请求此用户的所有用户信息
rrWeworkUser = await BindWeworkUser(users, mainExternalUsers);//查询当前客户好友关系
foreach (var item in rrWeworkUser)
{
await ChangeWeworkUser(item);//入库
}
//await SetSourcePassInfo(mainExternalUsers, users, t);
_logger.LogWarning("结束处理取关!");
break;
case "merge"://合并
_logger.LogWarning("开始处理合并!");
if (int.TryParse(t.cid, out int incid))
{
users = await GetUser(incid);//请求此用户的所有用户信息
rrWeworkUser = await BindWeworkUser(users, mainExternalUsers);//查询当前客户好友关系
foreach (var item in rrWeworkUser)
{
await ChangeWeworkUser(item);//入库
}
}
//await SetSourcePassInfo(mainExternalUsers, users, t);
_logger.LogWarning("结束处理合并!");
break;
case "unmerge"://解绑
_logger.LogWarning("开始处理解绑!");
var cidarry = t.cid?.Trim().Split(',');
if (cidarry != null && cidarry.Count() == 2)
{
var scid = cidarry[0];
var ecid = cidarry[1];
var susers = await GetUser(int.Parse(scid));//分别查询出两个cid对应的客户信息
var eusers = await GetUser(int.Parse(ecid));//
var swu = await BindWeworkUser(susers, mainExternalUsers);//查询当前客户好友关系
//await SetSourcePassInfo(mainExternalUsers, susers, t);
var ewu = await BindWeworkUser(eusers, secExternalUsers);//查询当前客户好友关系
//await SetSourcePassInfo(secExternalUsers, eusers, t);
foreach (var item in swu)
{
await ChangeWeworkUser(item);//入库
}
foreach (var item in ewu)
{
await ChangeWeworkUser(item);//入库
}
var unlist = await UnBindWeworkUser(susers, eusers);//拼接两个cid的 重叠部分
foreach (var item in unlist)
{
//事业部不发生变化
await ChangeWeworkUser(item, false);//解绑匹配的新数据不入库,旧数据标记删除
}
}
_logger.LogWarning("结束处理解绑!");
break;
case "eidbind"://跨部门换绑工号 300秒超时
if (t.olddeptid.HasValue && t.deptid.HasValue && t.olddeptid != t.deptid)
{
var kfuser = await GetKFUser(t.appid, t.userid); //请求此用户的所有用户信息
if (kfuser.Count() > 300)
{//给队列逐条处理
IList<string> appidStr = new List<string>();
IList<string> euseridStr = new List<string>();
foreach (var item in kfuser)
{
appidStr.Add(item.Appid_ext);
euseridStr.Add(item.Externaluserid);
if (appidStr.Count() > 9)
{
WeworkWorkerDto dto = new WeworkWorkerDto();
dto.type = "inner_eidbind_item";
dto.appuserid = String.Join(",", euseridStr);
dto.appid = String.Join(",", appidStr);
dto.deptid = t.deptid;
dto.olddeptid = t.olddeptid;
PushKafKa(dto);
appidStr = new List<string>();
euseridStr = new List<string>();
}
}
//循环结束验证是否有未完成
if (euseridStr.Any())
{
WeworkWorkerDto lastdto = new WeworkWorkerDto();
lastdto.type = "inner_eidbind_item";
lastdto.appuserid = String.Join(",", euseridStr);
lastdto.appid = String.Join(",", appidStr);
lastdto.deptid = t.deptid;
lastdto.olddeptid = t.olddeptid;
PushKafKa(lastdto);
}
}
else
{//直接批量处理
foreach (var item in kfuser)//逐个好友处理数据
{
var iusers = await GetUser(item.Appid, item.Externaluserid);
List<WeworkExternalUser> kfExternal = new List<WeworkExternalUser>();
rrWeworkUser = await BindWeworkUser(iusers, kfExternal);//重新计算当前客户好友关系
//await SetSourcePassInfo(kfExternal, iusers, t);
foreach (var rritem in rrWeworkUser)
{
await ChangeWeworkUser(rritem);//更新和插入 事业部变更后的好友关系
if (rritem.deptid == t.deptid)
{
rritem.deptid = t.olddeptid.Value;
await ChangeWheorkUserDeptid(rritem);//查找 事业部变更前的好友关系,有则标记删除
}
}
}
}
}
break;
case "inner_eidbind_item"://逐个处理换绑资源
if (!string.IsNullOrEmpty(t.appid) && !string.IsNullOrEmpty(t.appuserid) && t.olddeptid.HasValue && t.deptid.HasValue)
{
var appidList = t.appid.Split(',').ToList();
var appuseridList = t.appuserid.Split(',').ToList();
if (appidList.Count() == appuseridList.Count())
{//一次性处理9个
int index = 0;
foreach (var appid in appidList)
{
var appuserid = appuseridList[index];
users = await GetUser(appid, appuserid);
List<WeworkExternalUser> kfExternal = new List<WeworkExternalUser>();
rrWeworkUser = await BindWeworkUser(users, kfExternal);//重新计算当前客户好友关系
//await SetSourcePassInfo(kfExternal, users, t);
foreach (var rritem in rrWeworkUser)
{
await ChangeDeptWeworkUser(rritem, t.olddeptid.Value);//更新和插入 事业部变更后的好友关系
}
index++;
}
}
}
break;
case "register":
users = await GetUser(t.appid, t.appuserid);
rrWeworkUser = await BindWeworkUser(users, mainExternalUsers);//重新计算当前客户好友关系
//await SetSourcePassInfo(mainExternalUsers, users, t);
break;
}
}
catch (Exception ex)
{
_logger.LogWarning(ex.Message, ex);
}
}
/// <summary>
/// 查询客服名下所有客户
/// </summary>
/// <param name="appid"></param>
/// <param name="userid"></param>
/// <returns></returns>
private async Task<IList<WeworkExternalUser>> GetKFUser(string appid, string userid)
{
MySqlParameter[] param = new MySqlParameter[] {
new MySqlParameter(){ DbType=System.Data.DbType.String,Value=appid,ParameterName="appid"},
new MySqlParameter(){ DbType=System.Data.DbType.String,Value=userid,ParameterName="userid"}
};
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsDbContext>>();
var extUser = await dncmsRepository.ExecuteSqlToListAsync<WeworkExternalUser>($"select appid,userid,appid_ext,externaluserid,deptid,unionid,subscribe,subscribetime from WeworkExternalUser where appid=@appid and userid=@userid", param);
return extUser;
}
/// <summary>
/// 获取客户当前 resid数据
/// </summary>
/// <param name="appid"></param>
/// <param name="userid"></param>
/// <returns></returns>
private async Task<IList<UserInfoReq>> GetUser(string appid, string appuserid)
{
IList<UserInfoReq> res = new List<UserInfoReq>();
var url = $"{_systemConfig.Value.zxdCoreApi}/Api/UserInfo/list";
var resModel = await _httpClient.GetAsync<ZXDApiResult<IList<UserInfoReq>>>($"{url}?appid={appid}&appuserid={appuserid}");
if (resModel.code == 0)
{
res = resModel.data;
}
return res;
}
/// <summary>
/// 获取客户当前 resid数据
/// </summary>
/// <param name="appid"></param>
/// <param name="userid"></param>
/// <returns></returns>
private async Task<IList<UserInfoReq>> GetUser(int? customerid)
{
IList<UserInfoReq> res = new List<UserInfoReq>();
if (customerid.HasValue)
{
var url = $"{_systemConfig.Value.zxdCoreApi}/Api/UserInfo/list";
//var req = HttpHelper.GetData(url, $"?cid={customerid}", Encoding.UTF8);
//var resModel = JsonHelper.FromJson<ZXDApiResult<IList<UserInfoReq>>>(req);
var resModel = await _httpClient.GetAsync<ZXDApiResult<IList<UserInfoReq>>>($"{url}?cid={customerid}");
if (resModel.code == 0)
{
res = resModel.data;
}
}
return res;
}
/// <summary>
/// 匹配两个好友解绑时的所有情况
/// </summary>
/// <param name="userInfo1"></param>
/// <param name="userInfo2"></param>
/// <returns></returns>
private async Task<IList<ResResidWeworkUser>> UnBindWeworkUser(IList<UserInfoReq> userInfo1, IList<UserInfoReq> userInfo2)
{
IList<ResResidWeworkUser> res = new List<ResResidWeworkUser>();
//var userGup1 = userInfo1.Where(m => !string.IsNullOrEmpty(m.resid)).GroupBy(m => m.resid).ToDictionary(m => m.Key, n => n.ToList());
//var userGup2 = userInfo2.Where(m => !string.IsNullOrEmpty(m.resid)).GroupBy(m => m.resid).ToDictionary(m => m.Key, n => n.ToList());
List<WeworkExternalUser> mainExternalUsers = new List<WeworkExternalUser>();
List<WeworkExternalUser> secExternalUsers = new List<WeworkExternalUser>();
//获取当前解绑后的 rrwu数据
var wwuser1 = await BindWeworkUser(userInfo1, mainExternalUsers);
var wwuser2 = await BindWeworkUser(userInfo2, secExternalUsers);
//拼接两个数据的交集
foreach (var u1 in wwuser1)
{
foreach (var u2 in wwuser2)
{
ResResidWeworkUser r = new ResResidWeworkUser();
r.resid = u1.resid;
r.unionid = u1.unionid;
r.deptid = u1.deptid;
r.first_subscribetime = u1.first_subscribetime;
r.last_subscribetime = u1.last_subscribetime;
r.status = u1.status;
//删除集合1与集合2的 交集
r.appid = u2.appid;
r.appuserid = u2.appuserid;
r.isdelete = 1;
r.utime = DateTime.Now;
res.Add(r);
}
}
//cid2 与1的交集数据
foreach (var u1 in wwuser2)
{
foreach (var u2 in wwuser1)
{
ResResidWeworkUser r = new ResResidWeworkUser();
r.resid = u1.resid;
r.unionid = u1.unionid;
r.deptid = u1.deptid;
r.first_subscribetime = u1.first_subscribetime;
r.last_subscribetime = u1.last_subscribetime;
r.status = u1.status;
//删除集合1与集合2的 交集
r.appid = u2.appid;
r.appuserid = u2.appuserid;
r.isdelete = 1;
r.utime = DateTime.Now;
res.Add(r);
}
}
return res;
}
/// <summary>
/// 匹配当前用户最终好友状态
/// </summary>
/// <param name="userInfo"></param>
/// <returns></returns>
private async Task<List<ResResidWeworkUser>> BindWeworkUser(IList<UserInfoReq> userInfo, List<WeworkExternalUser> weworkExternalUsers)
{
List<ResResidWeworkUser> resResidWeworkUsers = new List<ResResidWeworkUser>();
if (userInfo.Count == 0)
{
return resResidWeworkUsers;
}
var extuserfilter = userInfo.Select(n => $"'{n.appuserid}'").Distinct().ToList();
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsDbContext>>();
MySqlParameter[] param = new MySqlParameter[] { };
var extUserList = await dncmsRepository.ExecuteSqlToListAsync<WeworkExternalUser>($"select appid,userid,appid_ext,externaluserid,deptid,unionid,subscribe,subscribetime,regdate from WeworkExternalUser where externaluserid in ({string.Join(",", extuserfilter)})", param);
foreach (var item in extUserList)
{
var userinfoItem = userInfo.FirstOrDefault(n => n.appid == item.Appid_ext && n.appuserid == item.Externaluserid && n.appid.EndsWith("_1"));
if (userinfoItem != null)
{
weworkExternalUsers.Add(item);
}
}
if (weworkExternalUsers.Any())
{
//好友关系分组
var weworkExternalUsersGup = weworkExternalUsers.GroupBy(m => m.deptid + ";" + m.Appid + ";" + m.Externaluserid)
.ToDictionary(m => m.Key, n => n.ToList());
//resid分组
var residGup = userInfo.Where(m => !string.IsNullOrEmpty(m.resid))
.GroupBy(m => m.resid).ToDictionary(m => m.Key, n => n.Min(i => i.uid));
//组合关系数据
foreach (var resid in residGup)
{
foreach (var wwe in weworkExternalUsersGup)
{
var deptid = wwe.Key.Split(';')[0];
var appid = wwe.Key.Split(';')[1];
var extuserid = wwe.Key.Split(';')[2];
var extList = wwe.Value;
var extSubList = extList.Where(m => m.subscribe == 1);
ResResidWeworkUser res = new ResResidWeworkUser();
res.uid = int.Parse(resid.Value);
res.deptid = int.Parse(deptid);
res.resid = resid.Key;
res.appid = appid;
res.appuserid = extuserid;
res.unionid = extList.Where(m => !string.IsNullOrEmpty(m.unionid)).FirstOrDefault()?.unionid;//unionid有则取
if (extSubList.Any())
{
res.first_subscribetime = extSubList.Min(m => m.subscribetime ?? m.regdate);
res.last_subscribetime = extSubList.Max(m => m.subscribetime ?? m.regdate);
res.status = 1;//最终关注状态
}
else
{
res.first_subscribetime = null;
res.last_subscribetime = null;
res.status = 0;//最终关注状态
}
res.ctime = DateTime.Now;
res.isdelete = 0;
res.utime = DateTime.Now;
resResidWeworkUsers.Add(res);
}
}
}
return resResidWeworkUsers;
}
/// <summary>
/// 入库ResResidWeworkUser
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
private async Task ChangeWeworkUser(ResResidWeworkUser model, bool add = true)
{
if (model == null) return;
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var tag = dncmsbaseRepository.GetRepository<ResResidWeworkUser>().Query()
.FirstOrDefault(m => m.resid == model.resid && m.appuserid == model.appuserid && m.appid == model.appid && m.deptid == model.deptid);
using var transaction = await dncmsbaseRepository.BeginTransactionAsync();
try
{
if (tag != null)
{
tag.status = model.status;
tag.first_subscribetime = model.first_subscribetime;
tag.last_subscribetime = model.last_subscribetime;
tag.isdelete = model.isdelete;
//更新已有好友关系
await dncmsbaseRepository.GetRepository<ResResidWeworkUser>().UpdateAsync(tag);
//PushKafKa(MappingTolog(tag));
await dncmsbaseRepository.GetRepository<ResResidWeworkUserLog>().InsertAsync(MappingTolog(tag));
}
else if (add)
{
//插入新的好友关系
var outModel = await dncmsbaseRepository.GetRepository<ResResidWeworkUser>().InsertAsync(model);
//PushKafKa(MappingTolog(outModel));
await dncmsbaseRepository.GetRepository<ResResidWeworkUserLog>().InsertAsync(MappingTolog(outModel));
}
await transaction.CommitAsync();
}
catch (Exception ex)
{
await transaction.RollbackAsync();
await transaction.DisposeAsync();
_logger.LogError($"入库ResResidWeworkUser报错{ex.Message}", ex);
}
}
/// <summary>
/// 换事业部
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
private async Task ChangeDeptWeworkUser(ResResidWeworkUser model, int olddeptid)
{
if (model == null) return;
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var tag = dncmsbaseRepository.GetRepository<ResResidWeworkUser>().Query()
.FirstOrDefault(m => m.resid == model.resid && m.appuserid == model.appuserid && m.appid == model.appid && m.deptid == olddeptid);
using var transaction = await dncmsbaseRepository.BeginTransactionAsync();
try
{
if (tag != null)
{
tag.status = model.status;
tag.first_subscribetime = model.first_subscribetime;
tag.last_subscribetime = model.last_subscribetime;
tag.isdelete = model.isdelete;
tag.deptid = model.deptid;//更新事业部
//更新已有好友关系
await dncmsbaseRepository.GetRepository<ResResidWeworkUser>().UpdateAsync(tag);
//插入新数据
await dncmsbaseRepository.GetRepository<ResResidWeworkUserLog>().InsertAsync(MappingTolog(tag));
//删除旧数据
var delTag = MappingTolog(tag);
delTag.isdelete = 1;
delTag.deptid = olddeptid;
await dncmsbaseRepository.GetRepository<ResResidWeworkUserLog>().InsertAsync(delTag);
}
else
{
//插入新的好友关系
var outModel = await dncmsbaseRepository.GetRepository<ResResidWeworkUser>().InsertAsync(model);
//PushKafKa(MappingTolog(outModel));
await dncmsbaseRepository.GetRepository<ResResidWeworkUserLog>().InsertAsync(MappingTolog(outModel));
}
await transaction.CommitAsync();
}
catch (Exception ex)
{
await transaction.RollbackAsync();
await transaction.DisposeAsync();
_logger.LogError($"换事业部报错:{ex.Message}", ex);
}
}
/// <summary>
/// 事业部替换
/// </summary>
/// <returns></returns>
private async Task ChangeWheorkUserDeptid(ResResidWeworkUser oldrrw)
{
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var tag = dncmsbaseRepository.GetRepository<ResResidWeworkUser>().Query().FirstOrDefault(m => m.appuserid == oldrrw.appuserid && m.appid == oldrrw.appid && m.deptid == oldrrw.deptid);
if (tag != null && tag.isdelete == 0)
{
//await dncmsbaseRepository.GetRepository<ResResidWeworkUser>().UpdateAsync(tag);
await dncmsbaseRepository.GetRepository<ResResidWeworkUserLog>().InsertAsync(MappingTolog(tag));//旧事业部数据删除
}
}
/// <summary>
/// 隐射对象
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
private ResResidWeworkUserLog MappingTolog(ResResidWeworkUser model)
{
if (model == null) return null;
ResResidWeworkUserLog res = new ResResidWeworkUserLog();
res.mainid = model.id;
res.uid = model.uid;
res.appid = model.appid;
res.appuserid = model.appuserid;
res.resid = model.resid;
res.deptid = model.deptid;
res.unionid = model.unionid;
res.status = model.status;
res.isdelete = model.isdelete;
res.first_subscribetime = model.first_subscribetime;
res.last_subscribetime = model.last_subscribetime;
res.ctime = model.ctime;
res.utime = model.utime;
return res;
}
/// <summary>
/// 推送队列
/// </summary>
/// <param name="log"></param>
/// <returns></returns>
private async Task PushKafKa(WeworkWorkerDto log)
{
//IList<UserInfoReq> res = new List<UserInfoReq>();
//var url = $"{_systemConfig.Value.zxdCoreApi}/Api/WeWork/SyncBinding";
//var req = HttpHelper.PostAjaxData(url, JsonHelper.ToJson(log), Encoding.UTF8);
//var resModel = JsonHelper.FromJson<ZXDApiResult<string>>(req);
//if (resModel.code != 0)
//{
// Log.Write(Serilog.Events.LogEventLevel.Error, req);
//}
var consumers = KafkaClient.GetConsumers();
await KafkaClient.SendMessage(consumers.FirstOrDefault(n => n.Topic == "crm-topic"), log);
}
/// <summary>
/// 记录日志
/// </summary>
/// <returns></returns>
private async Task EventLog(WeworkWorkerDto t)
{
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
ResResidWeworkEventLog log = new ResResidWeworkEventLog();
log.appid = t.appid;
log.userid = t.userid;
log.appuserid = t.appuserid;
log.type = t.type;
log.deptid = t.deptid;
log.olddeptid = t.olddeptid;
log.eventJson = JsonHelper.ToJson(t);
log.cid = t.cid;
log.ctime = DateTime.Now;
await dncmsbaseRepository.GetRepository<ResResidWeworkEventLog>().InsertAsync(log);
}
/// <summary>
/// 外部联系人 赋值资源进线时间
/// </summary>
/// <param name="extUser"></param>
/// <param name="userInfoList"></param>
/// <returns></returns>
private async Task SetSourcePassInfo(List<WeworkExternalUser> extUser, IList<UserInfoReq> userInfoList, WeworkWorkerDto t)
{
try
{
if (extUser.Count == 0 || userInfoList.Count == 0)
{
return;
}
var uidFilter = userInfoList.Select(n => Convert.ToInt32(n.uid)).Distinct().ToList();
var regdate = await GetRegDate(userInfoList);
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var dbList = await dncmsbaseRepository.GetRepository<ResourceProtectInfo>().Query().Where(n => uidFilter.Contains(n.uid)).ToListAsync();
var groupdeptid = extUser.GroupBy(n => n.deptid).ToList();
List<ResourceProtectInfo> insertList = new List<ResourceProtectInfo>();
List<ResourceProtectInfo> updateList = new List<ResourceProtectInfo>();
List<ResourceProtectInfo> delList = new List<ResourceProtectInfo>();
var customerid = 0;
Int32.TryParse(userInfoList.FirstOrDefault().customerid, out customerid);
using var transaction = await dncmsbaseRepository.BeginTransactionAsync();
foreach (var deptid in groupdeptid)
{
var deptUser = extUser.Where(n => n.deptid == deptid.Key).ToList();
var subItem = deptUser.OrderBy(n => n.subscribetime).OrderBy(n => n.regdate).FirstOrDefault();
var dbItem = dbList.FirstOrDefault(n => n.deptid == deptid.Key && n.customerid == customerid);
var useritem = userInfoList.FirstOrDefault(n => n.appid == subItem.Appid_ext && n.appuserid == subItem.Externaluserid);
if (useritem == null)
{
continue;
}
var uid = 0;
Int32.TryParse(useritem.uid, out uid);
if (dbItem == null)
{
ResourceProtectInfo model = new ResourceProtectInfo
{
uid = uid,
appid = useritem.appid,
appuserid = useritem.appuserid,
resid = useritem.resid,
unionid = useritem.unionid,
customerid = customerid,
deptid = subItem.deptid,
firstfollowtime = deptUser.Min(m => m.subscribetime ?? m.regdate),
followtime = deptUser.Where(n => n.subscribe == 1).Min(m => m.subscribetime ?? m.regdate),
ctime = DateTime.Now,
regtime = regdate,
eventtype = t.type,
};
if (!model.followtime.HasValue)
{
model.isdelete = 1;
}
insertList.Add(model);
}
else
{
dbItem.deptid = subItem.deptid;
dbItem.uid = uid;
dbItem.resid = useritem.resid;
dbItem.unionid = useritem.unionid;
dbItem.customerid = customerid;
dbItem.deptid = subItem.deptid;
dbItem.firstfollowtime = deptUser.Min(m => m.subscribetime ?? m.regdate);
dbItem.followtime = deptUser.Where(n => n.subscribe == 1).Min(m => m.subscribetime ?? m.regdate);
dbItem.utime = DateTime.Now;
dbItem.regtime = regdate;
dbItem.eventtype = t.type;
if (!dbItem.followtime.HasValue)
{
dbItem.isdelete = 1;
}
updateList.Add(dbItem);
}
}
bool update = false;
if (updateList.Count > 0)
{
update = true;
await SetDeptInfo(updateList);
await dncmsbaseRepository.GetRepository<ResourceProtectInfo>().BatchUpdateAsync(updateList);
}
if (insertList.Count > 0)
{
update = true;
await SetDeptInfo(insertList);
await dncmsbaseRepository.GetRepository<ResourceProtectInfo>().BatchInsertAsync(insertList);
}
if (update)
{
transaction.Commit();
}
}
catch (Exception ex)
{
Log.Error($"计算资源进线时间错误{ex.Message}");
}
}
private async Task<DateTime?> GetRegDate(IList<UserInfoReq> userInfoList)
{
using var scope = _serviceProvider.CreateAsyncScope();
var zxdRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<ZxdDbContext>>();
var userList = userInfoList.Select(n => n.appuserid).Distinct().ToList();
var softuser = await zxdRepository.GetRepository<SOFT_USER>().Query().Where(n => userList.Contains(n.USERNAME)).ToListAsync();
return softuser.Min(n => n.REGDATE);
}
private async Task SetDeptInfo(List<ResourceProtectInfo> model)
{
var key = "deptment_list_new";
var deptInfo = new List<DeptmentDto>();
if (await _redisManager.ExistsAsync(key))
{
deptInfo = await _redisManager.GetListAsync<DeptmentDto>(key);
}
if (deptInfo != null && deptInfo.Count > 0)
{
foreach (var item in model)
{
var dept = deptInfo.FirstOrDefault(n => n.Id == item.deptid);
item.deptname = dept?.Title;
item.groupid = dept?.GroupId;
}
}
}
}
}

View File

@ -0,0 +1,723 @@
using DG.EntityFramework;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WeworkUserWorker.Config;
using Zxd.Entity.Dncms;
using Zxd.Entity.UserCenter;
using Zxd.EntityFramework;
using Microsoft.Extensions.Options;
using DG.Kafka;
using MySqlConnector;
using System.Diagnostics;
using Zxd.Core.Shared.Dto;
using DG.Redis;
using Zxd.Entity.Zxd;
namespace WeworkUserWorker.Workers
{
internal class WeworkWorker : KafkaWorkerBase<WeworkWorkerDto>
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<WeworkWorker> _logger;
private readonly IOptionsSnapshot<SystemConfig> _systemConfig;
private readonly IHttpClient _httpClient;
private readonly IRedisManager _redisManager;
public WeworkWorker(
IServiceProvider serviceProvider,
IOptionsSnapshot<SystemConfig> systemConfig,
ILogger<WeworkWorker> logger,
IHttpClient httpClient, IRedisManager redisManager
) : base(logger)
{
_serviceProvider = serviceProvider;
_httpClient = httpClient;
_systemConfig = systemConfig;
_logger = logger;
_redisManager = redisManager;
}
/// <summary>
/// 处理好友最终关系
/// 数据逻辑
/// 1、通过appid和appuserid去usercenter.userinfo中找出customerid并通过customerid查出userinfo中所有用户数据
/// 2、假如数据中没有resid、忽略不处理。
/// 3、假如有resid去dncms.weworkexternaluser中查找数据subscribe = 1筛选数据用subscribetime去 res_resid_weworkuser
/// 表中appid、appuserid数据取firsttime和最早的subscribetime 对比有变动就修正假如res_resid_weworkuser 中的在刚刚查找出来没有关注数据了,
/// 将status改成0 。假如res_resid_weworkuser 没有数据,插入一条数据。
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
protected override async Task DoWorkAsync(WeworkWorkerDto t)
{
IList<ResResidWeworkUser> resResids = new List<ResResidWeworkUser>();
IList<UserInfoReq> users = new List<UserInfoReq>();
List<ResResidWeworkUser> rrWeworkUser = new List<ResResidWeworkUser>();
List<WeworkExternalUser> mainExternalUsers = new List<WeworkExternalUser>();
List<WeworkExternalUser> secExternalUsers = new List<WeworkExternalUser>();
try
{
await EventLog(t);
switch (t.type?.ToLower())
{
case "subscribe"://关注
case "unsubscribe"://取关
_logger.LogWarning("开始处理取关!");
users = await GetUser(t.appid, t.appuserid); //请求此用户的所有用户信息
rrWeworkUser = await BindWeworkUser(users, mainExternalUsers);//查询当前客户好友关系
foreach (var item in rrWeworkUser)
{
await ChangeWeworkUser(item);//入库
}
//await SetSourcePassInfo(mainExternalUsers, users, t);
_logger.LogWarning("结束处理取关!");
break;
case "merge"://合并
_logger.LogWarning("开始处理合并!");
if (int.TryParse(t.cid, out int incid))
{
users = await GetUser(incid);//请求此用户的所有用户信息
rrWeworkUser = await BindWeworkUser(users, mainExternalUsers);//查询当前客户好友关系
foreach (var item in rrWeworkUser)
{
await ChangeWeworkUser(item);//入库
}
}
//await SetSourcePassInfo(mainExternalUsers, users, t);
_logger.LogWarning("结束处理合并!");
break;
case "unmerge"://解绑
_logger.LogWarning("开始处理解绑!");
var cidarry = t.cid?.Trim().Split(',');
if (cidarry != null && cidarry.Count() == 2)
{
var scid = cidarry[0];
var ecid = cidarry[1];
var susers = await GetUser(int.Parse(scid));//分别查询出两个cid对应的客户信息
var eusers = await GetUser(int.Parse(ecid));//
var swu = await BindWeworkUser(susers, mainExternalUsers);//查询当前客户好友关系
//await SetSourcePassInfo(mainExternalUsers, susers, t);
var ewu = await BindWeworkUser(eusers, secExternalUsers);//查询当前客户好友关系
//await SetSourcePassInfo(secExternalUsers, eusers, t);
foreach (var item in swu)
{
await ChangeWeworkUser(item);//入库
}
foreach (var item in ewu)
{
await ChangeWeworkUser(item);//入库
}
var unlist = await UnBindWeworkUser(susers, eusers);//拼接两个cid的 重叠部分
foreach (var item in unlist)
{
//事业部不发生变化
await ChangeWeworkUser(item, false);//解绑匹配的新数据不入库,旧数据标记删除
}
}
_logger.LogWarning("结束处理解绑!");
break;
case "eidbind"://跨部门换绑工号 300秒超时
if (t.olddeptid.HasValue && t.deptid.HasValue && t.olddeptid != t.deptid)
{
var kfuser = await GetKFUser(t.appid, t.userid); //请求此用户的所有用户信息
if (kfuser.Count() > 300)
{//给队列逐条处理
IList<string> appidStr = new List<string>();
IList<string> euseridStr = new List<string>();
foreach (var item in kfuser)
{
appidStr.Add(item.Appid_ext);
euseridStr.Add(item.Externaluserid);
if (appidStr.Count() > 9)
{
WeworkWorkerDto dto = new WeworkWorkerDto();
dto.type = "inner_eidbind_item";
dto.appuserid = String.Join(",", euseridStr);
dto.appid = String.Join(",", appidStr);
dto.deptid = t.deptid;
dto.olddeptid = t.olddeptid;
PushKafKa(dto);
appidStr = new List<string>();
euseridStr = new List<string>();
}
}
//循环结束验证是否有未完成
if (euseridStr.Any())
{
WeworkWorkerDto lastdto = new WeworkWorkerDto();
lastdto.type = "inner_eidbind_item";
lastdto.appuserid = String.Join(",", euseridStr);
lastdto.appid = String.Join(",", appidStr);
lastdto.deptid = t.deptid;
lastdto.olddeptid = t.olddeptid;
PushKafKa(lastdto);
}
}
else
{//直接批量处理
foreach (var item in kfuser)//逐个好友处理数据
{
var iusers = await GetUser(item.Appid, item.Externaluserid);
List<WeworkExternalUser> kfExternal = new List<WeworkExternalUser>();
rrWeworkUser = await BindWeworkUser(iusers, kfExternal);//重新计算当前客户好友关系
//await SetSourcePassInfo(kfExternal, iusers, t);
foreach (var rritem in rrWeworkUser)
{
await ChangeWeworkUser(rritem);//更新和插入 事业部变更后的好友关系
if (rritem.deptid == t.deptid)
{
rritem.deptid = t.olddeptid.Value;
await ChangeWheorkUserDeptid(rritem);//查找 事业部变更前的好友关系,有则标记删除
}
}
}
}
}
break;
case "inner_eidbind_item"://逐个处理换绑资源
if (!string.IsNullOrEmpty(t.appid) && !string.IsNullOrEmpty(t.appuserid) && t.olddeptid.HasValue && t.deptid.HasValue)
{
var appidList = t.appid.Split(',').ToList();
var appuseridList = t.appuserid.Split(',').ToList();
if (appidList.Count() == appuseridList.Count())
{//一次性处理9个
int index = 0;
foreach (var appid in appidList)
{
var appuserid = appuseridList[index];
users = await GetUser(appid, appuserid);
List<WeworkExternalUser> kfExternal = new List<WeworkExternalUser>();
rrWeworkUser = await BindWeworkUser(users, kfExternal);//重新计算当前客户好友关系
//await SetSourcePassInfo(kfExternal, users, t);
foreach (var rritem in rrWeworkUser)
{
await ChangeDeptWeworkUser(rritem, t.olddeptid.Value);//更新和插入 事业部变更后的好友关系
}
index++;
}
}
}
break;
case "register":
users = await GetUser(t.appid, t.appuserid);
rrWeworkUser = await BindWeworkUser(users, mainExternalUsers);//重新计算当前客户好友关系
//await SetSourcePassInfo(mainExternalUsers, users, t);
break;
}
}
catch (Exception ex)
{
_logger.LogWarning(ex.Message, ex);
}
}
/// <summary>
/// 查询客服名下所有客户
/// </summary>
/// <param name="appid"></param>
/// <param name="userid"></param>
/// <returns></returns>
private async Task<IList<WeworkExternalUser>> GetKFUser(string appid, string userid)
{
MySqlParameter[] param = new MySqlParameter[] {
new MySqlParameter(){ DbType=System.Data.DbType.String,Value=appid,ParameterName="appid"},
new MySqlParameter(){ DbType=System.Data.DbType.String,Value=userid,ParameterName="userid"}
};
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsDbContext>>();
var extUser = await dncmsRepository.ExecuteSqlToListAsync<WeworkExternalUser>($"select appid,userid,appid_ext,externaluserid,deptid,unionid,subscribe,subscribetime from WeworkExternalUser where appid=@appid and userid=@userid", param);
return extUser;
}
/// <summary>
/// 获取客户当前 resid数据
/// </summary>
/// <param name="appid"></param>
/// <param name="userid"></param>
/// <returns></returns>
private async Task<IList<UserInfoReq>> GetUser(string appid, string appuserid)
{
IList<UserInfoReq> res = new List<UserInfoReq>();
var url = $"{_systemConfig.Value.zxdCoreApi}/Api/UserInfo/list";
var resModel = await _httpClient.GetAsync<ZXDApiResult<IList<UserInfoReq>>>($"{url}?appid={appid}&appuserid={appuserid}");
if (resModel.code == 0)
{
res = resModel.data;
}
return res;
}
/// <summary>
/// 获取客户当前 resid数据
/// </summary>
/// <param name="appid"></param>
/// <param name="userid"></param>
/// <returns></returns>
private async Task<IList<UserInfoReq>> GetUser(int? customerid)
{
IList<UserInfoReq> res = new List<UserInfoReq>();
if (customerid.HasValue)
{
var url = $"{_systemConfig.Value.zxdCoreApi}/Api/UserInfo/list";
//var req = HttpHelper.GetData(url, $"?cid={customerid}", Encoding.UTF8);
//var resModel = JsonHelper.FromJson<ZXDApiResult<IList<UserInfoReq>>>(req);
var resModel = await _httpClient.GetAsync<ZXDApiResult<IList<UserInfoReq>>>($"{url}?cid={customerid}");
if (resModel.code == 0)
{
res = resModel.data;
}
}
return res;
}
/// <summary>
/// 匹配两个好友解绑时的所有情况
/// </summary>
/// <param name="userInfo1"></param>
/// <param name="userInfo2"></param>
/// <returns></returns>
private async Task<IList<ResResidWeworkUser>> UnBindWeworkUser(IList<UserInfoReq> userInfo1, IList<UserInfoReq> userInfo2)
{
IList<ResResidWeworkUser> res = new List<ResResidWeworkUser>();
//var userGup1 = userInfo1.Where(m => !string.IsNullOrEmpty(m.resid)).GroupBy(m => m.resid).ToDictionary(m => m.Key, n => n.ToList());
//var userGup2 = userInfo2.Where(m => !string.IsNullOrEmpty(m.resid)).GroupBy(m => m.resid).ToDictionary(m => m.Key, n => n.ToList());
List<WeworkExternalUser> mainExternalUsers = new List<WeworkExternalUser>();
List<WeworkExternalUser> secExternalUsers = new List<WeworkExternalUser>();
//获取当前解绑后的 rrwu数据
var wwuser1 = await BindWeworkUser(userInfo1, mainExternalUsers);
var wwuser2 = await BindWeworkUser(userInfo2, secExternalUsers);
//拼接两个数据的交集
foreach (var u1 in wwuser1)
{
foreach (var u2 in wwuser2)
{
ResResidWeworkUser r = new ResResidWeworkUser();
r.resid = u1.resid;
r.unionid = u1.unionid;
r.deptid = u1.deptid;
r.first_subscribetime = u1.first_subscribetime;
r.last_subscribetime = u1.last_subscribetime;
r.status = u1.status;
//删除集合1与集合2的 交集
r.appid = u2.appid;
r.appuserid = u2.appuserid;
r.isdelete = 1;
r.utime = DateTime.Now;
res.Add(r);
}
}
//cid2 与1的交集数据
foreach (var u1 in wwuser2)
{
foreach (var u2 in wwuser1)
{
ResResidWeworkUser r = new ResResidWeworkUser();
r.resid = u1.resid;
r.unionid = u1.unionid;
r.deptid = u1.deptid;
r.first_subscribetime = u1.first_subscribetime;
r.last_subscribetime = u1.last_subscribetime;
r.status = u1.status;
//删除集合1与集合2的 交集
r.appid = u2.appid;
r.appuserid = u2.appuserid;
r.isdelete = 1;
r.utime = DateTime.Now;
res.Add(r);
}
}
return res;
}
/// <summary>
/// 匹配当前用户最终好友状态
/// </summary>
/// <param name="userInfo"></param>
/// <returns></returns>
private async Task<List<ResResidWeworkUser>> BindWeworkUser(IList<UserInfoReq> userInfo, List<WeworkExternalUser> weworkExternalUsers)
{
List<ResResidWeworkUser> resResidWeworkUsers = new List<ResResidWeworkUser>();
if (userInfo.Count == 0)
{
return resResidWeworkUsers;
}
var extuserfilter = userInfo.Select(n => $"'{n.appuserid}'").Distinct().ToList();
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsDbContext>>();
MySqlParameter[] param = new MySqlParameter[] { };
var extUserList = await dncmsRepository.ExecuteSqlToListAsync<WeworkExternalUser>($"select appid,userid,appid_ext,externaluserid,deptid,unionid,subscribe,subscribetime,regdate from WeworkExternalUser where externaluserid in ({string.Join(",", extuserfilter)})", param);
foreach (var item in extUserList)
{
var userinfoItem = userInfo.FirstOrDefault(n => n.appid == item.Appid_ext && n.appuserid == item.Externaluserid && n.appid.EndsWith("_1"));
if (userinfoItem != null)
{
weworkExternalUsers.Add(item);
}
}
if (weworkExternalUsers.Any())
{
//好友关系分组
var weworkExternalUsersGup = weworkExternalUsers.GroupBy(m => m.deptid + ";" + m.Appid + ";" + m.Externaluserid)
.ToDictionary(m => m.Key, n => n.ToList());
//resid分组
var residGup = userInfo.Where(m => !string.IsNullOrEmpty(m.resid))
.GroupBy(m => m.resid).ToDictionary(m => m.Key, n => n.Min(i => i.uid));
//组合关系数据
foreach (var resid in residGup)
{
foreach (var wwe in weworkExternalUsersGup)
{
var deptid = wwe.Key.Split(';')[0];
var appid = wwe.Key.Split(';')[1];
var extuserid = wwe.Key.Split(';')[2];
var extList = wwe.Value;
var extSubList = extList.Where(m => m.subscribe == 1);
ResResidWeworkUser res = new ResResidWeworkUser();
res.uid = int.Parse(resid.Value);
res.deptid = int.Parse(deptid);
res.resid = resid.Key;
res.appid = appid;
res.appuserid = extuserid;
res.unionid = extList.Where(m => !string.IsNullOrEmpty(m.unionid)).FirstOrDefault()?.unionid;//unionid有则取
if (extSubList.Any())
{
res.first_subscribetime = extSubList.Min(m => m.subscribetime ?? m.regdate);
res.last_subscribetime = extSubList.Max(m => m.subscribetime ?? m.regdate);
res.status = 1;//最终关注状态
}
else
{
res.first_subscribetime = null;
res.last_subscribetime = null;
res.status = 0;//最终关注状态
}
res.ctime = DateTime.Now;
res.isdelete = 0;
res.utime = DateTime.Now;
resResidWeworkUsers.Add(res);
}
}
}
return resResidWeworkUsers;
}
/// <summary>
/// 入库ResResidWeworkUser
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
private async Task ChangeWeworkUser(ResResidWeworkUser model, bool add = true)
{
if (model == null) return;
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var tag = dncmsbaseRepository.GetRepository<ResResidWeworkUser>().Query()
.FirstOrDefault(m => m.resid == model.resid && m.appuserid == model.appuserid && m.appid == model.appid && m.deptid == model.deptid);
using var transaction = await dncmsbaseRepository.BeginTransactionAsync();
try
{
if (tag != null)
{
tag.status = model.status;
tag.first_subscribetime = model.first_subscribetime;
tag.last_subscribetime = model.last_subscribetime;
tag.isdelete = model.isdelete;
//更新已有好友关系
await dncmsbaseRepository.GetRepository<ResResidWeworkUser>().UpdateAsync(tag);
//PushKafKa(MappingTolog(tag));
await dncmsbaseRepository.GetRepository<ResResidWeworkUserLog>().InsertAsync(MappingTolog(tag));
}
else if (add)
{
//插入新的好友关系
var outModel = await dncmsbaseRepository.GetRepository<ResResidWeworkUser>().InsertAsync(model);
//PushKafKa(MappingTolog(outModel));
await dncmsbaseRepository.GetRepository<ResResidWeworkUserLog>().InsertAsync(MappingTolog(outModel));
}
await transaction.CommitAsync();
}
catch (Exception ex)
{
await transaction.RollbackAsync();
await transaction.DisposeAsync();
_logger.LogError($"入库ResResidWeworkUser报错{ex.Message}", ex);
}
}
/// <summary>
/// 换事业部
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
private async Task ChangeDeptWeworkUser(ResResidWeworkUser model, int olddeptid)
{
if (model == null) return;
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var tag = dncmsbaseRepository.GetRepository<ResResidWeworkUser>().Query()
.FirstOrDefault(m => m.resid == model.resid && m.appuserid == model.appuserid && m.appid == model.appid && m.deptid == olddeptid);
using var transaction = await dncmsbaseRepository.BeginTransactionAsync();
try
{
if (tag != null)
{
tag.status = model.status;
tag.first_subscribetime = model.first_subscribetime;
tag.last_subscribetime = model.last_subscribetime;
tag.isdelete = model.isdelete;
tag.deptid = model.deptid;//更新事业部
//更新已有好友关系
await dncmsbaseRepository.GetRepository<ResResidWeworkUser>().UpdateAsync(tag);
//插入新数据
await dncmsbaseRepository.GetRepository<ResResidWeworkUserLog>().InsertAsync(MappingTolog(tag));
//删除旧数据
var delTag = MappingTolog(tag);
delTag.isdelete = 1;
delTag.deptid = olddeptid;
await dncmsbaseRepository.GetRepository<ResResidWeworkUserLog>().InsertAsync(delTag);
}
else
{
//插入新的好友关系
var outModel = await dncmsbaseRepository.GetRepository<ResResidWeworkUser>().InsertAsync(model);
//PushKafKa(MappingTolog(outModel));
await dncmsbaseRepository.GetRepository<ResResidWeworkUserLog>().InsertAsync(MappingTolog(outModel));
}
await transaction.CommitAsync();
}
catch (Exception ex)
{
await transaction.RollbackAsync();
await transaction.DisposeAsync();
_logger.LogError($"换事业部报错:{ex.Message}", ex);
}
}
/// <summary>
/// 事业部替换
/// </summary>
/// <returns></returns>
private async Task ChangeWheorkUserDeptid(ResResidWeworkUser oldrrw)
{
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var tag = dncmsbaseRepository.GetRepository<ResResidWeworkUser>().Query().FirstOrDefault(m => m.appuserid == oldrrw.appuserid && m.appid == oldrrw.appid && m.deptid == oldrrw.deptid);
if (tag != null && tag.isdelete == 0)
{
//await dncmsbaseRepository.GetRepository<ResResidWeworkUser>().UpdateAsync(tag);
await dncmsbaseRepository.GetRepository<ResResidWeworkUserLog>().InsertAsync(MappingTolog(tag));//旧事业部数据删除
}
}
/// <summary>
/// 隐射对象
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
private ResResidWeworkUserLog MappingTolog(ResResidWeworkUser model)
{
if (model == null) return null;
ResResidWeworkUserLog res = new ResResidWeworkUserLog();
res.mainid = model.id;
res.uid = model.uid;
res.appid = model.appid;
res.appuserid = model.appuserid;
res.resid = model.resid;
res.deptid = model.deptid;
res.unionid = model.unionid;
res.status = model.status;
res.isdelete = model.isdelete;
res.first_subscribetime = model.first_subscribetime;
res.last_subscribetime = model.last_subscribetime;
res.ctime = model.ctime;
res.utime = model.utime;
return res;
}
/// <summary>
/// 推送队列
/// </summary>
/// <param name="log"></param>
/// <returns></returns>
private async Task PushKafKa(WeworkWorkerDto log)
{
//IList<UserInfoReq> res = new List<UserInfoReq>();
//var url = $"{_systemConfig.Value.zxdCoreApi}/Api/WeWork/SyncBinding";
//var req = HttpHelper.PostAjaxData(url, JsonHelper.ToJson(log), Encoding.UTF8);
//var resModel = JsonHelper.FromJson<ZXDApiResult<string>>(req);
//if (resModel.code != 0)
//{
// Log.Write(Serilog.Events.LogEventLevel.Error, req);
//}
var consumers = KafkaClient.GetConsumers();
await KafkaClient.SendMessage(consumers.FirstOrDefault(n => n.Topic == "crm-topic"), log);
}
/// <summary>
/// 记录日志
/// </summary>
/// <returns></returns>
private async Task EventLog(WeworkWorkerDto t)
{
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
ResResidWeworkEventLog log = new ResResidWeworkEventLog();
log.appid = t.appid;
log.userid = t.userid;
log.appuserid = t.appuserid;
log.type = t.type;
log.deptid = t.deptid;
log.olddeptid = t.olddeptid;
log.eventJson = JsonHelper.ToJson(t);
log.cid = t.cid;
log.ctime = DateTime.Now;
await dncmsbaseRepository.GetRepository<ResResidWeworkEventLog>().InsertAsync(log);
}
/// <summary>
/// 外部联系人 赋值资源进线时间
/// </summary>
/// <param name="extUser"></param>
/// <param name="userInfoList"></param>
/// <returns></returns>
private async Task SetSourcePassInfo(List<WeworkExternalUser> extUser, IList<UserInfoReq> userInfoList, WeworkWorkerDto t)
{
try
{
if (extUser.Count == 0 || userInfoList.Count == 0)
{
return;
}
var uidFilter = userInfoList.Select(n => Convert.ToInt32(n.uid)).Distinct().ToList();
var regdate = await GetRegDate(userInfoList);
using var scope = _serviceProvider.CreateAsyncScope();
var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<DncmsbaseDbContext>>();
var dbList = await dncmsbaseRepository.GetRepository<ResourceProtectInfo>().Query().Where(n => uidFilter.Contains(n.uid)).ToListAsync();
var groupdeptid = extUser.GroupBy(n => n.deptid).ToList();
List<ResourceProtectInfo> insertList = new List<ResourceProtectInfo>();
List<ResourceProtectInfo> updateList = new List<ResourceProtectInfo>();
List<ResourceProtectInfo> delList = new List<ResourceProtectInfo>();
var customerid = 0;
Int32.TryParse(userInfoList.FirstOrDefault().customerid, out customerid);
using var transaction = await dncmsbaseRepository.BeginTransactionAsync();
foreach (var deptid in groupdeptid)
{
var deptUser = extUser.Where(n => n.deptid == deptid.Key).ToList();
var subItem = deptUser.OrderBy(n => n.subscribetime).OrderBy(n => n.regdate).FirstOrDefault();
var dbItem = dbList.FirstOrDefault(n => n.deptid == deptid.Key && n.customerid == customerid);
var useritem = userInfoList.FirstOrDefault(n => n.appid == subItem.Appid_ext && n.appuserid == subItem.Externaluserid);
if (useritem == null)
{
continue;
}
var uid = 0;
Int32.TryParse(useritem.uid, out uid);
if (dbItem == null)
{
ResourceProtectInfo model = new ResourceProtectInfo
{
uid = uid,
appid = useritem.appid,
appuserid = useritem.appuserid,
resid = useritem.resid,
unionid = useritem.unionid,
customerid = customerid,
deptid = subItem.deptid,
firstfollowtime = deptUser.Min(m => m.subscribetime ?? m.regdate),
followtime = deptUser.Where(n => n.subscribe == 1).Min(m => m.subscribetime ?? m.regdate),
ctime = DateTime.Now,
regtime = regdate,
eventtype = t.type,
};
if (!model.followtime.HasValue)
{
model.isdelete = 1;
}
insertList.Add(model);
}
else
{
dbItem.deptid = subItem.deptid;
dbItem.uid = uid;
dbItem.resid = useritem.resid;
dbItem.unionid = useritem.unionid;
dbItem.customerid = customerid;
dbItem.deptid = subItem.deptid;
dbItem.firstfollowtime = deptUser.Min(m => m.subscribetime ?? m.regdate);
dbItem.followtime = deptUser.Where(n => n.subscribe == 1).Min(m => m.subscribetime ?? m.regdate);
dbItem.utime = DateTime.Now;
dbItem.regtime = regdate;
dbItem.eventtype = t.type;
if (!dbItem.followtime.HasValue)
{
dbItem.isdelete = 1;
}
updateList.Add(dbItem);
}
}
bool update = false;
if (updateList.Count > 0)
{
update = true;
await SetDeptInfo(updateList);
await dncmsbaseRepository.GetRepository<ResourceProtectInfo>().BatchUpdateAsync(updateList);
}
if (insertList.Count > 0)
{
update = true;
await SetDeptInfo(insertList);
await dncmsbaseRepository.GetRepository<ResourceProtectInfo>().BatchInsertAsync(insertList);
}
if (update)
{
transaction.Commit();
}
}
catch (Exception ex)
{
Log.Error($"计算资源进线时间错误{ex.Message}");
}
}
private async Task<DateTime?> GetRegDate(IList<UserInfoReq> userInfoList)
{
using var scope = _serviceProvider.CreateAsyncScope();
var zxdRepository = scope.ServiceProvider.GetRequiredService<IBaseRepository<ZxdDbContext>>();
var userList = userInfoList.Select(n => n.appuserid).Distinct().ToList();
var softuser = await zxdRepository.GetRepository<SOFT_USER>().Query().Where(n => userList.Contains(n.USERNAME)).ToListAsync();
return softuser.Min(n => n.REGDATE);
}
private async Task SetDeptInfo(List<ResourceProtectInfo> model)
{
var key = "deptment_list_new";
var deptInfo = new List<DeptmentDto>();
if (await _redisManager.ExistsAsync(key))
{
deptInfo = await _redisManager.GetListAsync<DeptmentDto>(key);
}
if (deptInfo != null && deptInfo.Count > 0)
{
foreach (var item in model)
{
var dept = deptInfo.FirstOrDefault(n => n.Id == item.deptid);
item.deptname = dept?.Title;
item.groupid = dept?.GroupId;
}
}
}
}
}

View File

@ -0,0 +1,30 @@
{
"ConnectionStrings": {
"zxdcrm": "Data Source=10.22.15.61;Port=3306;Initial Catalog=zxdcrm;user id=qianbenjie;password=Hcqianbenjie@123;Old Guids=true;SslMode=None",
"dncmsbase": "Data Source=10.22.15.68;Port=3306;Initial Catalog=dncmsbase;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"usercenter": "Data Source=10.22.15.68;Port=3306;Initial Catalog=usercenter;user id=hguser;password=nH5L$&Hxxco;SslMode=None",
"dncms": "Data Source=10.22.15.68;Port=3306;Initial Catalog=dncms;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"companyBaseConf": "Data Source=10.22.15.68;Port=3306;Initial Catalog=db_company_base_conf;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None"
},
"Consumers": [
{
"Host": "172.18.11.77:9092,172.18.11.76:9092",
"GroupId": "crm",
"Topic": "crm-topic"
}
],
"TaskConfig": {
"TaskName": "Zxd.WeworkUserWorker",
"TaskRemarks": "Zxd.WeworkUserWorker",
"Enable": true,
"SecondsDelay": 5
},
"Exceptionless": {
"ServerUrl": "http://10.22.11.9:5000",
"ApiKey": "w8ktfSOJalWw6jZWjTs8qSOoLteO7WTSRdgIvja8"
},
"SystemConfig": {
"zxdCoreApi": "http://120.77.165.155:8089",
"nodeWebApi": "http://10.22.15.5:8080"
}
}

View File

@ -0,0 +1,46 @@
{
"ConnectionStrings": {
"zxdcrm": "Data Source=mysql98ff96c3dffa.rds.ivolces.com;Port=3306;Initial Catalog=zxdcrm;user id=qianbenjie;password=Hcqianbenjie@123;Old Guids=true;SslMode=None",
"dncmsbase": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=dncmsbase;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"usercenter": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=usercenter;user id=hguser;password=nH5L$&Hxxco;SslMode=None",
"dncms": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=dncms;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None",
"companyBaseConf": "Data Source=pc-wz927dkkv6y71jao7.rwlb.rds.aliyuncs.com;Port=3306;Initial Catalog=db_company_base_conf;user id=dn_cms;password=dn3EdxCms@8zsw_2Wkm;SslMode=None"
},
"Consumers": [
{
"Host": "172.18.11.77:9092,172.18.11.76:9092",
"GroupId": "crm",
"Topic": "crm-topic"
}
],
"TaskConfig": {
"TaskName": "Zxd.WeworkUserWorker",
"TaskRemarks": "Zxd.WeworkUserWorker",
"Enable": true,
"SecondsDelay": 5
},
"Exceptionless": {
"ServerUrl": "http://10.22.11.9:5000",
"ApiKey": "w8ktfSOJalWw6jZWjTs8qSOoLteO7WTSRdgIvja8"
},
"SystemConfig": {
"zxdCoreApi": "http://120.77.165.155:8089",
"nodeWebApi": "https://r2.soft.dn8188.com"
},
"Redis": [
{
"Name": "ZXD",
"HostName": "redis-cngzdnddztrevahsz.redis.ivolces.com",
"Port": "6379",
"Password": "dn8sCe@mxTvzx",
"Defaultdatabase": "0"
},
{
"Name": "UserCenter",
"HostName": "redis-cngzdnddztrevahsz.redis.ivolces.com",
"Port": "6379",
"Password": "dn8sCe@mxTvzx",
"Defaultdatabase": "0"
}
]
}

View File

@ -0,0 +1,52 @@
{
"ConnectionStrings": {
"zxdcrm": "Server=192.168.11.141;Database=zxdcrm;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"dncmsbase": "Server=192.168.11.41;Database=dncmsbase;UserId=root;Password=sa123456.;port=3306;",
"usercenter": "Server=192.168.11.41;Database=usercenter;UserId=root;Password=sa123456.;port=3306;",
"dncms": "Server=192.168.11.41;Database=dncms;UserId=root;Password=sa123456.;port=3306;",
"companyBaseConf": "Server=192.168.11.141;Database=db_company_base_conf;UserId=tafadmin;Password=tafadmin2017;port=3306;",
"crm": "Server=192.168.11.141;Database=db_crm;UserId=tafadmin;Password=tafadmin2017;port=3306;"
},
"Consumers": [
{
"Host": "192.168.11.101:9092,192.168.11.104:9092",
"GroupId": "crm",
"Topic": "crm-topic"
},
{
"Host": "192.168.11.104:9092,192.168.11.104:9092",
"GroupId": "crm",
"Topic": "ResPassTime"
}
],
"TaskConfig": {
"TaskName": "DG.Worker.Sample",
"TaskRemarks": "DG.Worker.Sample",
"Enable": true,
"SecondsDelay": 5
},
"Exceptionless": {
"ServerUrl": "http://10.22.12.9:5000",
"ApiKey": "tmrp0GmwyTMe6UxJIw7LXAcIWYKEUgy2kprMiybd"
},
"SystemConfig": {
"zxdCoreApi": "http://192.168.11.81:8089",
"nodeWebApi": "http://120.238.224.24:10034"
},
"Redis": [
{
"Name": "ZXD",
"HostName": "192.168.11.81",
"Port": "6379",
"Password": "Abc@123456",
"Defaultdatabase": "1"
},
{
"Name": "UserCenter",
"HostName": "192.168.11.103",
"Port": "6379",
"Password": "123",
"Defaultdatabase": "0"
}
]
}

View File

@ -0,0 +1,31 @@
using Zxd.Core.Domain.Dto.Activity;
namespace Zxd.Core.Domain
{
public class ActivityDomain : IActivityDomain
{
private readonly IBaseRepository<DncmsbaseDbContext> _cmsRepository;
public ActivityDomain(
IBaseRepository<DncmsbaseDbContext> cmsRepository)
{
_cmsRepository = cmsRepository;
}
/// <summary>
/// 获取活动名称
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task<string> GetActivityNameAsync(GetActivityNameRequest request)
{
var tag = await _cmsRepository.GetRepository<resourcetag>().Query().Where(w => w.remark == request.Code).FirstOrDefaultAsync();
if (tag == null)
{
throw new ArgumentException($"活动编码无效:{request.Code}");
}
return tag.name;
}
}
}

View File

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Zxd.Domain
{
internal class CacheDomain : ICacheDomain
{
private readonly IRedisManager _redisManager;
private readonly IBaseRepository<ZxdDbContext> _repository;
public CacheDomain(IRedisManager redisManager,
IBaseRepository<ZxdDbContext> repository)
{
_redisManager = redisManager;
_repository = repository;
}
private async Task<List<BAS_PARAMETER>> GetParameterList()
{
if (!await _redisManager.ExistsAsync(CacheKeys.ParameterList))
{
var list = await _repository.GetRepository<BAS_PARAMETER>().QueryListAsync();
await _redisManager.SetAsync(CacheKeys.ParameterList, list, new TimeSpan(0, 5, 0));//15分钟过期
return list;
}
else
{
return await _redisManager.GetListAsync<BAS_PARAMETER>(CacheKeys.ParameterList);
}
}
public async Task<string> GetValueParameter(string key)
{
var list = await GetParameterList();
return list.FirstOrDefault(x => x.PARAKEY == key)?.PARAVALUE ?? "";
}
public async Task<int> GetEarlyWarningValue()
{
var data = await GetValueParameter("EarlyWarningValue");
if (string.IsNullOrEmpty(data) || int.TryParse(data, out int value))
{
return 5;
}
return value;
}
public async Task<List<SsoUserTokenInfo>> GetTokens()
{
var key = CacheKeys.TokenList;
if (await _redisManager.ExistsAsync(key))
{
return await _redisManager.GetListAsync<SsoUserTokenInfo>(key);
}
return new List<SsoUserTokenInfo>();
}
public async Task AddToken(SsoUserTokenInfo token)
{
var key = CacheKeys.TokenList;
if (!await _redisManager.ExistsAsync(key))
{
await _redisManager.SetAsync(key, new List<SsoUserTokenInfo>() { token });
}
else
{
var list = await _redisManager.GetListAsync<SsoUserTokenInfo>(key);
list.Add(token);
await _redisManager.SetAsync(key, list, TimeSpan.FromDays(1));
}
}
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Zxd.Domain.Config
{
internal class CacheKeys
{
public const string ProductModuleList = "product_module_list";
public const string ProductGroupList = "product_group_list";
public const string ProductTypeList = "product_type_list";
public const string ProductTeacherList = "product_teacher_list";
public const string ProductList = "product_list";
public const string StandardType = "standard_product_type";
public const string StandardWay = "standard_product_way";
public const string DeptmentList = "deptment_list_new";
public const string ModuleList = "module_list";
public const string ParameterList = "cache_parameter_list";
public const string TokenList = "cache_token_list";
}
}

View File

@ -0,0 +1,13 @@
namespace Zxd.Domain.Config
{
public class ClientKey
{
public string Id { get; set; }
public string Name { get; set; }
public string AccessKey { get; set; }
public string Vi { get; set; }
public string NewAccessKey { get; set; }
}
}

View File

@ -0,0 +1,191 @@

using System.ComponentModel;
namespace Zxd.Domain.Config
{
#region
public enum EnumInterfaceErrcode
{
= 10000,
= 10001,//新增的code
= 10002,
= 10003,
= 10004,
= 10005,
= 10006,
= 10007,
= 10008,
= 10009,
= 10010,
= 10011,
= 10012,
= 10013,
= 10014,
= 10015,
= 10016,
= 10017,
= 10018,
= 10019,
= 10020,
= 10021,
= 10022,
= 10023,
= 10024,
= 10025,
= 10026,
= 10027,
= 10028,
= 10029,
= 10030,
= 10031,
使 = 10032,
= 10033,
= 10034,
= 10035,
= 10036,
= 10037,
= 10038,
= 10039,
= 10040,
= 10041,
= 10042,
= 10043,
= 10044,
= 10045,
= 10046,
= 10047,
= 10048,
openID已存在绑定 = 10049,
= 10050,
UP账号已存在绑定 = 10051,
使 = 10052,
UP账号已被管理员禁止修改密码 = 10053,
= 10054,
= 10055,
= 10056,
= 10057
}
#endregion
#region
public enum Parameter
{
, , , ,
ORD_MemoStatistics_01, //工单统计
ORD_MemoStatistics_ZhenGu, //诊股资源工单统计
Sys_Environment_DeptCode,//当前系统部门编码
Sys_ExlceImport_PhoneRule,//手机号码生成规则
Sys_ExcelImport_SkipOran,//是否跳过机构验证导入等于配置的机构数据
Sys_IsShowFXH,//是否显示渤海信息
Sys_RetrievePwd_Name,//找回密码短信配置 您好,密码找回短信配置:{0}是密码,{1}是帐号
Is_ShowCustomerMobileArea,//是否显示号码地区
Sys_HomeTab_Javascript,//系统home页面的主题框架脚本 主页面的js,Tab打开风格JS
Sys_IsFreeze_Wdzm,//是否冻结我的桌面 默认是不冻结可以关闭的true=冻结,false=可以关闭
Sys_IsMiniCustomerInfo,//显示简单详细资料
ISVR_AD_CheckUserNameCompetence,
ISVR_AD_upAgentCreateActiveOrder,
ISVR_AD_upAgentOpenOrder,
ISVR_AD_CancelActiveOrder,
ISVR_AD_HstRegUser,
ISVR_AD_HstDelUserName,
ISVR_AD_HstRoomConfig,
ISVR_AD_HstBatchAddUserRight,
ISVR_AD_HstAddUserPower,
ISVR_AD_TbHstRegUser,
ISVR_AD_TbHstRight,
ISVR_AD_TGHstRoomConfig,
ISVR_AD_HstBlackConfig,
ISVR_IAD_localhostOpenOrder,
ISVR_IAD_localhostSimpleOpenOrder,
ISVR_IAD_localhostCanclOrder,
ISVR_IAD_localhostHstBatchAddUserRight,
ISVR_IAD_localhostHstRoomConfig,
ISVR_IAD_localhostHstDelUserName,
ISVR_IAD_localhostHstRegUser,
ISVR_IAD_localhostHstAddUserPower,
HQ_RiaService_ActiveMaunl,
Sms_TencentResetPwdTid, //腾讯云重置密码模板ID
Sms_TencentHgMsgTid, //腾讯云合规消息模板ID
Sms_TencentOpenOrderTid,
Sms_TencentRegisterTid,//腾讯云用户注册模板ID
Sms_TencentOpenOrderDonateTid,
Sms_TencentSign, //腾讯云短信签名
Sms_TencentPayMsgTid, //腾讯云在线支付消息模板ID
CustomerCheckData,//质检抓取时间
Res_EffectAnalysis_01, //资源效果分析
ISVR_AD_UpdateManager,
Sys_Bussiness_Code, //营业部Code
Sys_Environment_LogOn, //当前系统部门编码
WeiXin_OrderCountShowColumn, //微信订单统计产品小类展示列
WeiXin_WorkAccountInitCount, //客服工作微信号数量
WeiXIn_SzzyOrderUnPayDayInterval, //上证综研未支付订单间隔天数
UserCenter_RiaService_AddOrderOpen,//创建订单
UserCenter_RiaService_OpenOrder,//开通订单
UserCenter_RiaService_AddOrderOpenFree,//免费订单
UserCenter_RiaService_refund,//退款接口
UserCenter_RiaService_closeFreeOrder,//关闭活动免费订单
UserCenter_RiaService_ResetPwd,//重置密码接口
UserCenter_RiaService_ResetMobile,//重置手机号接口
UserCenter_RiaService_UnBindQW,//软件和企业微信关系解绑
UserCenter_RiaService_ContractSign,//签订合同
UserCenter_RiaService_Settlement,//和解协议
UserCenter_RiaService_CancelComplaint,//撤销投诉协议
UserCenter_RiaService_SignPdf,//合同pdf地址
UserCenter_RiaService_OrderGet,//订单信息获取
UserCenter_RiaService_ForceMerge,//调用Node用户中心进行客户合并
UserCenter_RiaService_UnBind,//调用Node用户中心进行客户解绑
UserCenter_RiaService_Risk,//获取风险评测信息
UserCenter_RiaService_Flag,//获取是否适合信息
//UserCenter_RiaService_Riske,//风控URL
Sys_ProjectType, //项目类型,1:证券之星广州 2:南京鼎迈
Sys_OrderOpenCountByTimeType, //订单开通统计的时间周期 otime开通时间 arrivaltime到账时间
Sys_ShowMobileStartDate, //为空:就都不显示手机 日期:从该日期之后的资源,显示电话
Sys_UserComBoxAllShow, //员工分组下拉菜单显示所有员工 0不显示离职 1显示全部
WeiXin_GroupLeaderPasueReceiveRes, //主管是否暂停接收资源 1暂停 0接收
WeiXin_TotalPauseReceiveRes, //微信总资源客户数较多暂停接收资源 1暂停 0接收
Sys_CanExportAllocate, //是否能导出资源
Sys_OrderClientIdKey, //订单加密客户端ID的键名
Sys_CanShowResMobile, //是否在资源页面显示手机号
WeiXin_TotalPauseUserNum, //总资源暂停客服人数
WeiXin_OrderUserNameRequired, //订单用户微信用户名必填项
WeiXin_IllegalKewords, //合规关键词词配置
MonthCommission, //月份提成
WeiXin_CrossDBSzzyOrder, //跨库订单数据
Sys_QHData, //期货业务
WeiXin_OfflineResTypeIds, //线下资源类型id
Sys_IsShowMobileOfContent, //是否显示内容中的手机号
WeiXin_IsShowOpenOrderTip, //是否显示我的微信客户开通订单提示
Sys_SaleDeptId_Three, //三部的SaleDeptId
Sys_SaleDeptId_OneAndTwo, //一二部的SaleDeptId
Sys_rpt_json_config,//业绩预测配置
UserCenter_RiaService_RefundContract, //退款合同
WeiXin_IllegalKewordsDeptConfig,//合规关键词推送deptcode
UserCenter_UserEnter,//用户中心注册接口
UserCenter_HandelLabel,//用户中心写标签接口
HG_Level,//合规类型等级
AI_CallTaskConfig //回访机器人配置
}
public enum ParameterGroup
{
ORD_MemoStatistics,
FavoritesCustType,
SystemConfig,//系统配置
ExternalInterfaceAddress,
SMS_CONFIG,
OrderPayType
}
#endregion
}

View File

@ -0,0 +1,177 @@
namespace Zxd.Domain.Config
{
/// <summary>
/// 系统参数
/// </summary>
public class SystemConfig
{
public List<App> Apps { get; set; }
public string Appid { get; set; }
public string AppSecret { get; set; }
public string SsoUrl { get; set; }
public string ZxdCroeUrl { get; set; }
public string SsoOrganizationUrl { get; set; }
/// <summary>
/// 销售线索URL
/// </summary>
public string? SalesLeadUrl { get; set; }
/// <summary>
/// CRM号码加密key(clientKey)
/// </summary>
public string? CRMClientKey { get; set; }
/// <summary>
/// 推送部门编码
/// </summary>
public string? DataClientCode { get; set; }
public string WeworkSendUrl { get; set; }
/// <summary>
/// 深海捷固定坐席
/// </summary>
public string? Shj { get; set; }
/// <summary>
/// 客户端密钥
/// </summary>
public List<ClientKey>? ClientKey { get; set; }
/// <summary>
///
/// </summary>
public string DataSyncApiUrl { get; set; }
/// <summary>
/// 短信注册信息
/// </summary>
public string SoftRegisterMsg { get; set; }
public string SsoTokenUrl { get; set; }
public string CrmBaseUrl { get; set; }
public string AddStandardProduct { get; set; }
public string AddStandardPackage { get; set; }
public string AddVirtualProduct { get; set; }
public string AddVirtualPackage { get; set; }
public string ActiveProduct { get; set; }
public string ActivePackage { get; set; }
public string UserGroupList { get; set; }
public string GroupUrl { get; set; }
/// <summary>
/// 数据中台
/// </summary>
public string BdMarkting { get; set; }
public string KfUserCount { get; set; }
public string UsercenterUrl { get; set; }
public string GetAddStandardProduct()
{
return $"{CrmBaseUrl}{AddStandardProduct}";
}
public string GetActiveProduct()
{
return $"{CrmBaseUrl}{ActiveProduct}";
}
public string GetUserGroupList()
{
return $"{GroupUrl}{UserGroupList}";
}
public string GetKFSourceCount()
{
return $"{BdMarkting}{KfUserCount}";
}
public string GetActivePackage()
{
return $"{CrmBaseUrl}{ActivePackage}";
}
public string GetAddStandardPackage()
{
return $"{CrmBaseUrl}{AddStandardPackage}";
}
public string GetAddVirtualProduct()
{
return $"{CrmBaseUrl}{AddVirtualProduct}";
}
public string GetAddVirtualPackage()
{
return $"{CrmBaseUrl}{AddVirtualPackage}";
}
public string CrmCoreUrl { get; set; }
public string GetSsoOrganizationUrl()
{
return $"{SsoUrl}{SsoOrganizationUrl}";
}
public string GetZxdCroeUrl() {
return $"{ZxdCroeUrl}/Api/Deptment/Depts";
}
public string GetSsoTokenUrl()
{
return $"{SsoUrl}{SsoTokenUrl}";
}
/// <summary>
/// 资源系统ip地址
/// </summary>
public string CmsUrl { get; set; }
public string[] ClearCacheUrls { get; set; }
public string GetAccessKey(string id)
{
return ClientKey?.First(x => x.Id == id).AccessKey ?? "";
}
public string GetUserList()
{
return $"{UsercenterUrl}usercenter/cgi/getUserList";
}
}
public class App
{
public List<decimal> Deptids { get; set; }
public string AppName { get; set; }
public string Appid { get; set; }
public string AppSecret { get; set; }
public string CrmUrl { get; set; }
}
public class PayCallbackConfig
{
public string AppId { get; set; }
public string Secret { get; set; }
public string PushUrl { get; set; }
}
}

View File

@ -0,0 +1,338 @@
using MySqlConnector;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Zxd.Core.Domain.Dto.Wework;
using Zxd.EntityFramework;
using static Zxd.Domain.Impl.ICustomerDomain;
namespace Zxd.Domain
{
public class CustomerDomain : ICustomerDomain
{
private readonly IConfiguration _configuration;
private readonly IHttpClient _httpClient;
private readonly ICacheDomain _cacheDomain;
private readonly IBaseRepository<ZxdDbContext> _repository;
private readonly IBaseRepository<DncmsDbContext> _dncmsbaseRepository;
public CustomerDomain(IConfiguration configuration,
IHttpClient httpClient,
ICacheDomain cacheDomain,
IBaseRepository<DncmsDbContext> dncmsbaseRepository,
IBaseRepository<ZxdDbContext> repository)
{
_configuration = configuration;
_httpClient = httpClient;
_cacheDomain = cacheDomain;
_repository = repository;
_dncmsbaseRepository = dncmsbaseRepository;
}
/// <summary>
/// 添加标签数据
/// </summary>
/// <param name="resid"></param>
/// <param name="tag"></param>
/// <returns></returns>
/// <exception cref="ApiException"></exception>
public async Task<bool> AddTag(string resid, string tag,int ceid)
{
tag = tag.Trim();
resid = resid.Trim();
if (string.IsNullOrEmpty(tag))
throw new ApiException("参数错误");
if (string.IsNullOrEmpty(resid))
throw new ApiException("参数错误");
var info = await _repository.GetRepository<Res_Tag>().Query().Where(m => m.resid == resid && m.tag == tag && m.isdelete==0).FirstOrDefaultAsync();
if (info != null)
throw new ApiException("已存在此标签");
await _repository.GetRepository<Res_Tag>().InsertAsync(new Res_Tag()
{
resid = resid,
tag = tag,
ceid = ceid
});
return true;
}
public async Task<bool> DelTag(int id,int deleteeid)
{
var info = await _repository.GetRepository<Res_Tag>().Query().Where(m => m.id == id).FirstOrDefaultAsync();
if (info == null)
throw new ApiException("找不到这个标签");
info.deleteeid = deleteeid;
info.isdelete = 1;
await _repository.GetRepository<Res_Tag>().UpdateAsync(info);
return true;
}
/// <summary>
/// 获取标签列表
/// </summary>
/// <param name="resid"></param>
/// <returns></returns>
public async Task<List<Res_Tag>> GetTag(string resid)
{
resid = resid.Trim();
return await _repository.GetRepository<Res_Tag>().Query().Where(m => m.resid == resid && m.isdelete==0).ToListAsync();
}
public async Task<List<string>> GetUsernames(string? resid)
{
if (string.IsNullOrEmpty(resid)) return new List<string>();
var usernames = await _repository.GetRepository<RES_CUSTOMERUSER>().Query()
.Where(x => x.RESID == resid)
.Select(x => x.USERNAME)
.ToListAsync();
return usernames;
}
public async Task<List<userRet>> GetUsernamesByOrderid(string orderidListIn)
{
if (orderidListIn == null) return new List<userRet> { };
var orderidList = orderidListIn.Split(";").ToList();
if (orderidList.Count == 0) return new List<userRet>();
var usernamesList = await _repository.GetRepository<WX_SZZYORDER>().Query()
.Where(x => orderidList.Contains(x.ORDERID.ToString()))
.Select(x => new userRet { orderid = x.ORDERID.ToString(), softwarename = x.SOFTUSERNAME })
.ToListAsync();
var res = new List<userRet>();
orderidList.ForEach(orderid =>
{
var findOder = usernamesList.Find(u => u.orderid == orderid);
if (findOder != null)
{
res.Add(findOder);
}
});
return res;
}
public async Task<CreateCustomerRes> CreateCustomer(string MOBILE, string ResId, string CustomerFrom)
{
CreateCustomerRes res = new CreateCustomerRes();
var customer = await _repository.GetRepository<RES_CUSTOMER>().Query().FirstOrDefaultAsync(x => x.RESID == ResId);
if (customer == null)
{
//开始创建
var mobile = MOBILE.Replace("+86", "");
if (mobile.StartsWith("01") && mobile.Length == 12)
{
mobile = mobile.Substring(1);
}
var systemConfig = _configuration.GetSection("SystemConfig").Get<SystemConfig>();
var key = systemConfig.GetAccessKey(systemConfig.CRMClientKey);
var CNumber = SecurityHelper.EncyptData(mobile, key);
var param = new List<MySqlParameter>
{
new MySqlParameter() { ParameterName = "p_CNumber", DbType = DbType.String, Value =CNumber},
new MySqlParameter() { ParameterName = "p_ResId", DbType = DbType.String, Value = ResId },
new MySqlParameter() { ParameterName = "p_CustomerFrom", DbType = DbType.String, Value = CustomerFrom }
};
param.Add(!string.IsNullOrEmpty(mobile) ? new MySqlParameter() { ParameterName = "p_ECNumber", DbType = DbType.String, Value = NumberFormat(mobile) } : new MySqlParameter() { ParameterName = "p_ECNumber", DbType = DbType.String, Value = DBNull.Value });
var i = await _repository.ExecuteSqlCommandNonQueryAsync(System.Data.CommandType.StoredProcedure, "res_ResgisterCustomer", param.ToArray());
res.code = CCenum.;
}
else
{
res.code = CCenum.;
}
return res;
}
public async Task<List<WwUserFollowOutDto>> GetFollow(List<WwUserFollowInDto> queryList)
{
var extuseridList = queryList.Select(s => s.extuserid).ToList();
// 依据appid userid extuserid 找出表中对应的数据并根据subscribe判断是否关注
var temp = await _dncmsbaseRepository.GetRepository<WeworkExternalUser>().Query()
.Where(w => extuseridList.Contains(w.Externaluserid))
.Select(x => new WwUserFollowOutDto()
{
appid = x.Appid,
userid = x.Userid,
extuserid = x.Externaluserid,
isFollow = x.subscribe
})
.ToListAsync();
List<WwUserFollowOutDto> res = new List<WwUserFollowOutDto>();
queryList.ForEach(q =>
{
var t = temp.Find(f => f.appid == q.appid && f.userid == q.userid && f.extuserid == q.extuserid);
if (t != null)
{
res.Add(new WwUserFollowOutDto
{
appid = t.appid,
userid = t.userid,
extuserid = t.extuserid,
isFollow = t.isFollow
});
}
});
return res;
}
public async Task<List<SalesLeadDto>> GetSalesLeadList(string? resId)
{
var systemConfig = _configuration.GetSection("SystemConfig").Get<SystemConfig>();
var result = new List<SalesLeadDto>();
var url = systemConfig.SalesLeadUrl;
var key = systemConfig.GetAccessKey(systemConfig.CRMClientKey);
var reslist = await _repository.GetRepository<RES_RESOURCEMOBILE>().Query()
.Where(x => x.RESID == resId).ToListAsync();
var mobiles = new List<string>();
foreach (var res in reslist)
{
if (res == null) continue;
mobiles.Add(SecurityHelper.DecyptData(res.MOBILE, key));
}
if (mobiles == null || !mobiles.Any())
{
throw new ApiException("暂无销售线索!", -1);
}
var parameter = await _cacheDomain.GetValueParameter("ISVR_Saleclus_Get");
if (string.IsNullOrEmpty(parameter)) return result;
var response = await _httpClient.PostAsync<SaleClusResult<SaleRelation>>(parameter, new { mobiles });
if (response.Ret != 0 || response.List == null)
{
return result;
}
foreach (var saleRelation in response.List)
{
if (result.Any(x => x.Appuserid == saleRelation.Appuserid && x.Appid == saleRelation.Appid))
{
continue;
}
var name = saleRelation.Appuserid.Length > 10 ?
saleRelation.Appuserid[..10] + "*****" :
saleRelation.Appuserid;
result.Add(new SalesLeadDto
{
Name = name,
Appid = saleRelation.Appid,
Appuserid = saleRelation.Appuserid,
Url = $"{url}?appid={saleRelation.Appid}&appuserid={saleRelation.Appuserid}"
});
}
return result;
}
public string NumberFormat(string number)
{
if (string.IsNullOrEmpty(number) || number.Length <= 6)
{
return number;
}
return number.Substring(0, 3) + new string('*', number.Length - 6) + number.Substring(number.Length - 3);
}
/// <summary>
///
/// </summary>
/// <param name="resId"></param>
/// <param name="DeptCode"></param>
/// <returns></returns>
public async Task<List<ExUserModel>> GetWorkWXUserSelectAsync(string? resId, string? DeptCode)
{
if (string.IsNullOrEmpty(resId))
{
return new List<ExUserModel>();
}
var customerQuery = _repository.GetRepository<RES_CUSTOMER>().Query();
var allCustomer = (from a in customerQuery
join b in customerQuery on a.CUSTOMERID equals b.CUSTOMERID
where b.RESID == resId
select a.RESID).ToList();
var data = await _repository.GetRepository<WW_EXTUSER_RESID>().Query()
.Where(x => allCustomer.Contains(x.Resid) || x.Resid == resId)
.ToListAsync();
var result = new List<ExUserModel>();
var unameList = data.Select(n => n.Userid).Distinct().ToList();
foreach (var name in unameList)
{
ExUserModel newModel = new ExUserModel
{
Userid = name,
};
var names = data.Where(n => n.Userid == name).OrderByDescending(n => n.Ctime).ToList();
var seftItem = names.FirstOrDefault(n => n.Deptcode == DeptCode);
if (seftItem != null)
{
newModel.Ctime = seftItem.Ctime;
newModel.Deptcode = seftItem.Deptcode;
newModel.Resid = seftItem.Resid;
}
else
{
newModel.Ctime = names.FirstOrDefault().Ctime;
newModel.Deptcode = names.FirstOrDefault().Deptcode;
newModel.Resid = names.FirstOrDefault().Resid;
}
result.Add(newModel);
}
return result.OrderByDescending(n => n.Ctime).ToList();
}
public async Task<List<string>> ExtUserBandGetAsync(string? resId)
{
if (string.IsNullOrEmpty(resId))
{
return new List<string>();
}
var customerQuery = _repository.GetRepository<RES_CUSTOMER>().Query();
var allCustomer = (from a in customerQuery
join b in customerQuery on a.CUSTOMERID equals b.CUSTOMERID
where b.RESID == resId
select a.RESID).ToList();
var userList = await _repository.GetRepository<WW_EXTUSER_RESID>().Query().Where(n => allCustomer.Contains(n.Resid) || n.Resid == resId).ToListAsync();
List<string> result = new List<string>();
foreach (var item in userList)
{
result.Add(item.Userid);
}
result = result.Distinct().ToList();
return result;
}
public async Task<List<string>> GetResidByExtuser(List<string> extUser)
{
if (!extUser.Any()) return new List<string>();
var resids = await _repository.GetRepository<WW_EXTUSER_RESID>().Query()
.Where(x => extUser.Contains(x.Userid))
.Select(x => x.Userid + ":" + x.Resid)
.ToListAsync();
return resids;
}
public async Task<IList<string>> GetPhoneByUnionid(string unionid)
{
IList<string> mobiles = new List<string>();
var unionlist = await _repository.GetRepository<Soft_Userinfo_Sub>().Query()
.Where(x => x.unionid == unionid || x.appuserid == unionid).ToListAsync();
if (unionlist.Any())
{
mobiles = await _repository.GetRepository<Soft_Userinfo_Sub>().Query()
.Where(x => x.cid == unionlist.First().cid && !string.IsNullOrEmpty(x.mobile)).Select(m => m.mobile).Distinct().ToListAsync();
}
return mobiles;
}
}
public class ExtUserItem
{
public string userid { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More