commit d5bf18d324106b4cc03486348e1b45311c98938a
Author: zhuxiaojiong <645680426@qq.com>
Date: Sat Jun 28 09:51:49 2025 +0800
init
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..908fb28
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,5 @@
+# 2010
+*.txt -crlf
+
+# 2020
+*.txt text eol=lf
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9807c61
--- /dev/null
+++ b/.gitignore
@@ -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
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f41f06f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# zxd.webapi
+
diff --git a/code/.dockerignore b/code/.dockerignore
new file mode 100644
index 0000000..3729ff0
--- /dev/null
+++ b/code/.dockerignore
@@ -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
\ No newline at end of file
diff --git a/code/CommonWorker/CommonWorker.csproj b/code/CommonWorker/CommonWorker.csproj
new file mode 100644
index 0000000..1d901bd
--- /dev/null
+++ b/code/CommonWorker/CommonWorker.csproj
@@ -0,0 +1,71 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+ Linux
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
diff --git a/code/CommonWorker/Config/SystemConfig.cs b/code/CommonWorker/Config/SystemConfig.cs
new file mode 100644
index 0000000..3873acd
--- /dev/null
+++ b/code/CommonWorker/Config/SystemConfig.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/code/CommonWorker/Dockerfile b/code/CommonWorker/Dockerfile
new file mode 100644
index 0000000..45d03d4
--- /dev/null
+++ b/code/CommonWorker/Dockerfile
@@ -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"]
\ No newline at end of file
diff --git a/code/CommonWorker/Dto/ResPassTimeDto.cs b/code/CommonWorker/Dto/ResPassTimeDto.cs
new file mode 100644
index 0000000..783a96c
--- /dev/null
+++ b/code/CommonWorker/Dto/ResPassTimeDto.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/code/CommonWorker/Dto/UserModuleApiModel.cs b/code/CommonWorker/Dto/UserModuleApiModel.cs
new file mode 100644
index 0000000..69b2877
--- /dev/null
+++ b/code/CommonWorker/Dto/UserModuleApiModel.cs
@@ -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> 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; }
+ }
+}
\ No newline at end of file
diff --git a/code/CommonWorker/Program.cs b/code/CommonWorker/Program.cs
new file mode 100644
index 0000000..5999e26
--- /dev/null
+++ b/code/CommonWorker/Program.cs
@@ -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("Exceptionless:ApiKey"), config.GetValue("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(e => config.GetSection("SystemConfig").Bind(e));
+ ExceptionlessClient.Default.Startup(config.GetValue("Exceptionless:ApiKey"));
+ ExceptionlessClient.Default.Configuration.ServerUrl = config.GetValue("Exceptionless:ServerUrl");
+ //services.AddRedis(config);
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("zxdcrm"), ServerVersion.AutoDetect(config.GetConnectionString("zxdcrm")));
+ });
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("dncmsbase"), ServerVersion.AutoDetect(config.GetConnectionString("dncmsbase")));
+ });
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("usercenter"), ServerVersion.AutoDetect(config.GetConnectionString("usercenter")));
+ });
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("dncms"), ServerVersion.AutoDetect(config.GetConnectionString("dncms")));
+ });
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("crm"), ServerVersion.AutoDetect(config.GetConnectionString("crm")));
+ });
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("companyBaseConf"), ServerVersion.AutoDetect(config.GetConnectionString("companyBaseConf")));
+ });
+ services.AddKafkaWorker(config);
+ services.AddDGHttpClient();
+ services.AddRegisterWorker();
+ //构建容器
+ IServiceProvider serviceProvider = services.BuildServiceProvider();
+ var workerManager = serviceProvider.GetRequiredService();
+ await workerManager.RegisterWorker("ResPassTime");
+ await workerManager.RegisterWorker("ResPassTime");
+ await workerManager.RegisterWorker("ResPassTime");
+ await workerManager.RegisterWorker("ResPassTime");
+ await workerManager.RegisterWorker("ResPassTime");
+ var builder = new HostBuilder();
+ await builder.RunConsoleAsync();
+}
+catch (Exception ex)
+{
+ Log.Fatal(ex, "Host terminated unexpectedly");
+}
+finally
+{
+ Log.CloseAndFlush();
+}
\ No newline at end of file
diff --git a/code/CommonWorker/Properties/launchSettings.json b/code/CommonWorker/Properties/launchSettings.json
new file mode 100644
index 0000000..994e190
--- /dev/null
+++ b/code/CommonWorker/Properties/launchSettings.json
@@ -0,0 +1,16 @@
+{
+ "profiles": {
+ "CommonWorker": {
+ "commandName": "Project",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "Docker (1)": {
+ "commandName": "Docker",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/code/CommonWorker/Serilog.Production.json b/code/CommonWorker/Serilog.Production.json
new file mode 100644
index 0000000..491af8c
--- /dev/null
+++ b/code/CommonWorker/Serilog.Production.json
@@ -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" ]
+ }
+}
\ No newline at end of file
diff --git a/code/CommonWorker/Serilog.json b/code/CommonWorker/Serilog.json
new file mode 100644
index 0000000..067e7b1
--- /dev/null
+++ b/code/CommonWorker/Serilog.json
@@ -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" ]
+ }
+}
diff --git a/code/CommonWorker/Workers/CustomerPassTimeWorker.cs b/code/CommonWorker/Workers/CustomerPassTimeWorker.cs
new file mode 100644
index 0000000..f0a2e1b
--- /dev/null
+++ b/code/CommonWorker/Workers/CustomerPassTimeWorker.cs
@@ -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
+ {
+ private readonly IServiceProvider _serviceProvider;
+ private readonly ILogger _logger;
+ private readonly IHttpClient _httpClient;
+ private readonly IOptionsSnapshot _systemConfig;
+
+ public CustomerPassTimeWorker(
+ IServiceProvider serviceProvider,
+ IOptionsSnapshot systemConfig,
+ ILogger logger,
+ IHttpClient httpClient
+ ) : base(logger)
+ {
+ _serviceProvider = serviceProvider;
+ _httpClient = httpClient;
+ _logger = logger;
+ _systemConfig = systemConfig;
+ }
+
+ ///
+ /// 算出统计表
+ ///
+ ///
+ ///
+ 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>();
+ var _crmRepository = scope.ServiceProvider.GetRequiredService>();
+ //目前只支持一个resid
+ var allresid = item.Split(",").ToList();
+ //订单信息
+ allresid = allresid.Where(n => !string.IsNullOrWhiteSpace(n)).ToList();
+ var cust = await _repository.GetRepository().Query().FirstOrDefaultAsync(n => n.RESID == item || n.UMID == item);
+ allresid = await _repository.GetRepository().Query().Where(n => n.CUSTOMERID == cust.CUSTOMERID && !string.IsNullOrWhiteSpace(n.RESID))
+ .Select(n => n.RESID).Distinct().ToListAsync();
+ var customerList = await _repository.GetRepository().Query().Where(n => allresid.Contains(n.RESID)).ToListAsync();
+
+ var _orderRepository = _repository.GetRepository();
+ var orderList = await _orderRepository.Query().Where(n => allresid.Contains(n.RESID)).ToListAsync();
+ //退款信息 按实际退款
+ var refundOrderList = await _repository.GetRepository().Query()
+ .Where(n => allresid.Contains(n.resid) && n.auditstatus == 1 && n.isacturalrefund == 1).ToListAsync();
+ //到账信息
+ var orderDepositList = await _repository.GetRepository().Query()
+ .Where(n => allresid.Contains(n.resid) && n.auditstatus == 1).ToListAsync();
+ var _userRepository = _repository.GetRepository();
+ 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 passTimeList = new List();
+ 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().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().Query().FirstOrDefaultAsync(n => n.PARAKEY == "SoftBusiness");
+ //Log.Information($"获取配置{softbusiness.ToJson()}");
+ decimal limitPrice = 500; //默认500
+ if (softbusiness != null)
+ {
+ CompanyBussiness bussiness = JsonHelper.FromJson(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 { "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().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().InsertAsync(info);
+ }
+ else
+ {
+ info.utime = DateTime.Now;
+ await _crmRepository.GetRepository().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().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().BatchDeleteAsync(existItem);
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error($"{item}获取过期时间失败{ex.Message}");
+ }
+ }
+ }
+
+ private async Task> GetRootPassTime(string username)
+ {
+ List res = new List();
+ var url = $"{_systemConfig.Value.nodeWebApi.Trim('/')}/order/doGetUserPerssion";
+ var postJson = new
+ {
+ username = username
+ };
+ try
+ {
+ var data = await _httpClient.PostAsync(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;
+ }
+
+ ///
+ ///
+ /// java长整型日期,毫秒为单位
+ ///
+ 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
+ }
+ }
+}
\ No newline at end of file
diff --git a/code/CommonWorker/appsettings.Disaster.json b/code/CommonWorker/appsettings.Disaster.json
new file mode 100644
index 0000000..bfd9715
--- /dev/null
+++ b/code/CommonWorker/appsettings.Disaster.json
@@ -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"
+ }
+}
\ No newline at end of file
diff --git a/code/CommonWorker/appsettings.Production.json b/code/CommonWorker/appsettings.Production.json
new file mode 100644
index 0000000..474f4f1
--- /dev/null
+++ b/code/CommonWorker/appsettings.Production.json
@@ -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/"
+ }
+}
\ No newline at end of file
diff --git a/code/CommonWorker/appsettings.json b/code/CommonWorker/appsettings.json
new file mode 100644
index 0000000..d485f91
--- /dev/null
+++ b/code/CommonWorker/appsettings.json
@@ -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"
+ }
+}
\ No newline at end of file
diff --git a/code/DG.Kafka.Worker/BatchKafkaWorkerBase.cs b/code/DG.Kafka.Worker/BatchKafkaWorkerBase.cs
new file mode 100644
index 0000000..f8a43f5
--- /dev/null
+++ b/code/DG.Kafka.Worker/BatchKafkaWorkerBase.cs
@@ -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
+ {
+ private readonly ILogger> _logger;
+ private TaskConfig _taskConfig;
+ private int _milliSecondsDelay;
+
+ public BatchKafkaWorkerBase(ILogger> logger)
+ {
+ _logger = logger;
+ _taskConfig = KafkaClient.Default.ConfigurationManager.GetSection("TaskConfig").Get();
+ if (_taskConfig == null)
+ {
+ throw new ArgumentNullException(nameof(_taskConfig));
+ }
+ _milliSecondsDelay = _taskConfig.MilliSecondsDelay;
+ }
+
+ public async Task Start(List 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)
+ {
+ }
+
+ public virtual async Task ShopAsync()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/code/DG.Kafka.Worker/DG.Kafka.Worker.csproj b/code/DG.Kafka.Worker/DG.Kafka.Worker.csproj
new file mode 100644
index 0000000..556f314
--- /dev/null
+++ b/code/DG.Kafka.Worker/DG.Kafka.Worker.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net6.0
+ enable
+ enable
+ True
+ 1.0.21
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/DG.Kafka.Worker/IKafkaWorkerManager.cs b/code/DG.Kafka.Worker/IKafkaWorkerManager.cs
new file mode 100644
index 0000000..189ebd1
--- /dev/null
+++ b/code/DG.Kafka.Worker/IKafkaWorkerManager.cs
@@ -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(string? topic) where TWorker : KafkaWorkerBase;
+
+ Task RegisterBatchWorker(string? topic, int batchsize = 1000) where TWorker : BatchKafkaWorkerBase;
+ }
+}
diff --git a/code/DG.Kafka.Worker/IWorkerManager.cs b/code/DG.Kafka.Worker/IWorkerManager.cs
new file mode 100644
index 0000000..0b3ad7a
--- /dev/null
+++ b/code/DG.Kafka.Worker/IWorkerManager.cs
@@ -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 doWork);
+ }
+}
diff --git a/code/DG.Kafka.Worker/KafkaWorkerBase.cs b/code/DG.Kafka.Worker/KafkaWorkerBase.cs
new file mode 100644
index 0000000..c96dce7
--- /dev/null
+++ b/code/DG.Kafka.Worker/KafkaWorkerBase.cs
@@ -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
+ {
+ private readonly ILogger> _logger;
+ private TaskConfig _taskConfig;
+ private int _milliSecondsDelay;
+ public KafkaWorkerBase(ILogger> logger)
+ {
+ _logger = logger;
+ _taskConfig = KafkaClient.Default.ConfigurationManager.GetSection("TaskConfig").Get();
+ 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()
+ {
+
+ }
+ }
+}
diff --git a/code/DG.Kafka.Worker/KafkaWorkerManager.cs b/code/DG.Kafka.Worker/KafkaWorkerManager.cs
new file mode 100644
index 0000000..4e3af15
--- /dev/null
+++ b/code/DG.Kafka.Worker/KafkaWorkerManager.cs
@@ -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 _consumers;
+ private readonly ILogger _logger;
+ private readonly IServiceProvider _serviceProvider;
+
+ public KafkaWorkerManager(ILogger logger, IServiceProvider serviceProvider)
+ {
+ _logger = logger;
+ // 读取任务配置
+ _taskConfig = KafkaClient.Default.ConfigurationManager.GetSection("TaskConfig").Get();
+ _consumers = KafkaClient.Default.ConfigurationManager.GetSection("Consumers").Get>();
+ if (_taskConfig == null)
+ {
+ throw new ArgumentNullException(nameof(_taskConfig));
+ }
+ if (_consumers == null)
+ {
+ throw new ArgumentNullException(nameof(_consumers));
+ }
+ _serviceProvider = serviceProvider;
+ }
+
+ public async Task RegisterWorker(string? topic)
+ where TWorker : KafkaWorkerBase
+ {
+ var t = _serviceProvider.GetRequiredService();
+ _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(consumer, t.Start, t.ShopAsync);
+ }
+
+ public async Task RegisterBatchWorker(string? topic, int batchsize = 1000)
+ where TWorker : BatchKafkaWorkerBase
+ {
+ var t = _serviceProvider.GetRequiredService();
+ _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(consumer, t.Start, t.ShopAsync, batchsize);
+ }
+ }
+}
diff --git a/code/DG.Kafka.Worker/ServiceCollectionExtensions.cs b/code/DG.Kafka.Worker/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000..7bf8cea
--- /dev/null
+++ b/code/DG.Kafka.Worker/ServiceCollectionExtensions.cs
@@ -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
+{
+ ///
+ /// Extensions method
+ ///
+ public static class ServiceCollectionExtensions
+ {
+
+ ///
+ /// Redis service registered
+ ///
+ ///
+ ///
+ ///
+ public static IServiceCollection AddKafkaWorker(this IServiceCollection services, IConfiguration configuration)
+ {
+ services.AddKafka(configuration);
+ services.AddSingleton();
+ services.AddSingleton();
+ return services;
+ }
+
+ public static IServiceCollection AddRegisterWorker(this IServiceCollection services) where TWorker : KafkaWorkerBase
+ {
+ services.AddSingleton();
+ return services;
+ }
+
+ public static IServiceCollection AddRegisterBatchWorker(this IServiceCollection services) where TWorker : BatchKafkaWorkerBase
+ {
+ services.AddSingleton();
+ return services;
+ }
+ }
+}
diff --git a/code/DG.Kafka.Worker/TaskConfig.cs b/code/DG.Kafka.Worker/TaskConfig.cs
new file mode 100644
index 0000000..6d9113e
--- /dev/null
+++ b/code/DG.Kafka.Worker/TaskConfig.cs
@@ -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
+ {
+ ///
+ /// 任务名称
+ ///
+ public string? TaskName { get; set; }
+
+ ///
+ /// 任务备注
+ ///
+ public string? TaskRemarks { get; set; }
+
+ ///
+ /// 是否启用
+ ///
+ public bool Enable { get; set; }
+
+ ///
+ /// 停止毫秒数
+ ///
+ public int MilliSecondsDelay { get; set; }
+ }
+}
diff --git a/code/DG.Kafka.Worker/WorkerManager.cs b/code/DG.Kafka.Worker/WorkerManager.cs
new file mode 100644
index 0000000..28e07bf
--- /dev/null
+++ b/code/DG.Kafka.Worker/WorkerManager.cs
@@ -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 _logger;
+
+ public WorkerManager(ILogger logger)
+ {
+ _logger = logger;
+ // 读取任务配置
+ _taskConfig = KafkaClient.Default.ConfigurationManager.GetSection("TaskConfig").Get();
+ 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 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);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/code/DG.Kafka/Consumer.cs b/code/DG.Kafka/Consumer.cs
new file mode 100644
index 0000000..a87ff7e
--- /dev/null
+++ b/code/DG.Kafka/Consumer.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/code/DG.Kafka/DG.Kafka.csproj b/code/DG.Kafka/DG.Kafka.csproj
new file mode 100644
index 0000000..adde5e4
--- /dev/null
+++ b/code/DG.Kafka/DG.Kafka.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net6.0
+ enable
+ enable
+ True
+ 1.0.18
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/DG.Kafka/IKafkaProducer.cs b/code/DG.Kafka/IKafkaProducer.cs
new file mode 100644
index 0000000..3f9bf34
--- /dev/null
+++ b/code/DG.Kafka/IKafkaProducer.cs
@@ -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(string? topic, TMessage message);
+ }
+}
diff --git a/code/DG.Kafka/KafkaClient.cs b/code/DG.Kafka/KafkaClient.cs
new file mode 100644
index 0000000..b404f8f
--- /dev/null
+++ b/code/DG.Kafka/KafkaClient.cs
@@ -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
+ {
+ ///
+ /// 是否停止服务
+ ///
+ public static bool Shop { get; set; } = false;
+
+ public void ReadFromConfiguration(IConfiguration configuration)
+ {
+ ConfigurationManager = configuration;
+ }
+
+ public IConfiguration ConfigurationManager { get; private set; }
+
+ private static readonly Lazy _defaultClient = new(() => new KafkaClient());
+
+ public static KafkaClient Default
+ {
+ get { return _defaultClient.Value; }
+ }
+
+
+ private static List _consumers { get; set; } = new List();
+
+ public static List GetConsumers()
+ {
+ _consumers = Default.ConfigurationManager.GetSection("Consumers").Get>();
+ return _consumers;
+ }
+
+ public static async Task Builder(Consumer consumer, Func received, Func shoped)
+ {
+ await Task.Run(() =>
+ {
+ var tasks = new List();
+ //var receivedDelegate = new ReceivedDelegate(ReceivedAsync);
+ ThreadPool.QueueUserWorkItem(async task =>
+ {
+ await ReceivedAsync(consumer, received, shoped);
+ });
+ });
+ }
+
+ public static async Task BatchBuilder(Consumer consumer, Func, Task> received, Func shoped, int batchsize = 1000)
+ {
+ await Task.Run(() =>
+ {
+ var tasks = new List();
+ //var receivedDelegate = new BatchReceivedDelegate(BatchReceivedAsync);
+ ThreadPool.QueueUserWorkItem(async task =>
+ {
+ await BatchReceivedAsync(consumer, received, shoped, batchsize);
+ });
+ });
+ }
+
+ public delegate Task ReceivedDelegate(Consumer consumer, Func received);
+
+ public delegate Task BatchReceivedDelegate(Consumer consumer, Func, Task> received, int batchsize = 1000);
+
+ public static async Task ReceivedAsync(Consumer consumer, Func received, Func 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(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(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(Consumer consumer, Func, Task> received, Func 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(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();
+ var consumeResults = new List>();
+ 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(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(Consumer consumer, TMessage message)
+ {
+ var config = new ProducerConfig
+ {
+ BootstrapServers = consumer.Host,
+ };
+ var topic = consumer.Topic;
+ Action> handler = r =>
+ Console.WriteLine(!r.Error.IsError
+ ? $"Delivered message to {r.TopicPartitionOffset}"
+ : $"Delivery Error: {r.Error.Reason}");
+
+ using (var p = new ProducerBuilder(config).Build())
+ {
+ try
+ {
+ p.Produce(topic, new Message { Value = JsonSerializer.Serialize(message) });
+ p.Flush(TimeSpan.FromSeconds(10));
+
+ }
+ catch (ProduceException e)
+ {
+ Console.WriteLine($"Delivery failed: {e.Error.Reason}");
+ }
+ }
+ }
+ }
+}
diff --git a/code/DG.Kafka/KafkaProducer.cs b/code/DG.Kafka/KafkaProducer.cs
new file mode 100644
index 0000000..9de566d
--- /dev/null
+++ b/code/DG.Kafka/KafkaProducer.cs
@@ -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 _consumers { get; set; } = new List();
+ private Dictionary> _producers;
+ public KafkaProducer()
+ {
+ _consumers = KafkaClient.Default.ConfigurationManager.GetSection("Consumers").Get>();
+ _producers = new Dictionary>();
+ foreach (var consumer in _consumers)
+ {
+ var config = new ProducerConfig
+ {
+ BootstrapServers = consumer.Host,
+ };
+ var topic = consumer.Topic;
+ Action> handler = r =>
+ Console.WriteLine(!r.Error.IsError
+ ? $"Delivered message to {r.TopicPartitionOffset}"
+ : $"Delivery Error: {r.Error.Reason}");
+ _producers.Add(consumer.Topic, new ProducerBuilder(config).Build());
+ }
+ }
+
+ public async Task ProduceAsync(string? topic, TMessage message)
+ {
+ var producer = _producers.First().Value;
+ if (!string.IsNullOrEmpty(topic))
+ {
+ producer = _producers.GetValueOrDefault(topic);
+ }
+ await producer.ProduceAsync(topic, new Message { Value = JsonSerializer.Serialize(message) });
+ producer.Flush();
+ }
+ }
+}
diff --git a/code/DG.Kafka/ServiceCollectionExtensions.cs b/code/DG.Kafka/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000..0331370
--- /dev/null
+++ b/code/DG.Kafka/ServiceCollectionExtensions.cs
@@ -0,0 +1,25 @@
+using DG.Kafka;
+using Microsoft.Extensions.Configuration;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ ///
+ /// Extensions method
+ ///
+ public static class ServiceCollectionExtensions
+ {
+
+ ///
+ /// Redis service registered
+ ///
+ ///
+ ///
+ ///
+ public static IServiceCollection AddKafka(this IServiceCollection services, IConfiguration configuration)
+ {
+ KafkaClient.Default.ReadFromConfiguration(configuration);
+ services = services.AddSingleton();
+ return services;
+ }
+ }
+}
diff --git a/code/EmployeeDepartmentDetailWorker/Config/SystemConfig.cs b/code/EmployeeDepartmentDetailWorker/Config/SystemConfig.cs
new file mode 100644
index 0000000..77b556c
--- /dev/null
+++ b/code/EmployeeDepartmentDetailWorker/Config/SystemConfig.cs
@@ -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; }
+ }
+}
diff --git a/code/EmployeeDepartmentDetailWorker/EmployeeDepartmentDetailServices.csproj b/code/EmployeeDepartmentDetailWorker/EmployeeDepartmentDetailServices.csproj
new file mode 100644
index 0000000..913015f
--- /dev/null
+++ b/code/EmployeeDepartmentDetailWorker/EmployeeDepartmentDetailServices.csproj
@@ -0,0 +1,57 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/code/EmployeeDepartmentDetailWorker/Program.cs b/code/EmployeeDepartmentDetailWorker/Program.cs
new file mode 100644
index 0000000..f6eedd3
--- /dev/null
+++ b/code/EmployeeDepartmentDetailWorker/Program.cs
@@ -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("Exceptionless:ApiKey"), config.GetValue("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(config);
+ services.AddOptions()
+ .Configure(e => config.GetSection("SystemConfig").Bind(e));
+ ExceptionlessClient.Default.Startup(config.GetValue("Exceptionless:ApiKey"));
+ ExceptionlessClient.Default.Configuration.ServerUrl = config.GetValue("Exceptionless:ServerUrl");
+ ExceptionlessClient.Default.Configuration.DefaultTags.Add("zxd-ResourceFlowWorker");
+ //services.AddRedis(config);
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("zxdcrm"), ServerVersion.AutoDetect(config.GetConnectionString("zxdcrm")));
+ });
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("dncmsbase"), ServerVersion.AutoDetect(config.GetConnectionString("dncmsbase")));
+ });
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("usercenter"), ServerVersion.AutoDetect(config.GetConnectionString("usercenter")));
+ });
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("dncms"), ServerVersion.AutoDetect(config.GetConnectionString("dncms")));
+ });
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("companyBaseConf"), ServerVersion.AutoDetect(config.GetConnectionString("companyBaseConf")));
+ });
+ //services.AddDGEntityFramework(options =>
+ //{
+ // options.UseMySql(config.GetConnectionString("hgaction"), ServerVersion.AutoDetect(config.GetConnectionString("hgaction")));
+ //});
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("crmcloud"), ServerVersion.AutoDetect(config.GetConnectionString("crmcloud")));
+ });
+
+ services.AddWorker(config);
+ services.AddDGHttpClient();
+ services.AddRegisterWorker();
+ var builder = new HostBuilder();
+ await builder.RunConsoleAsync();
+
+}
+catch (Exception ex)
+{
+ Log.Fatal(ex, "Host terminated unexpectedly");
+}
+finally
+{
+ Log.CloseAndFlush();
+}
diff --git a/code/EmployeeDepartmentDetailWorker/Properties/launchSettings.json b/code/EmployeeDepartmentDetailWorker/Properties/launchSettings.json
new file mode 100644
index 0000000..38ab25f
--- /dev/null
+++ b/code/EmployeeDepartmentDetailWorker/Properties/launchSettings.json
@@ -0,0 +1,16 @@
+{
+ "profiles": {
+ "WeworkUserWorker": {
+ "commandName": "Project",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "Docker": {
+ "commandName": "Docker",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/code/EmployeeDepartmentDetailWorker/Serilog.PreProduction.json b/code/EmployeeDepartmentDetailWorker/Serilog.PreProduction.json
new file mode 100644
index 0000000..067e7b1
--- /dev/null
+++ b/code/EmployeeDepartmentDetailWorker/Serilog.PreProduction.json
@@ -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" ]
+ }
+}
diff --git a/code/EmployeeDepartmentDetailWorker/Serilog.Production.json b/code/EmployeeDepartmentDetailWorker/Serilog.Production.json
new file mode 100644
index 0000000..067e7b1
--- /dev/null
+++ b/code/EmployeeDepartmentDetailWorker/Serilog.Production.json
@@ -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" ]
+ }
+}
diff --git a/code/EmployeeDepartmentDetailWorker/Serilog.json b/code/EmployeeDepartmentDetailWorker/Serilog.json
new file mode 100644
index 0000000..067e7b1
--- /dev/null
+++ b/code/EmployeeDepartmentDetailWorker/Serilog.json
@@ -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" ]
+ }
+}
diff --git a/code/EmployeeDepartmentDetailWorker/Worker/SynchronousWorker.cs b/code/EmployeeDepartmentDetailWorker/Worker/SynchronousWorker.cs
new file mode 100644
index 0000000..9e4dcb3
--- /dev/null
+++ b/code/EmployeeDepartmentDetailWorker/Worker/SynchronousWorker.cs
@@ -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 _logger;
+ private readonly SystemConfig? _systemConfig;
+ private static bool IsFrist = true;
+
+ public SynchronousWorker(ILogger logger,
+ IServiceProvider serviceProvider,
+ IConfiguration configuration,
+ IHttpClient httpClient
+ ) : base(logger)
+ {
+ _logger = logger;
+ _configuration = configuration;
+ _serviceProvider = serviceProvider;
+ _systemConfig = configuration.GetSection("SystemConfig").Get();
+ _httpClient = httpClient;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ protected override async Task DoWorkAsync()
+ {
+ try
+ {
+ List changeEdd = new List();
+ List changeEd = new List();
+ List changeDepart = new List();
+
+ using var scope = _serviceProvider.CreateAsyncScope();
+ var companyBaseConfRepository = scope.ServiceProvider.GetRequiredService>();
+ var zxdRepository = scope.ServiceProvider.GetRequiredService>();
+ var crmCloudRepository = scope.ServiceProvider.GetRequiredService>();
+ var dncmsbaseRepository = scope.ServiceProvider.GetRequiredService>();
+
+ //从员工系统同步数据
+ var employeedepartmentdetail = companyBaseConfRepository.GetRepository().Query();
+ var applicationpartment = companyBaseConfRepository.GetRepository().Query();
+ var application = companyBaseConfRepository.GetRepository().Query();
+ var department = companyBaseConfRepository.GetRepository().Query();
+
+ var employ = companyBaseConfRepository.GetRepository().Query();
+ var deptment = await _httpClient.GetAsync>>($"{_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().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().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().Query().ToList();
+
+ List deleteEdf = new List();
+ List insertEdf = new List();
+ List allEdf = new List();
+ var edList = await companyBaseConfRepository.ExecuteSqlToListAsync(@"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().BatchUpdateAsync(updateEdd);
+ await zxdRepository.GetRepository().BatchInsertAsync(insertEdd);
+
+ await zxdRepository.GetRepository().BatchDeleteAsync(deleteEdf);
+ await zxdRepository.GetRepository().BatchInsertAsync(insertEdf);
+
+ await zxdRepository.GetRepository().BatchUpdateAsync(updateDepart);
+ await zxdRepository.GetRepository().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().BatchUpdateAsync(Mapping(updateEdd));
+ await crmCloudRepository.GetRepository().BatchInsertAsync(Mapping(insertEdd));
+
+ await crmCloudRepository.GetRepository().BatchDeleteAsync(Mapping(deleteEdf));
+ await crmCloudRepository.GetRepository().BatchInsertAsync(Mapping(insertEdf));
+
+ await crmCloudRepository.GetRepository().BatchUpdateAsync(Mapping(updateDepart));
+ await crmCloudRepository.GetRepository().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().BatchUpdateAsync(MappingToDncms(updateEdd));
+ await dncmsbaseRepository.GetRepository().BatchInsertAsync(MappingToDncms(insertEdd));
+
+ await dncmsbaseRepository.GetRepository().BatchDeleteAsync(MappingToDncms(deleteEdf));
+ await dncmsbaseRepository.GetRepository().BatchInsertAsync(MappingToDncms(insertEdf));
+
+ await dncmsbaseRepository.GetRepository().BatchUpdateAsync(MappingToDncms(updateDepart));
+ await dncmsbaseRepository.GetRepository().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>(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>(url, new { eddList = list }, dept.Appid);//
+ // pindex++;
+ // }
+ // }
+ // }
+ // }
+ // catch (Exception ex)
+ // {
+ // Log.Error(ex, "推送失败!");
+ // continue;
+ // }
+ //}
+
+ #endregion 推送到坐席
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "同步失败!");
+ }
+ }
+
+ private List Mapping(List list)
+ {
+ List res = new List();
+ 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 Mapping(List list)
+ {
+ List res = new List();
+ 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 Mapping(List list)
+ {
+ List res = new List();
+ 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 MappingToDncms(List list)
+ {
+ List res = new List();
+ 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 MappingToDncms(List list)
+ {
+ List res = new List();
+ 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 MappingToDncms(List list)
+ {
+ List res = new List();
+ 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>();
+ var configs = dncmsRepository.GetRepository().Query()
+ .Where(x => x.Status == ResourceConfigStatus.执行中).ToList();
+
+ configs.ForEach(x => x.Status = ResourceConfigStatus.待重启);
+
+ dncmsRepository.GetRepository().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; }
+ }
+}
\ No newline at end of file
diff --git a/code/EmployeeDepartmentDetailWorker/appsettings.PreProduction.json b/code/EmployeeDepartmentDetailWorker/appsettings.PreProduction.json
new file mode 100644
index 0000000..55451f5
--- /dev/null
+++ b/code/EmployeeDepartmentDetailWorker/appsettings.PreProduction.json
@@ -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"
+ }
+}
\ No newline at end of file
diff --git a/code/EmployeeDepartmentDetailWorker/appsettings.Production.json b/code/EmployeeDepartmentDetailWorker/appsettings.Production.json
new file mode 100644
index 0000000..a73cb02
--- /dev/null
+++ b/code/EmployeeDepartmentDetailWorker/appsettings.Production.json
@@ -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"
+ }
+}
\ No newline at end of file
diff --git a/code/EmployeeDepartmentDetailWorker/appsettings.json b/code/EmployeeDepartmentDetailWorker/appsettings.json
new file mode 100644
index 0000000..71f9ad6
--- /dev/null
+++ b/code/EmployeeDepartmentDetailWorker/appsettings.json
@@ -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"
+ }
+}
\ No newline at end of file
diff --git a/code/NuGet.Config b/code/NuGet.Config
new file mode 100644
index 0000000..3f0e003
--- /dev/null
+++ b/code/NuGet.Config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/code/ResourceFlowWorker/Config/Properties/launchSettings.json b/code/ResourceFlowWorker/Config/Properties/launchSettings.json
new file mode 100644
index 0000000..c5c9812
--- /dev/null
+++ b/code/ResourceFlowWorker/Config/Properties/launchSettings.json
@@ -0,0 +1,16 @@
+{
+ "profiles": {
+ "ResourceFlowWorker": {
+ "commandName": "Project",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "Docker": {
+ "commandName": "Docker",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/code/ResourceFlowWorker/Config/SystemConfig.cs b/code/ResourceFlowWorker/Config/SystemConfig.cs
new file mode 100644
index 0000000..133fffc
--- /dev/null
+++ b/code/ResourceFlowWorker/Config/SystemConfig.cs
@@ -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; }
+ }
+}
diff --git a/code/ResourceFlowWorker/Dockerfile b/code/ResourceFlowWorker/Dockerfile
new file mode 100644
index 0000000..6ab0c11
--- /dev/null
+++ b/code/ResourceFlowWorker/Dockerfile
@@ -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"]
\ No newline at end of file
diff --git a/code/ResourceFlowWorker/Dockerfile.original b/code/ResourceFlowWorker/Dockerfile.original
new file mode 100644
index 0000000..90a3762
--- /dev/null
+++ b/code/ResourceFlowWorker/Dockerfile.original
@@ -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"]
\ No newline at end of file
diff --git a/code/ResourceFlowWorker/Dto/LivePageDto.cs b/code/ResourceFlowWorker/Dto/LivePageDto.cs
new file mode 100644
index 0000000..5acd711
--- /dev/null
+++ b/code/ResourceFlowWorker/Dto/LivePageDto.cs
@@ -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
+ {
+ ///
+ /// 当前页码
+ ///
+ [JsonPropertyName("currentPage")]
+ public int? CurrentPage { get; set; }
+
+ ///
+ /// 每页记录数
+ ///
+ [JsonPropertyName("pageSize")]
+ public int? PageSize { get; set; }
+
+ ///
+ /// 列表数据
+ ///
+ [JsonPropertyName("tableData")]
+ public List TableData { get; set; }
+
+ ///
+ /// 总记录数
+ ///
+ [JsonPropertyName("total")]
+ public long Total { get; set; }
+ }
+}
diff --git a/code/ResourceFlowWorker/Dto/ResourceFlowCustomerAndUserDto.cs b/code/ResourceFlowWorker/Dto/ResourceFlowCustomerAndUserDto.cs
new file mode 100644
index 0000000..0322d9b
--- /dev/null
+++ b/code/ResourceFlowWorker/Dto/ResourceFlowCustomerAndUserDto.cs
@@ -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; }
+
+ }
+}
diff --git a/code/ResourceFlowWorker/Dto/ResourceFlowWorkerDto.cs b/code/ResourceFlowWorker/Dto/ResourceFlowWorkerDto.cs
new file mode 100644
index 0000000..1a323c9
--- /dev/null
+++ b/code/ResourceFlowWorker/Dto/ResourceFlowWorkerDto.cs
@@ -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 sourceResources)
+ {
+ Config = config;
+ From = from;
+ SourceResources = sourceResources;
+ OperateResources = JsonHelper.FromJson>(sourceResources.ToJson());
+ AssignedCount = 0;
+ }
+
+ public ResourceFlowConfig Config { get; set; }
+
+ public ResourceFlowConfigFrom From { get; set; }
+
+ ///
+ /// 源人群包信息
+ ///
+ public List SourceResources { get; set; }
+
+ ///
+ /// 操作人群包信息
+ ///
+ public List OperateResources { get; set; }
+
+ ///
+ /// 已分配数量
+ ///
+ public int AssignedCount { get; set; }
+
+ ///
+ /// 权重
+ ///
+ 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; }
+ }
+}
diff --git a/code/ResourceFlowWorker/Program.cs b/code/ResourceFlowWorker/Program.cs
new file mode 100644
index 0000000..0ac9a50
--- /dev/null
+++ b/code/ResourceFlowWorker/Program.cs
@@ -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("Exceptionless:ApiKey"), config.GetValue("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(config);
+ services.AddOptions()
+ .Configure(e => config.GetSection("SystemConfig").Bind(e));
+ ExceptionlessClient.Default.Startup(config.GetValue("Exceptionless:ApiKey"));
+ ExceptionlessClient.Default.Configuration.ServerUrl = config.GetValue("Exceptionless:ServerUrl");
+ ExceptionlessClient.Default.Configuration.DefaultTags.Add("zxd-ResourceFlowWorker");
+ //services.AddRedis(config);
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("zxdcrm"), ServerVersion.AutoDetect(config.GetConnectionString("zxdcrm")));
+ });
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("dncmsbase"), ServerVersion.AutoDetect(config.GetConnectionString("dncmsbase")));
+ });
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("usercenter"), ServerVersion.AutoDetect(config.GetConnectionString("usercenter")));
+ });
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("dncms"), ServerVersion.AutoDetect(config.GetConnectionString("dncms")));
+ });
+ services.AddDGEntityFramework(options =>
+ {
+ options.UseMySql(config.GetConnectionString("companyBaseConf"), ServerVersion.AutoDetect(config.GetConnectionString("companyBaseConf")));
+ });
+ services.AddWorker(config);
+ services.AddDGHttpClient();
+ services.AddRegisterWorker();
+ var builder = new HostBuilder();
+ await builder.RunConsoleAsync();
+}
+catch (Exception ex)
+{
+ Log.Fatal(ex, "Host terminated unexpectedly");
+}
+finally
+{
+ Log.CloseAndFlush();
+}
diff --git a/code/ResourceFlowWorker/Properties/launchSettings.json b/code/ResourceFlowWorker/Properties/launchSettings.json
new file mode 100644
index 0000000..c5c9812
--- /dev/null
+++ b/code/ResourceFlowWorker/Properties/launchSettings.json
@@ -0,0 +1,16 @@
+{
+ "profiles": {
+ "ResourceFlowWorker": {
+ "commandName": "Project",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "Docker": {
+ "commandName": "Docker",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/code/ResourceFlowWorker/ResourceFlowWorker.csproj b/code/ResourceFlowWorker/ResourceFlowWorker.csproj
new file mode 100644
index 0000000..8c4777e
--- /dev/null
+++ b/code/ResourceFlowWorker/ResourceFlowWorker.csproj
@@ -0,0 +1,82 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+ Linux
+
+
+
+
+ $(DockerDefaultDockerfile)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
diff --git a/code/ResourceFlowWorker/Serilog.Production.json b/code/ResourceFlowWorker/Serilog.Production.json
new file mode 100644
index 0000000..491af8c
--- /dev/null
+++ b/code/ResourceFlowWorker/Serilog.Production.json
@@ -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" ]
+ }
+}
\ No newline at end of file
diff --git a/code/ResourceFlowWorker/Serilog.json b/code/ResourceFlowWorker/Serilog.json
new file mode 100644
index 0000000..067e7b1
--- /dev/null
+++ b/code/ResourceFlowWorker/Serilog.json
@@ -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" ]
+ }
+}
diff --git a/code/ResourceFlowWorker/Workers/ResourceWorker.cs b/code/ResourceFlowWorker/Workers/ResourceWorker.cs
new file mode 100644
index 0000000..b517ab4
--- /dev/null
+++ b/code/ResourceFlowWorker/Workers/ResourceWorker.cs
@@ -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 _logger;
+ private readonly SystemConfig? _systemConfig;
+ private static bool IsFrist = true;
+
+ public ResourceWorker(ILogger logger,
+ IServiceProvider serviceProvider,
+ IConfiguration configuration,
+ IHttpClient httpClient
+ ) : base(logger)
+ {
+ _logger = logger;
+ _configuration = configuration;
+ _serviceProvider = serviceProvider;
+ _systemConfig = configuration.GetSection("SystemConfig").Get();
+ _httpClient = httpClient;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ protected override async Task DoWorkAsync()
+ {
+ var status = new List { ResourceConfigStatus.待执行 };
+ if (IsFrist)
+ {
+ IsFrist = !IsFrist;
+ status.Add(ResourceConfigStatus.执行中);
+ status.Add(ResourceConfigStatus.待重启);
+ }
+ using var scope = _serviceProvider.CreateAsyncScope();
+ var dncmsRepository = scope.ServiceProvider.GetRequiredService>();
+ var configs = await dncmsRepository.GetRepository().Query()
+ .Where(x => status.Contains(x.Status)).ToListAsync();
+ if (configs.Any())
+ {
+ configs.ForEach(x => x.Status = ResourceConfigStatus.执行中);
+ await dncmsRepository.GetRepository().BatchUpdateAsync(configs, x => new { x.Status });
+
+ foreach (var config in configs)
+ {
+ var fromList = await dncmsRepository.GetRepository().Query()
+ .Where(x => x.ConfigId == config.Id).ToListAsync();
+ var toList = await dncmsRepository.GetRepository().Query()
+ .Where(x => x.ConfigId == config.Id).ToListAsync();
+ await DoDistribute(config, fromList, toList);
+ }
+ }
+ }
+
+ ///
+ /// 执行分配
+ ///
+ ///
+ private async Task DoDistribute(ResourceFlowConfig config, List froms, List tos)
+ {
+ var flowFroms = new List