| | |
| | | using Aop.Api.Domain; |
| | | using Azure; |
| | | using Furion; |
| | | using Furion.DatabaseAccessor; |
| | | using Furion.DistributedIDGenerator; |
| | | using Furion.FriendlyException; |
| | | using Furion.Schedule; |
| | | using Microsoft.EntityFrameworkCore; |
| | | using StackExchange.Redis; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Linq; |
| | |
| | | IRepository<TaskInfoUser> repTaskInfoUser, |
| | | IRepository<EnterpriseWallet> repEnterpriseWallet, |
| | | IRepository<EnterpriseWalletTransaction> repEnterpriseWalletTransaction, |
| | | IRepository<UserWallet> repUserWallet, |
| | | IRepository<UserWalletTransaction> repUserWalletTransaction, |
| | | AlipayUtils alipayUtils |
| | | ) : IJob |
| | | { |
| | | private readonly IRepository<TaskInfo> rep = rep; |
| | | private readonly IRepository<TaskInfoUser> repTaskInfoUser = repTaskInfoUser; |
| | | private readonly IRepository<EnterpriseWallet> repEnterpriseWallet = repEnterpriseWallet; |
| | | private readonly IRepository<UserWallet> repUserWallet = repUserWallet; |
| | | private readonly IRepository<UserWalletTransaction> repUserWalletTransaction = repUserWalletTransaction; |
| | | private readonly AlipayUtils alipayUtils = alipayUtils; |
| | | |
| | | [UnitOfWork(false)] |
| | |
| | | var env = App.GetConfig<string>("Environment"); |
| | | if (env != "Local") |
| | | { |
| | | var now = DateTime.Now; |
| | | var q = rep.AsQueryable() |
| | | .Where(it => it.SettlementStatus == EnumTaskSettlementStatus.InProcess); |
| | | var minTime = env == "Test" |
| | | ? now.AddMinutes(-5) |
| | | : now.AddDays(-1); |
| | | q = q.Where(it => it.SettlementStartTime.HasValue && it.SettlementStartTime < minTime); |
| | | var tasks = await q.ToListAsync(); |
| | | var tasks = await GetTasks(); |
| | | var enterpriseIds = tasks.DistinctSelect(it => it.EnterpriseId); |
| | | var taskIds = tasks.DistinctSelect(it => it.Id); |
| | | var taskUsers = await repTaskInfoUser |
| | | .Where(it => taskIds.Contains(it.TaskInfoId) && it.SettlementStatus == EnumTaskSettlementStatus.InProcess) |
| | | .ToListAsync(); |
| | | var wallets = await repEnterpriseWallet.AsQueryable() |
| | | .Where(it => enterpriseIds.Contains(it.EnterpriseId) && it.Access == EnumEnterpriseWalletAccess.Alipay) |
| | | .ToListAsync(); |
| | | foreach (var wallet in wallets) |
| | | { |
| | | var response = alipayUtils.FundAccountbookQuery(new AlipayFundAccountbookQueryModel |
| | | { |
| | | AccountBookId = wallet.AccountBookId, |
| | | SceneCode = "SATF_FUND_BOOK", |
| | | MerchantUserId = wallet.Code, |
| | | }); |
| | | if (response.IsError) throw Oops.Oh(EnumErrorCodeType.s510, response.SubMsg ?? response.Msg); |
| | | wallet.Balance = response.AvailableAmount.ToDecimal() ?? 0; |
| | | await repEnterpriseWallet.UpdateNowAsync(wallet); |
| | | } |
| | | var taskUsers = await GetTaskUsers(taskIds); |
| | | var userIds = taskUsers.DistinctSelect(it => it.EnterpriseEmployee.UserId.Value); |
| | | var userWallets = await GetUserWallets(userIds); |
| | | var enterpriseWallets = await GetEnterpriseWallets(enterpriseIds); |
| | | if (tasks.IsNotNull()) |
| | | { |
| | | foreach (var task in tasks) |
| | | { |
| | | var wallet = wallets.FirstOrDefault(it => it.EnterpriseId == task.EnterpriseId); |
| | | var enterpriseWallet = enterpriseWallets.FirstOrDefault(it => it.EnterpriseId == task.EnterpriseId); |
| | | |
| | | task.SettlementStatus = EnumTaskSettlementStatus.Completed; |
| | | task.SettlementTime = DateTime.Now; |
| | | await rep.UpdateNowAsync(task); |
| | |
| | | user.SettlementTime = DateTime.Now; |
| | | await repTaskInfoUser.UpdateNowAsync(user); |
| | | |
| | | var order = new EnterpriseWalletTransaction(); |
| | | order.Type = EnumEnterpriseWalletTransactionType.Recharge; |
| | | order.WalletId = wallet.Id; |
| | | order.TaskUserId = user.Id; |
| | | order.Amount = user.ActualSettlementAmount ?? 0; |
| | | order.Remark = user.SettlementRemark; |
| | | order.ProductCode = "SINGLE_TRANSFER_NO_PWD"; |
| | | order.BizScene = "ENTRUST_TRANSFER"; |
| | | order.TransactionStatus = EnumEnterpriseWalletTransactionStatus.WaitSubmit; |
| | | order.Balance = wallet.Balance; |
| | | order.ReceiveName = user.ReceiveName; |
| | | order.ReceiveAccount = user.ReceiveAccount; |
| | | await SetCode(order); |
| | | await repEnterpriseWalletTransaction.InsertNowAsync(order); |
| | | var userWallet = await AddUserWalletTransactionIncome(userWallets, task, user); |
| | | |
| | | var response = alipayUtils.FundTransUniTransfer(new AlipayFundTransUniTransferModel |
| | | { |
| | | OutBizNo = order.Code, |
| | | TransAmount = order.Amount.ToString(), |
| | | ProductCode = order.ProductCode, |
| | | BizScene = order.BizScene, |
| | | PayeeInfo = new Participant |
| | | { |
| | | IdentityType = "ALIPAY_LOGON_ID", |
| | | Identity = order.ReceiveAccount, |
| | | Name = order.ReceiveName, |
| | | }, |
| | | PayerInfo = new Participant |
| | | { |
| | | IdentityType = "ACCOUNT_BOOK_ID", |
| | | Identity = wallet.AccountBookId, |
| | | ExtInfo = new |
| | | { |
| | | agreement_no = wallet.AgreementNo, |
| | | }.ToJson(), |
| | | }, |
| | | OrderTitle = order.Remark, |
| | | Remark = order.Remark, |
| | | BusinessParams = new |
| | | { |
| | | withdraw_timeliness = "T0" |
| | | }.ToJson() |
| | | }, "/api/user/enterpriseWallet/alipayFundTransOrderChangedNotify"); |
| | | if (response.IsError) |
| | | { |
| | | order.ErrorCode = response.Code; |
| | | order.FailReason = response.SubMsg ?? response.Msg; |
| | | } |
| | | else |
| | | { |
| | | order.OrderId = response.OrderId; |
| | | order.PayFundOrderId = response.PayFundOrderId; |
| | | order.SettleSerialNo = response.SettleSerialNo; |
| | | order.TransDate = response.TransDate.ToDateTime(); |
| | | order.Link = response.Link; |
| | | order.Status = response.Status; |
| | | order.SubStatus = response.SubStatus; |
| | | order.TransactionStatus = response.Status == "SUCCESS" |
| | | ? EnumEnterpriseWalletTransactionStatus.Success |
| | | : response.Status == "DEALING" |
| | | ? EnumEnterpriseWalletTransactionStatus.Dealing |
| | | : response.Status == "REFUND" |
| | | ? EnumEnterpriseWalletTransactionStatus.Refund |
| | | : EnumEnterpriseWalletTransactionStatus.Fail; |
| | | await repEnterpriseWalletTransaction.UpdateNowAsync(order); |
| | | } |
| | | var withdraw = await AddUserWalletTransactionWithdraw(userWallet, task, user); |
| | | |
| | | var transfer = await AddEnterpriseWalletTransactionTransfer(enterpriseWallet, task, user); |
| | | |
| | | await UpdateUserWalletTransactionWithdraw(userWallet, withdraw, transfer); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 更新用户钱包提现记录 |
| | | /// </summary> |
| | | /// <param name="wallet"></param> |
| | | /// <param name="withdraw"></param> |
| | | /// <param name="transfer"></param> |
| | | /// <returns></returns> |
| | | private async Task UpdateUserWalletTransactionWithdraw(UserWallet wallet, UserWalletTransaction withdraw, EnterpriseWalletTransaction transfer) |
| | | { |
| | | withdraw.EnterpriseWalletTransactionId = transfer.Id; |
| | | withdraw.TransactionStatus = transfer.TransactionStatus; |
| | | withdraw.TransDate = transfer.TransDate; |
| | | if (withdraw.TransactionStatus == EnumWalletTransactionStatus.Success) |
| | | { |
| | | withdraw.ActualAmount = withdraw.Amount; |
| | | } |
| | | else |
| | | { |
| | | withdraw.ActualAmount = 0; |
| | | |
| | | var order = new UserWalletTransaction(); |
| | | order.WalletId = wallet.Id; |
| | | order.Type = EnumUserWalletTransactionType.Income; |
| | | order.OperatorUserId = withdraw.OperatorUserId; |
| | | order.OperatorTime = withdraw.OperatorTime; |
| | | order.Title = $"收入-提现失败退款"; |
| | | order.Amount = withdraw.Amount; |
| | | order.ActualAmount = order.Amount; |
| | | order.Balance = wallet.Balance; |
| | | order.AfterBalance = wallet.Balance + order.Amount; |
| | | order.TransDate = DateTime.Now; |
| | | order.TransactionStatus = EnumWalletTransactionStatus.Success; |
| | | await SetCode(order); |
| | | await repUserWalletTransaction.InsertNowAsync(order); |
| | | |
| | | wallet.Balance = order.AfterBalance; |
| | | await repUserWallet.UpdateNowAsync(wallet); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 添加用户钱包提现记录 |
| | | /// </summary> |
| | | /// <param name="wallet"></param> |
| | | /// <param name="task"></param> |
| | | /// <param name="user"></param> |
| | | /// <returns></returns> |
| | | private async Task<UserWalletTransaction> AddUserWalletTransactionWithdraw(UserWallet wallet, TaskInfo task, TaskInfoUser user) |
| | | { |
| | | wallet.Balance -= user.ActualSettlementAmount ?? 0; |
| | | await repUserWallet.UpdateNowAsync(wallet); |
| | | |
| | | var order = new UserWalletTransaction(); |
| | | order.WalletId = wallet.Id; |
| | | order.Type = EnumUserWalletTransactionType.Withdraw; |
| | | order.OperatorUserId = task.SettlementOperatorUserId; |
| | | order.OperatorTime = task.SettlementStartTime; |
| | | order.Title = $"提现-支付宝提现"; |
| | | order.Amount = user.ActualSettlementAmount ?? 0; |
| | | order.Balance = wallet.Balance; |
| | | order.AfterBalance = wallet.Balance - order.Amount; |
| | | order.ReceiveName = user.ReceiveName; |
| | | order.ReceiveAccount = user.ReceiveAccount; |
| | | order.TransDate = DateTime.Now; |
| | | order.TransactionStatus = EnumWalletTransactionStatus.Dealing; |
| | | await SetCode(order); |
| | | await repUserWalletTransaction.InsertNowAsync(order); |
| | | return order; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 添加用户钱包收入记录 |
| | | /// </summary> |
| | | /// <param name="wallets"></param> |
| | | /// <param name="task"></param> |
| | | /// <param name="user"></param> |
| | | /// <returns></returns> |
| | | private async Task<UserWallet> AddUserWalletTransactionIncome(List<UserWallet> wallets, TaskInfo task, TaskInfoUser user) |
| | | { |
| | | var wallet = wallets.FirstOrDefault(it => it.UserId == user.EnterpriseEmployee.UserId); |
| | | if (wallet == null) |
| | | { |
| | | wallet = new UserWallet(); |
| | | wallet.UserId = user.EnterpriseEmployee.UserId.Value; |
| | | await repUserWallet.InsertNowAsync(wallet); |
| | | } |
| | | var order = new UserWalletTransaction(); |
| | | order.WalletId = wallet.Id; |
| | | order.Type = EnumUserWalletTransactionType.Income; |
| | | order.TaskUserId = user.Id; |
| | | order.EnterpriseName = task.Enterprise.EnterpriseName; |
| | | order.SettlementTime = user.SettlementTime; |
| | | order.SettlementAmount = user.SettlementAmount; |
| | | order.OperatorUserId = task.SettlementOperatorUserId; |
| | | order.OperatorTime = task.SettlementStartTime; |
| | | order.Title = $"收入-{order.EnterpriseName}"; |
| | | order.Amount = user.ActualSettlementAmount ?? 0; |
| | | order.ActualAmount = order.Amount; |
| | | order.Balance = wallet.Balance; |
| | | order.AfterBalance = wallet.Balance + order.Amount; |
| | | order.TransDate = DateTime.Now; |
| | | order.TransactionStatus = EnumWalletTransactionStatus.Success; |
| | | await SetCode(order); |
| | | await repUserWalletTransaction.InsertNowAsync(order); |
| | | |
| | | wallet.Balance = order.AfterBalance; |
| | | await repUserWallet.UpdateNowAsync(wallet); |
| | | |
| | | return wallet; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 添加企业钱包转账记录 |
| | | /// </summary> |
| | | /// <param name="wallet"></param> |
| | | /// <param name="task"></param> |
| | | /// <param name="user"></param> |
| | | /// <returns></returns> |
| | | private async Task<EnterpriseWalletTransaction> AddEnterpriseWalletTransactionTransfer(EnterpriseWallet wallet, TaskInfo task, TaskInfoUser user) |
| | | { |
| | | var order = new EnterpriseWalletTransaction(); |
| | | order.Type = EnumEnterpriseWalletTransactionType.Transfer; |
| | | order.WalletId = wallet.Id; |
| | | order.TaskUserId = user.Id; |
| | | order.Amount = user.ActualSettlementAmount ?? 0; |
| | | order.Remark = user.SettlementRemark; |
| | | order.ProductCode = "SINGLE_TRANSFER_NO_PWD"; |
| | | order.BizScene = "ENTRUST_TRANSFER"; |
| | | order.TransactionStatus = EnumWalletTransactionStatus.WaitSubmit; |
| | | order.Balance = wallet.Balance; |
| | | order.AfterBalance = wallet.Balance - order.Amount; |
| | | order.OperatorUserId = task.SettlementOperatorUserId; |
| | | order.OperatorTime = task.SettlementStartTime; |
| | | order.ReceiveUserId = user.EnterpriseEmployee.UserId; |
| | | order.ReceiveName = user.ReceiveName; |
| | | order.ReceiveAccount = user.ReceiveAccount; |
| | | await SetCode(order); |
| | | await repEnterpriseWalletTransaction.InsertNowAsync(order); |
| | | |
| | | var response = alipayUtils.FundTransUniTransfer(new AlipayFundTransUniTransferModel |
| | | { |
| | | OutBizNo = order.Code, |
| | | TransAmount = order.Amount.ToString(), |
| | | ProductCode = order.ProductCode, |
| | | BizScene = order.BizScene, |
| | | PayeeInfo = new Participant |
| | | { |
| | | IdentityType = "ALIPAY_LOGON_ID", |
| | | Identity = order.ReceiveAccount, |
| | | Name = order.ReceiveName, |
| | | }, |
| | | PayerInfo = new Participant |
| | | { |
| | | IdentityType = "ACCOUNT_BOOK_ID", |
| | | Identity = wallet.AccountBookId, |
| | | ExtInfo = new |
| | | { |
| | | agreement_no = wallet.AgreementNo, |
| | | }.ToJson(), |
| | | }, |
| | | OrderTitle = order.Remark, |
| | | Remark = order.Remark, |
| | | BusinessParams = new |
| | | { |
| | | withdraw_timeliness = "T0" |
| | | }.ToJson() |
| | | }, "/api/user/enterpriseWallet/alipayFundTransOrderChangedNotify"); |
| | | if (response.IsError) |
| | | { |
| | | order.ErrorCode = response.Code; |
| | | order.FailReason = response.SubMsg ?? response.Msg; |
| | | await repEnterpriseWalletTransaction.UpdateNowAsync(order); |
| | | } |
| | | else |
| | | { |
| | | order.OrderId = response.OrderId; |
| | | order.PayFundOrderId = response.PayFundOrderId; |
| | | order.SettleSerialNo = response.SettleSerialNo; |
| | | order.TransDate = response.TransDate.ToDateTime(); |
| | | order.Link = response.Link; |
| | | order.Status = response.Status; |
| | | order.SubStatus = response.SubStatus; |
| | | order.TransactionStatus = response.Status == "SUCCESS" |
| | | ? EnumWalletTransactionStatus.Success |
| | | : response.Status == "DEALING" |
| | | ? EnumWalletTransactionStatus.Dealing |
| | | : response.Status == "REFUND" |
| | | ? EnumWalletTransactionStatus.Refund |
| | | : EnumWalletTransactionStatus.Fail; |
| | | await repEnterpriseWalletTransaction.UpdateNowAsync(order); |
| | | } |
| | | return order; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 查询用户钱包 |
| | | /// </summary> |
| | | /// <param name="userIds"></param> |
| | | /// <returns></returns> |
| | | private async Task<List<UserWallet>> GetUserWallets(List<Guid> userIds) |
| | | { |
| | | var wallets = await repUserWallet.AsQueryable().AsNoTracking() |
| | | .Where(it => userIds.Contains(it.UserId)) |
| | | .ToListAsync(); |
| | | return wallets; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 查询企业钱包 |
| | | /// </summary> |
| | | /// <param name="enterpriseIds"></param> |
| | | /// <returns></returns> |
| | | private async Task<List<EnterpriseWallet>> GetEnterpriseWallets(List<Guid> enterpriseIds) |
| | | { |
| | | var wallets = await repEnterpriseWallet.AsQueryable() |
| | | .Where(it => enterpriseIds.Contains(it.EnterpriseId) && it.Access == EnumEnterpriseWalletAccess.Alipay) |
| | | .ToListAsync(); |
| | | foreach (var wallet in wallets) |
| | | { |
| | | var response = alipayUtils.FundAccountbookQuery(new AlipayFundAccountbookQueryModel |
| | | { |
| | | AccountBookId = wallet.AccountBookId, |
| | | SceneCode = "SATF_FUND_BOOK", |
| | | MerchantUserId = wallet.Code, |
| | | }); |
| | | if (response.IsError) throw Oops.Oh(EnumErrorCodeType.s510, response.SubMsg ?? response.Msg); |
| | | wallet.Balance = response.AvailableAmount.ToDecimal() ?? 0; |
| | | await repEnterpriseWallet.UpdateNowAsync(wallet); |
| | | } |
| | | return wallets; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 查询任务用户 |
| | | /// </summary> |
| | | /// <param name="taskIds"></param> |
| | | /// <returns></returns> |
| | | private async Task<List<TaskInfoUser>> GetTaskUsers(List<Guid> taskIds) |
| | | { |
| | | var taskUsers = await repTaskInfoUser |
| | | .Include(it => it.EnterpriseEmployee) |
| | | .Where(it => |
| | | taskIds.Contains(it.TaskInfoId) |
| | | && it.SettlementStatus == EnumTaskSettlementStatus.InProcess) |
| | | .ToListAsync(); |
| | | return taskUsers; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 查询任务 |
| | | /// </summary> |
| | | /// <param name="env"></param> |
| | | /// <returns></returns> |
| | | private async Task<List<TaskInfo>> GetTasks() |
| | | { |
| | | var now = DateTime.Now; |
| | | var q = rep.AsQueryable() |
| | | .Include(it => it.Enterprise) |
| | | .Where(it => it.SettlementStatus == EnumTaskSettlementStatus.InProcess); |
| | | var minTime = App.GetConfig<string>("Task:SettlementTime") == "T0" |
| | | ? now.AddMinutes(-5) |
| | | : now.AddDays(-1); |
| | | q = q.Where(it => it.SettlementStartTime.HasValue && it.SettlementStartTime < minTime); |
| | | var tasks = await q.ToListAsync(); |
| | | return tasks; |
| | | } |
| | | |
| | | private async Task SetCode(EnterpriseWalletTransaction entity) |
| | | { |
| | | entity.Code = $"{DateTime.Now:yyyyMMddHHmmss}{new Random(IDGen.NextID().GetHashCode()).Next(1000, 9999)}"; |
| | |
| | | } |
| | | } |
| | | |
| | | private async Task SetCode(UserWalletTransaction entity) |
| | | { |
| | | entity.Code = $"{DateTime.Now:yyyyMMddHHmmss}{new Random(IDGen.NextID().GetHashCode()).Next(1000, 9999)}"; |
| | | var exist = await repUserWalletTransaction.AsQueryable().AsNoTracking() |
| | | .AnyAsync(it => it.Code == entity.Code); |
| | | if (exist) |
| | | { |
| | | await SetCode(entity); |
| | | } |
| | | } |
| | | |
| | | } |
| | | } |