using NCC.Common.Core.Manager;
using NCC.Common.Enum;
using NCC.Common.Extension;
using NCC.Common.Filter;
using NCC.Dependency;
using NCC.DynamicApiController;
using NCC.FriendlyException;
using NCC.Extend.Interfaces.UavOrder;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NCC.Extend.Entitys;
using NCC.Extend.Entitys.Dto.UavOrder;
using Yitter.IdGenerator;
using NCC.Common.Helper;
using NCC.JsonSerialization;
using Microsoft.AspNetCore.Authorization;
using NCC.Extend.Entitys.Enums;
using NCC.Code;
using System.Text.Json;
using AlipaySDKNet.OpenAPI.Util.Model;
using Alipay.AopSdk.Core.Util;
using Microsoft.AspNetCore.Http;
using Serilog;
using System.IO;
using System.Text;
using NCC.Extend.Interfaces.MqttPublisher;
using NCC.Extend.Entitys.Dto.UavDevice;
using NCC.Extend.Interfaces.AliPay;
using NCC.Extend.Entitys.Dto.UavFeeRule;
using NCC.UnifyResult;
using NCC.Localization;
using Spire.Presentation;
using NCC.System.Entitys;
using NCC.System.Entitys.Permission;
using SqlSugar;
namespace NCC.Extend.UavOrder
{
///
/// 订单管理服务
///
[ApiDescriptionSettings(Tag = "订单管理服务", Name = "UavOrder", Order = 200)]
[Route("api/Extend/[controller]")]
public class UavOrderService : IUavOrderService, IDynamicApiController, ITransient
{
private readonly ISqlSugarRepository _uavOrderRepository;
private readonly SqlSugarScope _db;
private readonly IUserManager _userManager;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IMqttPublisherService _mqttService;
private readonly IAliPayService _aliPayService;
///
/// 初始化一个类型的新实例
///
public UavOrderService(ISqlSugarRepository uavOrderRepository, IUserManager userManager, IHttpContextAccessor httpContextAccessor, IMqttPublisherService mqttService, IAliPayService aliPayService)
{
_uavOrderRepository = uavOrderRepository;
_db = _uavOrderRepository.Context;
_userManager = userManager;
_httpContextAccessor = httpContextAccessor;
_mqttService = mqttService;
_aliPayService = aliPayService;
}
#region 获取支付宝配置
[NonAction]
private static AlipayConfig GetAlipayConfig()
{
return new AlipayConfig
{
ServerUrl = "https://openapi.alipay.com",
// AppId = "2021005150691832",
AppId = App.Configuration["PaymentSettings:AlipayConfig:APPLETE_APPID"],
PrivateKey = App.Configuration["PaymentSettings:AlipayConfig:ALIPay_RSA_PRIVATEKEY"],
AlipayPublicKey = App.Configuration["PaymentSettings:AlipayConfig:ALIPay_RSA_ALI_PUBLICKEY"],
};
}
#endregion
#region 获取订单详情
///
/// 获取订单详情
///
/// 订单ID
///
[HttpGet("{id}")]
public async Task GetInfo(string id)
{
// 查询订单实体
var entity = await _db.Queryable().FirstAsync(p => p.Id == id);
if (entity == null) return null;
// 查询相关信息(设备、站点、套餐、货道)一次性批量查询,减少数据库访问次数
var deviceTask = _db.Queryable().Where(p => p.Id == entity.DeviceId).FirstAsync();
var siteTask = _db.Queryable().Where(p => p.Id == entity.SiteId).FirstAsync();
var feeRuleTask = string.IsNullOrEmpty(entity.FeeRuleId) ? Task.FromResult(null) : _db.Queryable().Where(p => p.Id == entity.FeeRuleId).FirstAsync();
var cellTask = _db.Queryable().Where(p => p.Id == entity.RentCellId).FirstAsync();
await Task.WhenAll(deviceTask, siteTask, feeRuleTask, cellTask);
var output = entity.Adapt();
output.startTime = entity.StartTime;
output.endTime = entity.EndTime;
output.statusString = global::System.Enum.GetName(typeof(UavOrderStatusEnum), output.status);
output.feeRuleName = feeRuleTask.Result?.RuleName ?? "未选择套餐";
output.deviceName = deviceTask.Result?.DeviceName ?? "";
output.deviceCode = deviceTask.Result?.DeviceCode ?? "";
output.siteName = siteTask.Result?.SiteName ?? "";
output.rentCellCode = cellTask.Result?.CellCode ?? "";
return output;
}
#endregion
#region 获取订单管理列表
///
/// 获取订单管理列表
///
/// 请求参数
///
[HttpGet("")]
public async Task GetList([FromQuery] UavOrderListQueryInput input)
{
var sidx = input.sidx == null ? "id" : input.sidx;
var data = await _db.Queryable()
.WhereIF(!string.IsNullOrEmpty(input.orderNo), p => p.OrderNo.Contains(input.orderNo))
.WhereIF(!string.IsNullOrEmpty(input.deviceName), p => SqlFunc.Subqueryable().Where(d => d.Id == p.DeviceId).Select(d => d.DeviceName).Contains(input.deviceName))
.WhereIF(!string.IsNullOrEmpty(input.siteName), p => SqlFunc.Subqueryable().Where(s => s.Id == p.SiteId).Select(s => s.SiteName).Contains(input.siteName))
.WhereIF(!string.IsNullOrEmpty(input.aircraftName), p => SqlFunc.Subqueryable().Where(a => a.Id == p.AircraftId).Select(a => a.Model).Contains(input.aircraftName))
.WhereIF(!string.IsNullOrEmpty(input.agentName), p => SqlFunc.Subqueryable().Where(u => u.Id == p.AgentId).Select(u => u.RealName).Contains(input.agentName))
.WhereIF(!string.IsNullOrEmpty(input.rentCellName), p => SqlFunc.Subqueryable().Where(c => c.Id == p.RentCellId).Select(c => c.CellCode).Contains(input.rentCellName))
.WhereIF(!string.IsNullOrEmpty(input.returnCellName), p => SqlFunc.Subqueryable().Where(c => c.Id == p.ReturnCellId).Select(c => c.CellCode).Contains(input.returnCellName))
.WhereIF(!string.IsNullOrEmpty(input.userPhone), p => p.UserPhone.Contains(input.userPhone))
.Select(it => new UavOrderListOutput
{
id = it.Id,
orderNo = it.OrderNo,
deviceId = it.DeviceId,
deviceName = SqlFunc.Subqueryable().Where(u => u.Id == it.DeviceId).Select(u => u.DeviceName),
siteId = it.SiteId,
siteName = SqlFunc.Subqueryable().Where(u => u.Id == it.SiteId).Select(u => u.SiteName),
aircraftId = it.AircraftId,
aircraftName = SqlFunc.Subqueryable().Where(u => u.Id == it.AircraftId).Select(u => u.Model),
agentId = it.AgentId,
agentName = SqlFunc.Subqueryable().Where(u => u.Id == it.AgentId).Select(u => u.RealName),
userAccount = it.UserAccount,
userPhone = it.UserPhone,
userName = it.UserName,
discountAmount = it.DiscountAmount,
actualAmount = it.ActualAmount,
shareRatio = it.ShareRatio,
shareAmount = it.ShareAmount,
finalAmount = it.FinalAmount,
rentCellId = it.RentCellId,
rentCellName = SqlFunc.Subqueryable().Where(u => u.Id == it.RentCellId).Select(u => u.CellCode),
returnCellId = it.ReturnCellId,
returnCellName = SqlFunc.Subqueryable().Where(u => u.Id == it.ReturnCellId).Select(u => u.CellCode),
status = it.Status,
remark = it.Remark,
createTime = it.CreateTime,
startTime = it.StartTime,
endTime = it.EndTime,
feeRuleId = it.FeeRuleId,
aliAuthNo = it.AliAuthNo,
returnDeviceId = it.ReturnDeviceId,
returnDeviceName = SqlFunc.Subqueryable().Where(u => u.Id == it.ReturnDeviceId).Select(u => u.DeviceName),
returnSiteId = it.ReturnSiteId,
returnSiteName = SqlFunc.Subqueryable().Where(u => u.Id == it.ReturnSiteId).Select(u => u.SiteName),
rfid1 = it.RFID1,
rfid2 = it.RFID2
}).MergeTable().Mapper(p =>
{
p.statusString = global::System.Enum.GetName(typeof(UavOrderStatusEnum), p.status);
}).OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize);
return PageResult.SqlSugarPageResult(data);
}
#endregion
#region 获取当前代理商相关的订单
///
/// 获取当前代理商相关的订单
///
/// 请求参数
///
[HttpGet("GetListToAgent")]
public async Task GetListToAgent([FromQuery] UavOrderListQueryInput input)
{
var sidx = input.sidx == null ? "id" : input.sidx;
var data = await _db.Queryable()
.Where(p => p.AgentId == _userManager.UserId)
.WhereIF(!string.IsNullOrEmpty(input.orderNo), p => p.OrderNo.Contains(input.orderNo))
.WhereIF(!string.IsNullOrEmpty(input.deviceName), p => SqlFunc.Subqueryable().Where(d => d.Id == p.DeviceId).Select(d => d.DeviceName).Contains(input.deviceName))
.WhereIF(!string.IsNullOrEmpty(input.siteName), p => SqlFunc.Subqueryable().Where(s => s.Id == p.SiteId).Select(s => s.SiteName).Contains(input.siteName))
.WhereIF(!string.IsNullOrEmpty(input.aircraftName), p => SqlFunc.Subqueryable().Where(a => a.Id == p.AircraftId).Select(a => a.Model).Contains(input.aircraftName))
.WhereIF(!string.IsNullOrEmpty(input.rentCellName), p => SqlFunc.Subqueryable().Where(c => c.Id == p.RentCellId).Select(c => c.CellCode).Contains(input.rentCellName))
.WhereIF(!string.IsNullOrEmpty(input.returnCellName), p => SqlFunc.Subqueryable().Where(c => c.Id == p.ReturnCellId).Select(c => c.CellCode).Contains(input.returnCellName))
.WhereIF(!string.IsNullOrEmpty(input.createTime_start), p => p.CreateTime >= new DateTime(input.createTime_start.ToDate().Year, input.createTime_start.ToDate().Month, input.createTime_start.ToDate().Day, 0, 0, 0) && p.CreateTime <= new DateTime(input.createTime_end.ToDate().Year, input.createTime_end.ToDate().Month, input.createTime_end.ToDate().Day, 23, 59, 59))
.Select(it => new UavOrderListOutput
{
id = it.Id,
orderNo = it.OrderNo,
deviceId = it.DeviceId,
deviceName = SqlFunc.Subqueryable().Where(u => u.Id == it.DeviceId).Select(u => u.DeviceName),
siteId = it.SiteId,
siteName = SqlFunc.Subqueryable().Where(u => u.Id == it.SiteId).Select(u => u.SiteName),
aircraftId = it.AircraftId,
aircraftName = SqlFunc.Subqueryable().Where(u => u.Id == it.AircraftId).Select(u => u.Model),
agentId = it.AgentId,
agentName = SqlFunc.Subqueryable().Where(u => u.Id == it.AgentId).Select(u => u.RealName),
userAccount = it.UserAccount,
userPhone = it.UserPhone,
userName = it.UserName,
discountAmount = it.DiscountAmount,
actualAmount = it.ActualAmount,
shareRatio = it.ShareRatio,
shareAmount = it.ShareAmount,
finalAmount = it.FinalAmount,
rentCellId = it.RentCellId,
rentCellName = SqlFunc.Subqueryable().Where(u => u.Id == it.RentCellId).Select(u => u.CellCode),
returnCellId = it.ReturnCellId,
returnCellName = SqlFunc.Subqueryable().Where(u => u.Id == it.ReturnCellId).Select(u => u.CellCode),
status = it.Status,
remark = it.Remark,
endTime = it.EndTime,
startTime = it.StartTime,
createTime = it.CreateTime,
}).MergeTable().Mapper(p =>
{
p.statusString = global::System.Enum.GetName(typeof(UavOrderStatusEnum), p.status);
}).OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize);
return PageResult.SqlSugarPageResult(data);
}
#endregion
#region 新建订单管理
///
/// 新建订单管理
///
/// 参数
///
[HttpPost("")]
public async Task Create([FromBody] UavOrderCrInput input)
{
var entity = input.Adapt();
entity.Id = YitIdHelper.NextId().ToString();
var isOk = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteCommandAsync();
if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000);
}
#endregion
#region 新建订单管理(支付宝)
///
/// 新建订单管理(支付宝)
///
/// 参数
///
[HttpPost("CreateAlipayOrder")]
public async Task CreateAlipayOrder([FromBody] UavOrderCrInputByAlipay input)
{
var DeviceEntity = await _db.Queryable().FirstAsync(x => x.DeviceCode == input.deviceId);
var feeRuleEntity = await _db.Queryable().FirstAsync(x => x.Id == input.feeRuleId);
if (DeviceEntity.Status != DeviceStatusEnum.启用.GetHashCode())
{
return NCCException.Oh("设备未启用,请使用其他设备!");
}
// 开启事务
try
{
var RerutnEntity = await _db.UseTranAsync(async () =>
{
var halfHourAgo = DateTime.Now.AddMinutes(-30);
// var halfHourAgo = DateTime.Now.AddMinutes(-0);
try
{
//通过设备去找到设备货道,使用With(SqlSugar.SqlWith.RowLock)添加行锁
var deviceCellInfo = await _db.Queryable()
.With(SqlSugar.SqlWith.RowLock)
.Where(x => x.DeviceId == DeviceEntity.Id && x.Status == DeviceCellStatusEnum.充电.GetHashCode())
.Where(x => x.UpdateTime <= halfHourAgo) // 添加条件:更新时间超过半个小时
.WhereIF(feeRuleEntity.MultiCells == 1, x => !SqlFunc.IsNullOrEmpty(x.Rfid1) && !SqlFunc.IsNullOrEmpty(x.Rfid2))
.WhereIF(feeRuleEntity.MultiCells == 0, x => !SqlFunc.IsNullOrEmpty(x.Rfid1) && SqlFunc.IsNullOrEmpty(x.Rfid2))
.OrderBy(x => x.UpdateTime, OrderByType.Asc)
.FirstAsync();
if (deviceCellInfo.IsNullOrEmpty())
{
return new
{
orderNo = "",
cellNo = "",
message = "提示充电时间不足或无可使用的无人机!",
};
}
//再次确认货道状态
if (deviceCellInfo.Status != DeviceCellStatusEnum.充电.GetHashCode())
{
return new
{
orderNo = "",
cellNo = "",
message = "该货道已被占用,请重试!",
};
}
//查询当前用户是否有进行中的订单
var OrderInfo = await _db.Queryable()
.Where(x => x.UserAccount == _userManager.UserId && x.Status == UavOrderStatusEnum.已租接.GetHashCode())
.OrderBy(x => x.UpdateTime, OrderByType.Desc)
.FirstAsync();
if (!OrderInfo.IsNullOrEmpty())
{
return new
{
orderNo = "",
cellNo = "",
message = "一个ID限租一台无人机!",
};
// throw new Exception("一个ID限租一台无人机!");
}
// 更新货道状态为空闲,表示已被预定
deviceCellInfo.Status = DeviceCellStatusEnum.空闲.GetHashCode();
await _db.Updateable(deviceCellInfo).ExecuteCommandAsync();
// 创建订单
UavOrderEntity entity = new UavOrderEntity();
entity.Id = YitIdHelper.NextId().ToString();
entity.OrderNo = $"DD{DateTime.Now:yyyyMMddHHmmssfff}{new Random().Next(1000, 9999)}";
entity.UserAccount = _userManager.UserId;
entity.UserName = _userManager.RealName;
entity.UserPhone = _userManager.Account;
entity.DeviceId = DeviceEntity.Id;
entity.RentCellId = deviceCellInfo.Id;
entity.SiteId = DeviceEntity.SiteId;
entity.AgentId = DeviceEntity.BelongUserId;
entity.AircraftId = deviceCellInfo.UavCode;
entity.RFID1 = deviceCellInfo.Rfid1;
entity.RFID2 = deviceCellInfo.Rfid2;
entity.Status = UavOrderStatusEnum.待付款.GetHashCode();
entity.CreateTime = DateTime.Now;
entity.UpdateTime = DateTime.Now;
entity.FeeRuleId = input.feeRuleId;
entity.FeeRuleSnapshot = _db.Queryable().First(x => x.Id == input.feeRuleId).ToJson();
var isOk = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteCommandAsync();
if (!(isOk > 0))
{
return new
{
orderNo = "",
cellNo = "",
message = "订单创建失败",
};
}
return new
{
orderNo = entity.OrderNo,
cellNo = deviceCellInfo.Id,
message = "订单创建成功,请在三分钟内进行支付!",
};
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
});
if (RerutnEntity.IsSuccess)
{
return RerutnEntity.Data;
}
else
{
Log.Error($"创建订单失败:{RerutnEntity.ErrorMessage}");
return RerutnEntity.Data;
// return throw new NCC.Exception("订单创建失败");
}
}
catch (Exception ex)
{
Log.Error($"创建订单失败:{ex.Message}");
return new { code = 500, message = ex.Message };
}
}
#endregion
#region 更新订单管理
///
/// 更新订单管理
///
/// 主键
/// 参数
///
[HttpPut("{id}")]
public async Task Update(string id, [FromBody] UavOrderUpInput input)
{
var entity = input.Adapt();
var isOk = await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1001);
}
#endregion
#region 删除订单管理
///
/// 删除订单管理
///
///
[HttpDelete("{id}")]
public async Task Delete(string id)
{
var entity = await _db.Queryable().FirstAsync(p => p.Id == id);
_ = entity ?? throw NCCException.Oh(ErrorCode.COM1005);
var isOk = await _db.Deleteable().Where(d => d.Id == id).ExecuteCommandAsync();
if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1002);
}
#endregion
#region 订单完结
///
/// 订单完结
///
///
///
[HttpPost("FinishOrderAsync")]
public async Task FinishOrderAsync(UavEndOrderCrInput input)
{
var order = await _db.Queryable().FirstAsync(p => p.OrderNo == input.orderNo && p.Status == UavOrderStatusEnum.已租接.GetHashCode());
if (order == null)
{
throw NCCException.Oh("订单不存在或已经操作归还");
}
//获取归还缓存数据
var OldcacheEntityInfo = await _db.Queryable().Where(x => x.OrderNo == order.OrderNo && x.Status != OrderDoorStatus.归还错误.GetHashCode()).FirstAsync();
if (OldcacheEntityInfo.IsNotEmptyOrNull())
{
throw NCCException.Oh("请勿重复操作归还订单");
}
//查询当前柜子是否有空的货道,可以随机一个空闲的货道
//通过设备编码获取设备信息
var DeviceInfo = _db.Queryable().First(p => p.DeviceCode == input.deviceCode);
//判断订单里面的设备编码是否一致
if (order.DeviceId != DeviceInfo.Id)
{
throw NCCException.Oh("请在租借的机柜上进行归还");
}
// var CellCodeInfo = await _db.Queryable().Where(x => x.DeviceId == DeviceInfo.Id && x.Status == DeviceCellStatusEnum.空闲.GetHashCode()).OrderBy(x => SqlFunc.GetRandom()).FirstAsync();
// if (CellCodeInfo.IsNullOrEmpty())
// {
// throw NCCException.Oh("当前无空闲货道,请换其他机柜进行归还");
// }
// await CloseOrderAndUnlockAsync(new UavDeviceOpenCrInput
// {
// id = order.DeviceId,
// lane = CellCodeInfo.CellCode//传入货道编号,1、2、3、4、5、6
// }, order.OrderNo);
var CellCodeInfo = await _db.Queryable().Where(x => x.Id == order.RentCellId).OrderBy(x => SqlFunc.GetRandom()).FirstAsync();
await CloseOrderAndUnlockAsync(new UavDeviceOpenCrInput
{
id = order.DeviceId,
lane = CellCodeInfo.CellCode//传入货道编号,1、2、3、4、5、6
}, order.OrderNo);
//变成归还中
await _db.Updateable(order).ExecuteCommandAsync();
//这里把订单缓存开门表的信息加上
UavOrderDoorCacheEntity cacheEntity = new UavOrderDoorCacheEntity
{
Id = YitIdHelper.NextId().ToString(),
DeviceCode = input.deviceCode,
Lane = CellCodeInfo.CellCode,
OrderNo = order.OrderNo,
Message = "订单归还开门",
Status = OrderDoorStatus.已开门.GetHashCode(),
};
var cacheEntityInfo = await _db.Queryable().Where(x => x.OrderNo == order.OrderNo).FirstAsync();
//通过订单查询数据是否存在
if (cacheEntityInfo.IsNotEmptyOrNull())
{
cacheEntityInfo.Status = OrderDoorStatus.已开门.GetHashCode();
cacheEntityInfo.Message += "订单重新归还开门";
var isOk = await _db.Updateable(cacheEntityInfo).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
}
else
{
await _db.Insertable(cacheEntity).ExecuteCommandAsync();
}
return new
{
orderNo = order.OrderNo,
lane = CellCodeInfo.CellCode
};
}
#endregion
#region 获取支付宝扣款后的回调信息
///
/// 获取支付宝扣款后的回调信息
///
///
[HttpPost("orderNotify")]
[AllowAnonymous, NonUnify]
public async Task OrderNotify()
{
var request = _httpContextAccessor.HttpContext.Request;
try
{
// 允许读取请求体多次
request.EnableBuffering();
// 读取原始 Body 字符串(可选,调试用)
request.Body.Position = 0;
using (var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true))
{
var rawBody = await reader.ReadToEndAsync();
Log.Information($"[支付宝扣款回调] 原始 Body:{rawBody}");
request.Body.Position = 0; // 复位
}
// 读取 Form 表单参数
var form = await request.ReadFormAsync();
var paramDict = form.ToDictionary(k => k.Key, v => v.Value.ToString());
Log.Information($"[支付宝扣款回调] 表单参数:{JsonSerializer.Serialize(paramDict)}");
// ====== 可选:支付宝签名校验 ======
var isValid = AlipaySignature.RSACheckV1(
paramDict,
GetAlipayConfig().AlipayPublicKey, // 公钥
"UTF-8",
"RSA2",
false
);
if (!isValid)
{
Log.Information("[支付宝扣款回调] 签名验证失败");
return "fail";
}
// 处理业务逻辑
// 3. 验签通过,处理业务逻辑
string outTradeNo = paramDict["out_trade_no"]; // 商户订单号
string tradeNo = paramDict["trade_no"]; // 支付宝交易号
string tradeStatus = paramDict["trade_status"]; // 交易状态
string totalAmount = paramDict["total_amount"]; // 支付金额
string buyerId = paramDict["buyer_id"]; // 支付宝买家id
string passbackParams = paramDict.GetValueOrDefault("passback_params"); // 自定义参数
if (tradeStatus == "TRADE_SUCCESS" || tradeStatus == "TRADE_FINISHED")
{
// 支付成功,更新数据库订单状态等
Log4jHelper.LogInfo($"支付宝支付成功:订单号={outTradeNo}, 支付宝交易号={tradeNo}");
var order = _db.Queryable().First(x => x.OrderNo == outTradeNo);
if (order != null)
{
order.Status = UavOrderStatusEnum.已完成.GetHashCode();
//实际金额
order.ActualAmount = totalAmount.ToDecimal();
await _db.Updateable(order).ExecuteCommandAsync();
var agent = _db.Queryable().Where(it => it.AgentId == order.AgentId).First();
var agentLIst = _db.Queryable().ToParentList(it => it.ParentId, agent.Id);
decimal AgentFinalAmount = 0;
foreach (var item in agentLIst)
{
if (item.ParentId.IsNotEmptyOrNull())
{
UavWalletFlowEntity uavWalletFlowEntity_Agent = new UavWalletFlowEntity
{
Id = YitIdHelper.NextId().ToString(),
UserId = item.ParendAgentId,
FlowDirection = FlowDirectionEnum.收入.GetHashCode(),
Amount = order.ActualAmount * item.ProfitPercent,
RelatedId = order.OrderNo,
RelatedType = "分润",
CreateTime = DateTime.Now,
};
AgentFinalAmount += uavWalletFlowEntity_Agent.Amount;
await _db.Insertable(uavWalletFlowEntity_Agent).ExecuteCommandAsync();
}
}
//计算终端最终收入多少钱
var terminalFinalAmount = order.ActualAmount - AgentFinalAmount;
//查询终端人员分润设置
var terminalProfitSettingLIst = _db.Queryable().Where(x => x.AgentId == order.AgentId && x.EnabledMark == 1).ToList();
foreach (var item in terminalProfitSettingLIst)
{
//计算终端人员分润
var terminalProfitAmount = terminalFinalAmount * (item.ProfitRatio / 100);
UavWalletFlowEntity uavWalletFlowEntity_Terminal = new UavWalletFlowEntity
{
Id = YitIdHelper.NextId().ToString(),
UserId = item.ProfitUserId,
FlowDirection = FlowDirectionEnum.收入.GetHashCode(),
Amount = terminalProfitAmount,
RelatedId = order.OrderNo,
RelatedType = "分润",
CreateTime = DateTime.Now,
};
await _db.Insertable(uavWalletFlowEntity_Terminal).ExecuteCommandAsync();
}
//计算代理商最终收入多少钱
var agentFinalAmount = terminalFinalAmount - AgentFinalAmount;
UavWalletFlowEntity uavWalletFlowEntity = new UavWalletFlowEntity
{
Id = YitIdHelper.NextId().ToString(),
UserId = order.AgentId,
FlowDirection = FlowDirectionEnum.收入.GetHashCode(),
Amount = agentFinalAmount,
RelatedId = order.OrderNo,
RelatedType = "订单",
CreateTime = DateTime.Now,
};
await _db.Insertable(uavWalletFlowEntity).ExecuteCommandAsync();
var OpenCacheInfo = _db.Queryable().First(x => x.OrderNo == order.OrderNo);
if (OpenCacheInfo.IsNotEmptyOrNull())
{
OpenCacheInfo.Status = OrderDoorStatus.已关门.GetHashCode();
OpenCacheInfo.Message += $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} :订单已完成,更新关门";
_db.Updateable(OpenCacheInfo).ExecuteCommand();
}
}
}
return "success"; // 必须返回 success 才视为成功
}
catch (Exception ex)
{
Log.Information(ex, "[支付宝扣款回调] 处理异常");
return "fail"; // 返回 fail 会触发支付宝重试
}
}
#endregion
#region 获取支付宝预授权后的回调信息
///
/// 获取支付宝预授权后的回调信息
///
///
[HttpPost("OrderNotify_V2")]
[AllowAnonymous, NonUnify]
public async Task OrderNotify_V2()
{
var request = _httpContextAccessor.HttpContext.Request;
try
{
// 允许读取请求体多次
request.EnableBuffering();
// 读取原始 Body 字符串(可选,调试用)
request.Body.Position = 0;
using (var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true))
{
var rawBody = await reader.ReadToEndAsync();
Log.Information($"[支付宝授权回调] 原始 Body:{rawBody}");
request.Body.Position = 0; // 复位
}
// 读取 Form 表单参数
var form = await request.ReadFormAsync();
var paramDict = form.ToDictionary(k => k.Key, v => v.Value.ToString());
Log.Information($"[支付宝授权回调] 表单参数:{JsonSerializer.Serialize(paramDict)}");
// ====== 可选:支付宝签名校验 ======
var isValid = AlipaySignature.RSACheckV1(
paramDict,
GetAlipayConfig().AlipayPublicKey, // 公钥
"UTF-8",
"RSA2",
false
);
if (!isValid)
{
Log.Information("[支付宝回调] 签名验证失败");
return "fail";
}
// 处理业务逻辑
string outOrderNo = paramDict.GetValueOrDefault("out_order_no");
string authNo = paramDict.GetValueOrDefault("auth_no");
string status = paramDict.GetValueOrDefault("status");
if (status == "SUCCESS")
{
Log.Information($"[支付宝回调] 授权成功,订单号: {outOrderNo}, 授权号: {authNo}");
//根据订单编号处理订单并开门
await ProcessOrderAndOpenDoor(outOrderNo, authNo);
}
return "success"; // 必须返回 success 才视为成功
}
catch (Exception ex)
{
Log.Information(ex, "[支付宝回调] 处理异常");
return "fail"; // 返回 fail 会触发支付宝重试
}
}
///
/// 根据订单编号处理订单并开门
///
/// 外部订单号
/// 支付宝授权号
///
[NonAction]
public async Task ProcessOrderAndOpenDoor(string outOrderNo, string authNo)
{
var order = _db.Queryable().First(x => x.OrderNo == outOrderNo);
if (order == null)
{
Log.Information($"未找到订单号为 {outOrderNo} 的订单");
return false;
}
if (order.Status == UavOrderStatusEnum.已租接.GetHashCode())
{
Log.Information("订单已处理,请勿重复处理");
return false;
}
// 更新订单信息
order.Status = UavOrderStatusEnum.已租接.GetHashCode();
order.AliAuthNo = authNo;
order.StartTime = DateTime.Now;
await _db.Updateable(order).ExecuteCommandAsync();
var CellCodeInfo = _db.Queryable().First(x => x.Id == order.RentCellId);
// CellCodeInfo.Status = DeviceCellStatusEnum.已租接.GetHashCode();
// await _db.Updateable(CellCodeInfo).ExecuteCommandAsync();
// 执行开门操作
await OrderCheckOpenDoor(new UavDeviceOpenCrInput
{
id = order.DeviceId,
lane = CellCodeInfo.CellCode//传入货道编号,1、2、3、4、5、6
}, order.OrderNo);
return true;
}
#endregion
#region 获取支付宝取消预授权后的回调信息
///
/// 获取支付宝取消预授权后的回调信息
///
///
[HttpPost("OrderNotify_Cancel")]
[AllowAnonymous, NonUnify]
public async Task OrderNotify_Cancel()
{
var request = _httpContextAccessor.HttpContext.Request;
try
{
// 允许读取请求体多次
request.EnableBuffering();
// 读取原始 Body 字符串(可选,调试用)
request.Body.Position = 0;
using (var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true))
{
var rawBody = await reader.ReadToEndAsync();
Log.Information($"[支付宝取消预授权回调] 原始 Body:{rawBody}");
request.Body.Position = 0; // 复位
}
// 读取 Form 表单参数
var form = await request.ReadFormAsync();
var paramDict = form.ToDictionary(k => k.Key, v => v.Value.ToString());
Log.Information($"[支付宝取消预授权回调] 表单参数:{JsonSerializer.Serialize(paramDict)}");
// ====== 可选:支付宝签名校验 ======
var isValid = AlipaySignature.RSACheckV1(
paramDict,
GetAlipayConfig().AlipayPublicKey, // 公钥
"UTF-8",
"RSA2",
false
);
if (!isValid)
{
Log.Information("[支付宝取消预授权回调] 签名验证失败");
return "fail";
}
// 处理业务逻辑
string outOrderNo = paramDict.GetValueOrDefault("out_order_no");
string authNo = paramDict.GetValueOrDefault("auth_no");
string status = paramDict.GetValueOrDefault("action");
Log.Information($"[支付宝取消预授权回调] 授权成功,订单号: {outOrderNo}, 授权号: {authNo}");
if (status == "unfreeze")
{
var order = _db.Queryable().First(x => x.OrderNo == outOrderNo);
if (order != null)
{
order.Status = UavOrderStatusEnum.已完成.GetHashCode();
order.EndTime = DateTime.Now;
await _db.Updateable(order).ExecuteCommandAsync();
// var UavInfo = _db.Queryable().Where(x => x.Id == order.AircraftId).First();
// UavInfo.Status = UavAircraftStatusEnum.空闲.GetHashCode();
// _db.Updateable(UavInfo).ExecuteCommand();//无人机状态改为租赁中
var DeviceCellInfo = _db.Queryable().Where(x => x.Id == order.RentCellId).First();//找到租赁的货道
DeviceCellInfo.Status = DeviceCellStatusEnum.充电.GetHashCode();//更新货道为空闲,后续可以进行归还
_db.Updateable(DeviceCellInfo).ExecuteCommand();
}
}
return "success"; // 必须返回 success 才视为成功
}
catch (Exception ex)
{
Log.Information(ex, "[支付宝取消预授权回调] 处理异常");
return "fail"; // 返回 fail 会触发支付宝重试
}
}
#endregion
#region 处理消息订阅的信息
///
/// 处理消息订阅的信息
///
///
///
[NonAction]
public async Task HandleSubscribeMessage(string message)
{
UavDeviceOprnNotifyCrInput info = message.ToObject();
if (!string.IsNullOrEmpty(info.rfid_1) && info.rfid_1.All(c => c == '0'))
{
info.rfid_1 = "";
}
if (!string.IsNullOrEmpty(info.rfid_2) && info.rfid_2.All(c => c == '0'))
{
info.rfid_2 = "";
}
//如果两个rfid都为空,无人机不为空,就给mqtt发送消息
// if (string.IsNullOrEmpty(info.rfid_1) && !string.IsNullOrEmpty(info.uavcode))
// {
// }
//通过设备id去查询设备信息
var entity = await _db.Queryable().Where(x => x.DeviceCode == info.id).FirstAsync();
info.id = entity.Id;//重写ID,这样就不用去修改下面的逻辑了
//通过机柜和货道去找到货道的id
var CellCodeInfo = _db.Queryable().First(x => x.DeviceId == info.id && x.CellCode == info.lane);
//通过货道去查询货道缓存信息
var OpenCacheInfo = _db.Queryable().First(x => x.DeviceCode == entity.DeviceCode && x.Lane == info.lane);
//只有rfid并且没有无人机
if (info.rfid_1.IsNotEmptyOrNull() && info.uavcode.IsNullOrEmpty())
{
var order = _db.Queryable().OrderBy(x => x.CreateTime).First(x => x.RentCellId == CellCodeInfo.Id && x.Status == UavOrderStatusEnum.已租接.GetHashCode());
if (order == null)
{
//如果没有通过货道去找到这个订单,就开门,认为是在补货
string topic = $"device/{entity.DeviceCode}/uav";
string MqttMessage = new MqttContent
{
className = "OrderErrorOpen",
action = "open",
lane = info.lane,
orderNo = "",
instructionTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
tokenUrl = ""
}.ToJson();
await _mqttService.PublishAsync(topic, MqttMessage);
}
else
{
//查询出开门的缓存信息
if (OpenCacheInfo.IsNotEmptyOrNull())
{
OpenCacheInfo.Status = OrderDoorStatus.归还错误.GetHashCode();
OpenCacheInfo.Message += $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} :没有检测到无人机,归还错误。";
_db.Updateable(OpenCacheInfo).ExecuteCommand();
}
}
return false;
}
// else if (info.rfid_1.IsNullOrEmpty() && info.uavcode.IsNotEmptyOrNull())
// {
// //如果有rfid和无人机,那么就开门
// string topic = $"device/{entity.DeviceCode}/uav";
// string MqttMessage = new MqttContent
// {
// className = "OrderErrorOpen",
// action = "open",
// lane = info.lane,
// orderNo = info.orderNo,
// instructionTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
// tokenUrl = ""
// }.ToJson();
// await _mqttService.PublishAsync(topic, MqttMessage);
// }
//首先判断是否有无人机RFID,没有无人机ID的情况,一般是关门,把无人机拿走,有可能是订单也有可能是维修
if (info.uavcode.IsNullOrEmpty())
{
//如果是空,那么通过机柜和货道去找订单
var order = _db.Queryable().OrderBy(x => x.CreateTime).First(x => x.RentCellId == CellCodeInfo.Id && x.Status == UavOrderStatusEnum.已租接.GetHashCode());
if (order.IsNotEmptyOrNull())
{
Log.Information("1,进入订单修改机柜的状态");
await OrderSuccessUpdateStatus(order);
}
//如果订单是空,那么就认定是维修,可能是无人机被管理者拿出去了
if (order.IsNullOrEmpty())
{
await UavMaintain(CellCodeInfo);
}
}
//有无人机RFID的情况,正常来说是归还或者是补货
if (info.uavcode.IsNotEmptyOrNull())
{
Log.Information($"进入有无人机的订单处理");
//通过无人机code去找到订单
var order = _db.Queryable().OrderBy(x => x.CreateTime).First(x => x.AircraftId == info.uavcode && x.Status == UavOrderStatusEnum.已租接.GetHashCode());
if (order.IsNullOrEmpty())
{
await Replenishment(info);
}
if (order.IsNotEmptyOrNull())
{
//如果找到这个订单,那么就需要判断相关rfid和无人机是否一致
bool isSameRFIDPair = (order.RFID1 == info.rfid_1 && order.RFID2 == info.rfid_2) || (order.RFID1 == info.rfid_2 && order.RFID2 == info.rfid_1);
if (!isSameRFIDPair || order.AircraftId != info.uavcode)
{
// 信息不一致,执行你的处理逻辑
order.Remark += $"{DateTime.Now}:订单设备不一致,无法归还,不一致为:{order.RFID1}和{order.RFID2},当前设备为{info.rfid_1}和{info.rfid_2},无人机为{info.uavcode}。";
_db.Updateable(order).ExecuteCommand();
if (OpenCacheInfo.IsNotEmptyOrNull())
{
OpenCacheInfo.Status = OrderDoorStatus.归还错误.GetHashCode();
OpenCacheInfo.Message += $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} :订单设备不一致,无法归还,不一致为:{order.RFID1}和{order.RFID2},当前设备为{info.rfid_1}和{info.rfid_2},无人机为{info.uavcode}。";
_db.Updateable(OpenCacheInfo).ExecuteCommand();
}
}
else
{
//订单设备一致,就去关闭订单
await OrderReturnStatus(order, info);
}
}
}
// string topic = $"device/{entity.DeviceCode}/ack";
// string Returnmessage = new
// {
// ask = "success"
// }.ToJson();
// await _mqttService.PublishAsync(topic, message);
return true;
}
#endregion
#region 无人机维护
///
/// 无人机维护
///
///
///
[NonAction]
public async Task UavMaintain(UavDeviceCellEntity CellCodeInfo)
{
//var Aircraft = await _db.Queryable().Where(x => x.CellId == CellCodeInfo.Id).FirstAsync();
//首先把格子状态改为空闲
CellCodeInfo.Rfid1 = "";
CellCodeInfo.Rfid2 = "";
CellCodeInfo.UavCode = "";
CellCodeInfo.Status = DeviceCellStatusEnum.空闲.GetHashCode();
await _db.Updateable(CellCodeInfo).ExecuteCommandAsync();
//通过设备编号,货到编号,去查询订单开门缓存表,并且状态是已开门的
var OpenCacheInfo = _db.Queryable().First(x => x.DeviceCode == CellCodeInfo.DeviceId && x.Lane == CellCodeInfo.CellCode && x.Status == OrderDoorStatus.已开门.GetHashCode());
if (OpenCacheInfo.IsNotEmptyOrNull())
{
//没有放无人机进去,修改状态是归还错误
OpenCacheInfo.Status = OrderDoorStatus.归还错误.GetHashCode();
OpenCacheInfo.Message += $"{DateTime.Now}:归还没有放无人机";
_db.Updateable(OpenCacheInfo).ExecuteCommand();
}
//然后把格子绑定的无人机相关的绑定取消
// Aircraft.DeviceId = string.Empty;
// Aircraft.CellId = string.Empty;
// Aircraft.Status = UavAircraftStatusEnum.维修.GetHashCode();
// await _db.Updateable(Aircraft).ExecuteCommandAsync();
}
#endregion
#region 订单下单成功修改状态
///
/// OrderSuccessUpdateStatus
///
///
///
[NonAction]
public async Task OrderSuccessUpdateStatus(UavOrderEntity orderInfo)
{
// var UavInfo = _db.Queryable().Where(x => x.Id == orderInfo.AircraftId).First();
// UavInfo.Status = UavAircraftStatusEnum.租赁中.GetHashCode();
// UavInfo.DeviceId = "";//租赁中的无人机就不在柜子里面了
// UavInfo.CellId = "";//租赁中的无人机就不在柜子里面了
// await _db.Updateable(UavInfo).ExecuteCommandAsync();//无人机状态改为租赁中
var DeviceCellInfo = _db.Queryable().Where(x => x.Id == orderInfo.RentCellId).First();//找到租赁的货道
DeviceCellInfo.Rfid1 = "";
DeviceCellInfo.Rfid2 = "";
DeviceCellInfo.UavCode = "";
DeviceCellInfo.Status = DeviceCellStatusEnum.空闲.GetHashCode();//更新货道为空闲,后续可以进行归还
await _db.Updateable(DeviceCellInfo).ExecuteCommandAsync();
var OpenCacheInfo = _db.Queryable().First(x => x.OrderNo == orderInfo.OrderNo && x.Status == OrderDoorStatus.已开门.GetHashCode());
if (OpenCacheInfo.IsNotEmptyOrNull())
{
//没有放无人机进去,修改状态是归还错误
OpenCacheInfo.Status = OrderDoorStatus.归还错误.GetHashCode();
OpenCacheInfo.Message += $"{DateTime.Now}:归还没有放无人机";
_db.Updateable(OpenCacheInfo).ExecuteCommand();
}
}
#endregion
#region 订单归还无人机
///
/// 订单归还无人机
///
///
///
///
[NonAction]
public async Task OrderReturnStatus(UavOrderEntity orderInfo, UavDeviceOprnNotifyCrInput Notifyinput)
{
Log.Information($"进入订单完结操作----1");
//根据设备货道去查询货道id,找到退租的设备货道信息
var DeviceCellInfo = _db.Queryable().Where(x => x.DeviceId == Notifyinput.id && x.CellCode == Notifyinput.lane).First();
Log.Information($"进入订单完结操作----2");
//根据设备去查询场所id,找到设备的场所信息
var DeviceInfo = _db.Queryable().Where(x => x.Id == Notifyinput.id).First();
Log.Information($"进入订单完结操作----3");
//如果关门成功,就进行订单完成的操作
orderInfo.ReturnDeviceId = Notifyinput.id;//设置退租设备id
orderInfo.ReturnCellId = DeviceCellInfo.Id;
orderInfo.ReturnSiteId = DeviceInfo.SiteId;
orderInfo.EndTime = DateTime.Now;
UavFeeRuleEntity uavFee = orderInfo.FeeRuleSnapshot.ToObject().Adapt();//获取快照中的费用规则
Log.Information($"进入订单完结操作----4-----{uavFee.ToJson()}");
var TimeSpan = orderInfo.EndTime.ToDate() - orderInfo.StartTime.ToDate();
var TimeSpanMinute = TimeSpan.TotalMilliseconds;
var Price = CalculateFee(uavFee, TimeSpan.TotalSeconds.ToInt());
_db.Updateable(orderInfo).IgnoreColumns(it => new { it.Status }).ExecuteCommand();
var ActualAmount = decimal.Parse(Price.ToString());
DeviceCellInfo.Rfid1 = Notifyinput.rfid_1;
DeviceCellInfo.Rfid2 = Notifyinput.rfid_2;
DeviceCellInfo.UavCode = Notifyinput.uavcode;
DeviceCellInfo.Status = DeviceCellStatusEnum.充电.GetHashCode();//更新货道为充电,来表示可以对外租赁
DeviceCellInfo.UpdateTime = DateTime.Now;//更新货道更新时间,更新时间来表示充电开始时间
_db.Updateable(DeviceCellInfo).ExecuteCommand();
//最后通知去芝麻信用去扣款
if (ActualAmount == 0)
{
//如果金额是0,则不需要去扣款,直接取消预授权
await _aliPayService.liPayOperationCance(new AliPayOperationCancelEntity
{
OutOrderNo = orderInfo.OrderNo,
OutRequestNo = orderInfo.OrderNo,
Remark = "免费时间内归还"
});
var OpenCacheInfo = _db.Queryable().First(x => x.OrderNo == orderInfo.OrderNo);
if (OpenCacheInfo.IsNotEmptyOrNull())
{
OpenCacheInfo.Status = OrderDoorStatus.已关门.GetHashCode();
OpenCacheInfo.Message += $"{DateTime.Now}:免费订单完结关门";
_db.Updateable(OpenCacheInfo).ExecuteCommand();
}
}
else
{
await _aliPayService.TradePay(new AliPayTradePayEntity
{
OutTradeNo = orderInfo.OrderNo,
TotalAmount = ActualAmount,
Subject = "无人机租借"
});
}
}
#endregion
#region 费用计算
///
/// 计算费用方法:根据套餐规则和实际使用秒数,计算最终费用
///
/// 套餐规则实体
/// 使用时长(单位:秒)
/// 应收费用(单位:元)
[NonAction]
public decimal CalculateFee(UavFeeRuleEntity info, int usedSeconds)
{
Log.Information($"进入订单完结操作----5(费用计算)");
// 如果使用时间小于等于0秒,费用为0
if (usedSeconds <= 0)
return 0m;
// 获取套餐类型(minute 或 hour),默认为 minute
string packageType = info.PackageType?.ToLower() ?? "minute";
// 获取免费时长(单位:秒),如果未设置默认为0
int freeSeconds = info.FreeDuration ?? 0;
// 如果实际使用时间未超过免费时长,则不计费
if (usedSeconds <= freeSeconds)
return 0m;
// 不需要扣除免费时长,免费时长已经包含在基础的套餐内
int chargeableSeconds = usedSeconds;
// 最终按“分钟”为单位进行计费(包括小时套餐),转换为计费单位数
int chargeableUnits = (int)Math.Ceiling(chargeableSeconds / 60.0);
// 计算套餐基础时长(单位:分钟)
// 若是小时套餐,则 Duration * 60,转成分钟
int baseUnits = packageType switch
{
"minute" => info.Duration ?? 0,
"hour" => info.Duration.HasValue ? info.Duration.Value * 60 : 0,
_ => throw new InvalidOperationException("未知的套餐类型")
};
if (baseUnits <= 0)
throw new InvalidOperationException("套餐时长未配置");
// 获取价格设置
decimal unitPrice = info.UnitPrice; // 基础套餐费用
decimal overtimeUnitPrice = info.OvertimePrice; // 超时每单位费用(分钟/小时)
decimal dailyCap = info.DailyCap; // 日封顶
decimal totalCap = info.TotalCap; // 总封顶
// 每天的分钟数为 1440(24小时)
int minutesPerDay = 1440;
// 初始化总费用
decimal totalFee = 0;
// 计算所有使用时长的总分钟数(用于分天计费)
int totalMinutes = chargeableUnits;
// 计算完整的使用天数
int fullDays = totalMinutes / minutesPerDay;
// 计算最后一天的剩余分钟数
int remainingMinutes = totalMinutes % minutesPerDay;
//按天计算费用,考虑是否超时及超时计价方式
decimal CalcDailyFee(int minutesInDay)
{
if (minutesInDay <= baseUnits)
return unitPrice;
// 超过套餐时间部分
int overtime = minutesInDay - baseUnits;
// 超时单位转换:按分钟或小时计费
int overtimeUnits = packageType switch
{
"minute" => overtime,
"hour" => (int)Math.Ceiling(overtime / 60.0),
_ => 0
};
return unitPrice + overtimeUnits * overtimeUnitPrice;
}
// 计算每一天的费用(如果有多天),每天不超过日封顶
for (int i = 0; i < fullDays; i++)
{
totalFee += Math.Min(CalcDailyFee(minutesPerDay), dailyCap);
}
// 计算最后一天的费用(不足一天的分钟数)
if (remainingMinutes > 0)
{
totalFee += Math.Min(CalcDailyFee(remainingMinutes), dailyCap);
}
// 总费用不超过总封顶金额
if (totalCap > 0 && totalFee > totalCap)
{
totalFee = totalCap;
}
// 最终返回保留两位小数的金额
return Math.Round(totalFee, 2);
}
#endregion
#region 补货
///
/// 补货
///
///
///
[NonAction]
public async Task Replenishment(UavDeviceOprnNotifyCrInput input)
{
var result = await _db.Ado.UseTranAsync(async () =>
{
var deviceInfo = await _db.Queryable().FirstAsync(x => x.Id == input.id);
if (input.rfid_1 == "000000")
{
input.rfid_1 = "";
}
if (input.rfid_2 == "000000")
{
input.rfid_2 = "";
}
// 检测并清空重复的无人机信息
await ClearDuplicateUavInfo(input.uavcode, input.rfid_1, input.rfid_2, input.id, input.lane);
//添加电池1
await HandleReplenishment(input.rfid_1, input, "电池", deviceInfo.BelongUserId);
//添加电池2
await HandleReplenishment(input.rfid_2, input, "电池", deviceInfo.BelongUserId);
//添加无人机
await HandleReplenishment(input.uavcode, input, "无人机", deviceInfo.BelongUserId);
var cell = await _db.Queryable().FirstAsync(x => x.DeviceId == input.id && x.CellCode == input.lane);
//更新货道信息
cell.Rfid1 = input.rfid_1;
cell.Rfid2 = input.rfid_2;
cell.UavCode = input.uavcode;
cell.Status = DeviceCellStatusEnum.充电.GetHashCode();
cell.UpdateTime = DateTime.Now;
await _db.Updateable(cell).ExecuteCommandAsync();
});
if (!result.IsSuccess)
{
Log.Error("补货失败:" + result.ErrorMessage);
throw new Exception("补货失败:" + result.ErrorMessage);
}
}
[NonAction]
private async Task HandleReplenishment(string rfid, UavDeviceOprnNotifyCrInput input, string type, string BelongUserId)
{
if (string.IsNullOrWhiteSpace(rfid)) return;
var uav = await _db.Queryable().FirstAsync(x => x.UavFrid == rfid);
if (uav.IsNullOrEmpty() && rfid != "000000" && rfid != "00000000")
{
// 插入新无人机
var newUav = new UavAircraftEntity
{
Id = YitIdHelper.NextId().ToString(),
DeviceId = input.id,
Status = UavAircraftStatusEnum.空闲.GetHashCode(),
CreateTime = DateTime.Now,
UavFrid = rfid,
Model = type,
UavBatteryFrid = "",
AgentId = BelongUserId,
};
await _db.Insertable(newUav).ExecuteCommandAsync();
}
}
///
/// 检测并清空重复的无人机信息
///
/// 无人机编码
/// 电池1 RFID
/// 电池2 RFID
/// 当前设备ID
/// 当前货道
///
[NonAction]
private async Task ClearDuplicateUavInfo(string uavCode, string rfid1, string rfid2, string currentDeviceId, string currentLane)
{
try
{
// 主要检测无人机编码重复,如果重复则清空对应的RFID信息
if (!string.IsNullOrWhiteSpace(uavCode))
{
var duplicateUavCells = await _db.Queryable().Where(x => x.UavCode == uavCode && !(x.DeviceId == currentDeviceId && x.CellCode == currentLane)).ToListAsync();
if (duplicateUavCells.Any())
{
foreach (var cell in duplicateUavCells)
{
// 清空无人机编码和对应的RFID信息
cell.UavCode = "";
cell.Rfid1 = "";
cell.Rfid2 = "";
cell.UpdateTime = DateTime.Now;
}
await _db.Updateable(duplicateUavCells).ExecuteCommandAsync();
Log.Information($"清空了 {duplicateUavCells.Count} 个货道中重复的无人机编码及RFID信息: {uavCode}");
}
}
}
catch (Exception ex)
{
Log.Error($"清空重复无人机信息失败: {ex.Message}");
throw new Exception($"清空重复无人机信息失败: {ex.Message}");
}
}
#endregion
#region 订单下单开门
///
/// 订单下单开门
///
///
///
///
[NonAction]
public async Task OrderCheckOpenDoor(UavDeviceOpenCrInput input, string orderNo)
{
var entity = await _db.Queryable().FirstAsync(p => p.Id == input.id);
string topic = $"device/{entity.DeviceCode}/uav";
string message = new MqttContent
{
className = "OrderOpen",
action = "open",
lane = input.lane,
orderNo = orderNo,
instructionTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
tokenUrl = ""
}.ToJson();
await _mqttService.PublishAsync(topic, message);
return new { code = 200, msg = "开门指令已发送" };
}
#endregion
#region 订单完成开门
///
/// 订单下单开门
///
///
///
///
[NonAction]
public async Task CloseOrderAndUnlockAsync(UavDeviceOpenCrInput input, string orderNo)
{
var entity = await _db.Queryable().FirstAsync(p => p.Id == input.id);
string topic = $"device/{entity.DeviceCode}/uav";
string message = new MqttContent
{
className = "OrderEnd",
action = "open",
lane = input.lane,
orderNo = orderNo,
instructionTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
tokenUrl = "/api/Extend/UavOrder/orderCloseNotify"
}.ToJson();
await _mqttService.PublishAsync(topic, message);
return new { code = 200, msg = "开门指令已发送" };
}
#endregion
#region 获取用户订单金额的数据
///
/// 获取用户订单金额的数据
///
///
[HttpGet("GetUserOrderTotalAmount")]
public async Task GetUserOrderTotalAmount()
{
var now = DateTime.Now;
var today = now.Date;
var yesterday = today.AddDays(-1);
var monthStart = new DateTime(now.Year, now.Month, 1);
var agentId = _userManager.UserId;
var statusCompleted = UavOrderStatusEnum.已完成.GetHashCode();
var statusRenting = UavOrderStatusEnum.已租接.GetHashCode();
// 昨天
var yesterdayRevenue = await _db.Queryable().Where(x => x.Status == statusCompleted && x.AgentId == agentId && x.CreateTime >= yesterday).SumAsync(x => x.ShareAmount);
var yesterdayCount = await _db.Queryable().Where(x => x.Status == statusCompleted && x.AgentId == agentId && x.CreateTime >= yesterday).CountAsync();
var yesterdayRentingCount = await _db.Queryable().Where(x => x.Status == statusRenting && x.AgentId == agentId && x.CreateTime >= yesterday).CountAsync();
// 今天
var todayRevenue = await _db.Queryable()
.Where(x => x.Status == statusCompleted && x.AgentId == agentId && x.CreateTime >= today)
.SumAsync(x => x.ShareAmount);
var todayCount = await _db.Queryable()
.Where(x => x.Status == statusCompleted && x.AgentId == agentId && x.CreateTime >= today)
.CountAsync();
var todayRentingCount = await _db.Queryable()
.Where(x => x.Status == statusRenting && x.AgentId == agentId && x.CreateTime >= today)
.CountAsync();
// 本月
var thisMonthRevenue = await _db.Queryable()
.Where(x => x.Status == statusCompleted && x.AgentId == agentId && x.CreateTime >= monthStart)
.SumAsync(x => x.ShareAmount);
var thisMonthCount = await _db.Queryable()
.Where(x => x.Status == statusCompleted && x.AgentId == agentId && x.CreateTime >= monthStart)
.CountAsync();
var thisMonthRentingCount = await _db.Queryable()
.Where(x => x.Status == statusRenting && x.AgentId == agentId && x.CreateTime >= monthStart)
.CountAsync();
// 构造结果
return new
{
yesterday = new
{
revenue = yesterdayRevenue,
count = yesterdayCount,
rentingCount = yesterdayRentingCount
},
today = new
{
revenue = todayRevenue,
count = todayCount,
rentingCount = todayRentingCount
},
thisMonth = new
{
revenue = thisMonthRevenue,
count = thisMonthCount,
rentingCount = thisMonthRentingCount
}
};
}
#endregion
#region C端用户获取我的订单
///
/// C端用户获取我的订单
///
///
[HttpGet("GetMyOrders")]
public async Task GetMyOrders([FromQuery] UavOrderListQueryInput input)
{
var UserId = _userManager.UserId;
var sidx = input.sidx == null ? "id" : input.sidx;
var data = await _db.Queryable()
.WhereIF(!string.IsNullOrEmpty(input.orderNo), p => p.OrderNo.Contains(input.orderNo))
.Where(p => p.UserAccount == UserId)
.Select(it => new UavOrderListOutput
{
id = it.Id,
orderNo = it.OrderNo,
deviceId = it.DeviceId,
deviceName = SqlFunc.Subqueryable().Where(p => p.Id == it.DeviceId).Select(p => p.DeviceName),
siteId = it.SiteId,
siteName = SqlFunc.Subqueryable().Where(p => p.Id == it.SiteId).Select(p => p.SiteName),
aircraftId = it.AircraftId,
userAccount = it.UserAccount,
userPhone = it.UserPhone,
actualAmount = it.ActualAmount,
rentCellId = it.RentCellId,
returnCellId = it.ReturnCellId,
status = it.Status,
remark = it.Remark,
createTime = it.CreateTime,
startTime = it.StartTime,
endTime = it.EndTime,
feeRuleId = it.FeeRuleId,
aliAuthNo = it.AliAuthNo,
returnDeviceId = it.ReturnDeviceId,
returnSiteId = it.ReturnSiteId,
rfid1 = it.RFID1,
rfid2 = it.RFID2,
}).MergeTable().Mapper(p =>
{
p.statusString = global::System.Enum.GetName(typeof(UavOrderStatusEnum), p.status);
}).OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize);
return PageResult.SqlSugarPageResult(data);
}
#endregion
#region 获取某个设备产生的订单
///
/// 获取某个设备产生的订单
///
///
///
[HttpGet("GetOrderByDevice")]
public async Task GetOrderByDevice([FromQuery] UavOrderListQueryInput input)
{
var sidx = input.sidx == null ? "id" : input.sidx;
var data = await _db.Queryable()
.Where(p => p.AgentId == _userManager.UserId)
.WhereIF(!string.IsNullOrEmpty(input.orderNo), p => p.OrderNo.Contains(input.orderNo))
.WhereIF(!string.IsNullOrEmpty(input.createTime_start), p => p.CreateTime >= new DateTime(input.createTime_start.ToDate().Year, input.createTime_start.ToDate().Month, input.createTime_start.ToDate().Day, 0, 0, 0) && p.CreateTime <= new DateTime(input.createTime_end.ToDate().Year, input.createTime_end.ToDate().Month, input.createTime_end.ToDate().Day, 23, 59, 59))
.Select(it => new UavOrderListOutput
{
id = it.Id,
orderNo = it.OrderNo,
deviceId = it.DeviceId,
siteId = it.SiteId,
aircraftId = it.AircraftId,
agentId = it.AgentId,
userAccount = it.UserAccount,
userPhone = it.UserPhone,
userName = it.UserName,
discountAmount = it.DiscountAmount,
actualAmount = it.ActualAmount,
shareRatio = it.ShareRatio,
shareAmount = it.ShareAmount,
finalAmount = it.FinalAmount,
rentCellId = it.RentCellId,
returnCellId = it.ReturnCellId,
status = it.Status,
remark = it.Remark,
endTime = it.EndTime,
startTime = it.StartTime,
createTime = it.CreateTime,
}).MergeTable().Mapper(p =>
{
p.statusString = global::System.Enum.GetName(typeof(UavOrderStatusEnum), p.status);
}).OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize);
return PageResult.SqlSugarPageResult(data);
}
#endregion
#region 获取某个代理商的每天出货量
///
/// 获取某个代理商的每天出货量
///
///
[HttpGet("GetFlowByDay/{agentId}")]
public async Task GetFlowByDay(string agentId)
{
var data = await _db.Queryable()
.Where(p =>
p.AgentId == agentId
&& p.Status == UavOrderStatusEnum.已完成.GetHashCode()
&& p.CreateTime >= DateTime.Now.AddDays(-14)
&& p.CreateTime <= DateTime.Now
)
.GroupBy(p => SqlFunc.DateValue(p.CreateTime.Value, SqlSugar.DateType.Day))
.Select(p => new
{
date = SqlFunc.DateValue(p.CreateTime.Value, SqlSugar.DateType.Day),
count = SqlFunc.AggregateCount(p.Id)
})
.ToListAsync();
return data;
}
#endregion
#region 取消三分钟前未付款的订单,并且把订单的订单状态改为取消
///
/// 取消三分钟前未付款的订单
///
///
[HttpGet("CancelOrder")]
[AllowAnonymous]
public async Task CancelOrder()
{
try
{
// 获取3分钟前的时间
var timeLimit = DateTime.Now.AddMinutes(-3);
// 使用事务处理
return await _db.UseTranAsync(async () =>
{
// 查找需要取消的订单
var orders = await _db.Queryable()
.Where(x => x.Status == UavOrderStatusEnum.待付款.GetHashCode()
&& x.CreateTime <= timeLimit)
.ToListAsync();
foreach (var order in orders)
{
// 更新订单状态为已取消
order.Status = UavOrderStatusEnum.超时未付款.GetHashCode();
order.EndTime = DateTime.Now;
order.Remark = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} 系统自动取消超时未支付订单";
await _db.Updateable(order).ExecuteCommandAsync();
// 获取并更新货道状态
var deviceCell = await _db.Queryable().With(SqlSugar.SqlWith.RowLock).FirstAsync(x => x.Id == order.RentCellId);
if (deviceCell != null)
{
// 恢复货道状态为充电状态,表示可以继续租借
//如果已经是充电状态,则不处理
if (deviceCell.Status != DeviceCellStatusEnum.充电.GetHashCode())
{
deviceCell.Status = DeviceCellStatusEnum.充电.GetHashCode();
await _db.Updateable(deviceCell).ExecuteCommandAsync();
}
}
// 记录日志
Log.Information($"已自动取消超时订单:{order.OrderNo}");
}
return new
{
code = 200,
msg = $"成功取消{orders.Count}个超时订单",
data = orders.Select(x => new
{
orderNo = x.OrderNo,
createTime = x.CreateTime
}).ToList()
};
});
}
catch (Exception ex)
{
Log.Error($"取消超时订单失败:{ex.Message}");
return new { code = 500, msg = "取消超时订单失败" };
}
}
#endregion
#region 手动完结订单
///
/// 手动完结订单并扣费(货道状态和无人机状态不变,直接完结订单)
///
/// 订单编号
///
[HttpPost("FinishOrder/{orderNo}")]
public async Task FinishOrder(string orderNo)
{
try
{
return await _db.UseTranAsync(async () =>
{
// 1. 查询订单信息
var order = await _db.Queryable().FirstAsync(x => x.OrderNo == orderNo && x.Status == UavOrderStatusEnum.已租接.GetHashCode());
if (order == null)
{
throw new Exception($"未找到进行中的订单:{orderNo}");
}
// 2. 更新订单结束时间和状态
order.EndTime = DateTime.Now;
order.Status = UavOrderStatusEnum.已完成.GetHashCode();
order.Remark = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} 管理员手动完结订单";
// 3. 获取费用规则并计算费用
var uavFee = order.FeeRuleSnapshot.ToObject().Adapt();
var timeSpan = order.EndTime.Value - order.StartTime.Value;
var actualAmount = CalculateFee(uavFee, timeSpan.TotalSeconds.ToInt());
// 4. 更新订单金额
order.ActualAmount = actualAmount;
// 5. 更新设备和货道状态
// var deviceCell = await _db.Queryable() .FirstAsync(x => x.Id == order.RentCellId);
// if (deviceCell != null)
// {
// deviceCell.Status = DeviceCellStatusEnum.充电.GetHashCode();
// await _db.Updateable(deviceCell).ExecuteCommandAsync();
// }
// // 6. 更新无人机状态
// if (!string.IsNullOrEmpty(order.AircraftId))
// {
// var aircraft = await _db.Queryable()
// .FirstAsync(x => x.Id == order.AircraftId);
// if (aircraft != null)
// {
// aircraft.Status = UavAircraftStatusEnum.空闲.GetHashCode();
// await _db.Updateable(aircraft).ExecuteCommandAsync();
// }
// }
// 7. 处理支付
if (actualAmount > 0)
{
await _aliPayService.TradePay(new AliPayTradePayEntity
{
OutTradeNo = order.OrderNo,
TotalAmount = actualAmount,
Subject = "无人机租借-手动完结"
});
}
else
{
// 金额为0,直接取消预授权
await _aliPayService.liPayOperationCance(new AliPayOperationCancelEntity
{
OutOrderNo = order.OrderNo,
OutRequestNo = order.OrderNo,
Remark = "管理员手动完结-免费订单"
});
}
// 8. 更新订单状态
await _db.Updateable(order).ExecuteCommandAsync();
return new
{
code = 200,
msg = "订单手动完结成功",
data = new
{
orderNo = order.OrderNo,
amount = actualAmount,
startTime = order.StartTime,
endTime = order.EndTime,
duration = timeSpan.TotalMinutes
}
};
});
}
catch (Exception ex)
{
Log.Error($"手动完结订单失败:{ex.Message}");
return new { code = 500, msg = $"手动完结订单失败:{ex.Message}" };
}
}
#endregion
}
}