feat: 客户发件地址与揽收订单,复用小包创建全链路。
新增地址簿 CRUD、pickup/create、订单 orderSource 筛选;附带包裹商品图片上传接口。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
4b7c396d92
commit
341a318f0b
35
db.sql
35
db.sql
@ -133,6 +133,9 @@ CREATE TABLE `t_order` (
|
||||
`PreviousOrderStatus` VARCHAR(32) NULL COMMENT '进入异常状态前的订单状态',
|
||||
`WarehouseId` INT NULL COMMENT '仓库ID',
|
||||
`CustomerId` INT NULL COMMENT '客户ID',
|
||||
`OrderSource` VARCHAR(32) NULL COMMENT '订单来源:manual/pickup/upload/batch/platform',
|
||||
`PickupTime` DATETIME NULL COMMENT '预约揽收时间',
|
||||
`CustomerSenderAddressId` INT NULL COMMENT '发件地址簿 Id',
|
||||
`OrganizationId` CHAR(36) NULL COMMENT '数据归属组织',
|
||||
`SecretLevel` TINYINT NOT NULL DEFAULT 2 COMMENT '数据密级 1-4',
|
||||
`SenderId` INT NULL COMMENT '发件人ID',
|
||||
@ -168,9 +171,39 @@ CREATE TABLE `t_order` (
|
||||
KEY `idx_ord_sender` (`SenderId`),
|
||||
KEY `idx_ord_recipient` (`RecipientId`),
|
||||
KEY `idx_ord_declaration` (`DeclarationId`),
|
||||
KEY `idx_ord_org_secret_ctime` (`OrganizationId`, `SecretLevel`, `CreateTime`)
|
||||
KEY `idx_ord_org_secret_ctime` (`OrganizationId`, `SecretLevel`, `CreateTime`),
|
||||
KEY `idx_ord_source_ctime` (`OrderSource`, `CreateTime`)
|
||||
) COMMENT='订单表';
|
||||
|
||||
CREATE TABLE `t_customer_sender_address` (
|
||||
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`CustomerId` INT NOT NULL COMMENT '归属客户 t_customer.Id',
|
||||
`AddressName` VARCHAR(64) NULL COMMENT '地址别名',
|
||||
`SenderName` VARCHAR(50) NOT NULL COMMENT '发件人姓名',
|
||||
`Company` VARCHAR(255) NULL COMMENT '公司',
|
||||
`Country` INT NULL COMMENT '国家 t_country.Id',
|
||||
`Province` VARCHAR(50) NULL COMMENT '州省',
|
||||
`City` VARCHAR(50) NULL COMMENT '城市',
|
||||
`ZipCode` VARCHAR(50) NULL COMMENT '邮编',
|
||||
`StreetAddress1` VARCHAR(500) NOT NULL COMMENT '地址1',
|
||||
`StreetAddress2` VARCHAR(500) NULL COMMENT '地址2',
|
||||
`StreetAddress3` VARCHAR(500) NULL COMMENT '地址3',
|
||||
`HouseNumber` VARCHAR(50) NULL COMMENT '门牌号',
|
||||
`Phone` VARCHAR(50) NULL COMMENT '手机',
|
||||
`TelePhone` VARCHAR(50) NULL COMMENT '电话',
|
||||
`Email` VARCHAR(60) NULL COMMENT '邮箱',
|
||||
`IsDefault` TINYINT NOT NULL DEFAULT 0 COMMENT '是否默认:0-否,1-是',
|
||||
`IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是',
|
||||
`ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳',
|
||||
`CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人',
|
||||
`CreateTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`UpdateUserId` INT NULL COMMENT '更新人',
|
||||
`UpdateTime` DATETIME NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
KEY `idx_csa_customer` (`CustomerId`, `IsDeleted`, `Id`),
|
||||
KEY `idx_csa_customer_default` (`CustomerId`, `IsDefault`)
|
||||
) COMMENT='客户发件地址簿';
|
||||
|
||||
CREATE TABLE `t_order_template` (
|
||||
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键,自增,步长 1',
|
||||
`Name` VARCHAR(64) NOT NULL COMMENT '模板名称',
|
||||
|
||||
32
sql/migrate_t_customer_sender_address.sql
Normal file
32
sql/migrate_t_customer_sender_address.sql
Normal file
@ -0,0 +1,32 @@
|
||||
-- 已有库增量:客户发件地址簿 t_customer_sender_address
|
||||
-- 对应功能:客户 → 发件地址;揽收订单选址;见 oms系统设计-050801.md §4.2.10
|
||||
-- 可重复执行(IF NOT EXISTS)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `t_customer_sender_address` (
|
||||
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`CustomerId` INT NOT NULL COMMENT '归属客户 t_customer.Id',
|
||||
`AddressName` VARCHAR(64) NULL COMMENT '地址别名',
|
||||
`SenderName` VARCHAR(50) NOT NULL COMMENT '发件人姓名',
|
||||
`Company` VARCHAR(255) NULL COMMENT '公司',
|
||||
`Country` INT NULL COMMENT '国家 t_country.Id',
|
||||
`Province` VARCHAR(50) NULL COMMENT '州省',
|
||||
`City` VARCHAR(50) NULL COMMENT '城市',
|
||||
`ZipCode` VARCHAR(50) NULL COMMENT '邮编',
|
||||
`StreetAddress1` VARCHAR(500) NOT NULL COMMENT '地址1',
|
||||
`StreetAddress2` VARCHAR(500) NULL COMMENT '地址2',
|
||||
`StreetAddress3` VARCHAR(500) NULL COMMENT '地址3',
|
||||
`HouseNumber` VARCHAR(50) NULL COMMENT '门牌号',
|
||||
`Phone` VARCHAR(50) NULL COMMENT '手机',
|
||||
`TelePhone` VARCHAR(50) NULL COMMENT '电话',
|
||||
`Email` VARCHAR(60) NULL COMMENT '邮箱',
|
||||
`IsDefault` TINYINT NOT NULL DEFAULT 0 COMMENT '是否默认:0-否,1-是',
|
||||
`IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是',
|
||||
`ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳',
|
||||
`CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人',
|
||||
`CreateTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`UpdateUserId` INT NULL COMMENT '更新人',
|
||||
`UpdateTime` DATETIME NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`Id`),
|
||||
KEY `idx_csa_customer` (`CustomerId`, `IsDeleted`, `Id`),
|
||||
KEY `idx_csa_customer_default` (`CustomerId`, `IsDefault`)
|
||||
) COMMENT='客户发件地址簿';
|
||||
75
sql/migrate_t_order_pickup_fields.sql
Normal file
75
sql/migrate_t_order_pickup_fields.sql
Normal file
@ -0,0 +1,75 @@
|
||||
-- 已有库增量:t_order 揽收订单字段 OrderSource / PickupTime / CustomerSenderAddressId
|
||||
-- 对应功能:订单 → 揽收订单;见 oms系统设计-050801.md §4.2.1 / §4.4.4
|
||||
-- 可重复执行
|
||||
|
||||
SET @col_exists := (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 't_order'
|
||||
AND COLUMN_NAME = 'OrderSource'
|
||||
);
|
||||
|
||||
SET @sql := IF(
|
||||
@col_exists = 0,
|
||||
'ALTER TABLE `t_order` ADD COLUMN `OrderSource` VARCHAR(32) NULL COMMENT ''订单来源:manual/pickup/upload/batch/platform'' AFTER `CustomerId`',
|
||||
'SELECT ''t_order.OrderSource already exists'' AS info'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @col_exists := (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 't_order'
|
||||
AND COLUMN_NAME = 'PickupTime'
|
||||
);
|
||||
|
||||
SET @sql := IF(
|
||||
@col_exists = 0,
|
||||
'ALTER TABLE `t_order` ADD COLUMN `PickupTime` DATETIME NULL COMMENT ''预约揽收时间'' AFTER `OrderSource`',
|
||||
'SELECT ''t_order.PickupTime already exists'' AS info'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @col_exists := (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 't_order'
|
||||
AND COLUMN_NAME = 'CustomerSenderAddressId'
|
||||
);
|
||||
|
||||
SET @sql := IF(
|
||||
@col_exists = 0,
|
||||
'ALTER TABLE `t_order` ADD COLUMN `CustomerSenderAddressId` INT NULL COMMENT ''发件地址簿 Id'' AFTER `PickupTime`',
|
||||
'SELECT ''t_order.CustomerSenderAddressId already exists'' AS info'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
UPDATE `t_order`
|
||||
SET `OrderSource` = 'manual'
|
||||
WHERE `OrderSource` IS NULL OR `OrderSource` = '';
|
||||
|
||||
SET @idx_exists := (
|
||||
SELECT COUNT(1)
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 't_order'
|
||||
AND INDEX_NAME = 'idx_ord_source_ctime'
|
||||
);
|
||||
|
||||
SET @sql := IF(
|
||||
@idx_exists = 0,
|
||||
'ALTER TABLE `t_order` ADD KEY `idx_ord_source_ctime` (`OrderSource`, `CreateTime`)',
|
||||
'SELECT ''idx_ord_source_ctime already exists'' AS info'
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
159
src/Oms.Application/Dtos/Orders/CustomerSenderAddressDtos.cs
Normal file
159
src/Oms.Application/Dtos/Orders/CustomerSenderAddressDtos.cs
Normal file
@ -0,0 +1,159 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Oms.Application.Dtos.Orders;
|
||||
|
||||
/// <summary>
|
||||
/// 客户发件地址分页查询。
|
||||
/// </summary>
|
||||
public class CustomerSenderAddressPaginatedInput
|
||||
{
|
||||
[Newtonsoft.Json.JsonProperty("page")]
|
||||
public int Page { get; set; } = 1;
|
||||
|
||||
[Newtonsoft.Json.JsonProperty("pageSize")]
|
||||
public int PageSize { get; set; } = 30;
|
||||
|
||||
[Newtonsoft.Json.JsonProperty("keyword")]
|
||||
public string? Keyword { get; set; }
|
||||
|
||||
[Newtonsoft.Json.JsonProperty("onlyDefault")]
|
||||
public bool OnlyDefault { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增发件地址。
|
||||
/// </summary>
|
||||
public class CreateCustomerSenderAddressInput
|
||||
{
|
||||
[StringLength(64)]
|
||||
public string? AddressName { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(50)]
|
||||
public string SenderName { get; set; } = string.Empty;
|
||||
|
||||
[StringLength(255)]
|
||||
public string? Company { get; set; }
|
||||
|
||||
[StringLength(8)]
|
||||
public string? CountryCode { get; set; }
|
||||
|
||||
[StringLength(50)]
|
||||
public string? Province { get; set; }
|
||||
|
||||
[StringLength(50)]
|
||||
public string? City { get; set; }
|
||||
|
||||
[StringLength(50)]
|
||||
public string? ZipCode { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(500)]
|
||||
public string StreetAddress1 { get; set; } = string.Empty;
|
||||
|
||||
[StringLength(500)]
|
||||
public string? StreetAddress2 { get; set; }
|
||||
|
||||
[StringLength(500)]
|
||||
public string? StreetAddress3 { get; set; }
|
||||
|
||||
[StringLength(50)]
|
||||
public string? HouseNumber { get; set; }
|
||||
|
||||
[StringLength(50)]
|
||||
public string? Phone { get; set; }
|
||||
|
||||
[StringLength(50)]
|
||||
public string? TelePhone { get; set; }
|
||||
|
||||
[StringLength(60)]
|
||||
public string? Email { get; set; }
|
||||
|
||||
public bool IsDefault { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新发件地址。
|
||||
/// </summary>
|
||||
public class UpdateCustomerSenderAddressInput : CreateCustomerSenderAddressInput
|
||||
{
|
||||
[Required]
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发件地址出参。
|
||||
/// </summary>
|
||||
public class CustomerSenderAddressOutput
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public int CustomerId { get; set; }
|
||||
|
||||
public string? AddressName { get; set; }
|
||||
|
||||
public string SenderName { get; set; } = string.Empty;
|
||||
|
||||
public string? Company { get; set; }
|
||||
|
||||
public int? Country { get; set; }
|
||||
|
||||
public string? CountryCode { get; set; }
|
||||
|
||||
public string? Province { get; set; }
|
||||
|
||||
public string? City { get; set; }
|
||||
|
||||
public string? ZipCode { get; set; }
|
||||
|
||||
public string StreetAddress1 { get; set; } = string.Empty;
|
||||
|
||||
public string? StreetAddress2 { get; set; }
|
||||
|
||||
public string? StreetAddress3 { get; set; }
|
||||
|
||||
public string? HouseNumber { get; set; }
|
||||
|
||||
public string? Phone { get; set; }
|
||||
|
||||
public string? TelePhone { get; set; }
|
||||
|
||||
public string? Email { get; set; }
|
||||
|
||||
public bool IsDefault { get; set; }
|
||||
|
||||
public DateTime? CreateTime { get; set; }
|
||||
|
||||
public DateTime? UpdateTime { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建揽收订单入参。
|
||||
/// </summary>
|
||||
public class OrderPickupCreateInput
|
||||
{
|
||||
public string? RequestId { get; set; }
|
||||
|
||||
public string? ClientNo { get; set; }
|
||||
|
||||
[Required]
|
||||
public string ProductCode { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public int WarehouseId { get; set; }
|
||||
|
||||
[Required]
|
||||
public int SenderAddressId { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime PickupTime { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(200)]
|
||||
public string CargoName { get; set; } = string.Empty;
|
||||
|
||||
[Required]
|
||||
public decimal ActualWeight { get; set; }
|
||||
|
||||
public string? CargoAttribute { get; set; }
|
||||
}
|
||||
@ -11,6 +11,12 @@ public class OrderExportInput
|
||||
[Newtonsoft.Json.JsonProperty("status")]
|
||||
public string? Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单来源:all / manual / pickup 等。
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonProperty("orderSource")]
|
||||
public string? OrderSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关键词。
|
||||
/// </summary>
|
||||
|
||||
@ -95,6 +95,21 @@ public class OrderOutput
|
||||
/// </summary>
|
||||
public int? CustomerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单来源。
|
||||
/// </summary>
|
||||
public string? OrderSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预约揽收时间。
|
||||
/// </summary>
|
||||
public DateTime? PickupTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 发件地址簿 Id。
|
||||
/// </summary>
|
||||
public int? CustomerSenderAddressId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 幂等请求标识。
|
||||
/// </summary>
|
||||
|
||||
@ -31,6 +31,13 @@ public class OrderPaginatedInput
|
||||
[global::System.Text.Json.Serialization.JsonPropertyName("handleStatus")]
|
||||
public string? HandleStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单来源:all / manual / pickup / upload / batch / platform。
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonProperty("orderSource")]
|
||||
[global::System.Text.Json.Serialization.JsonPropertyName("orderSource")]
|
||||
public string? OrderSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关键词。
|
||||
/// </summary>
|
||||
|
||||
@ -119,6 +119,11 @@ public partial class OrderService
|
||||
? warehouse.WarehouseName
|
||||
: null,
|
||||
CustomerId = order.CustomerId,
|
||||
OrderSource = string.IsNullOrWhiteSpace(order.OrderSource)
|
||||
? OrderSourceCodes.Manual
|
||||
: order.OrderSource,
|
||||
PickupTime = order.PickupTime,
|
||||
CustomerSenderAddressId = order.CustomerSenderAddressId,
|
||||
RequestId = order.RequestId,
|
||||
ExportCustomsMode = order.ExportCustomsMode,
|
||||
PredeclareFailReason = string.IsNullOrWhiteSpace(order.PredeclareFailReason)
|
||||
|
||||
@ -276,6 +276,8 @@ public partial class OrderService
|
||||
return new OrderListFilter
|
||||
{
|
||||
Status = filter.Status,
|
||||
HandleStatus = filter.HandleStatus,
|
||||
OrderSource = filter.OrderSource,
|
||||
Keyword = filter.Keyword,
|
||||
CreateTimeStart = start,
|
||||
CreateTimeEnd = filter.CreateTimeEnd,
|
||||
|
||||
@ -40,9 +40,19 @@ public partial class OrderService
|
||||
private OrderAutoTriggerService OrderAutoTriggerService =>
|
||||
LazyServiceProvider.GetRequiredService<OrderAutoTriggerService>();
|
||||
|
||||
private sealed class ParcelCreateContext
|
||||
{
|
||||
public string? OrderSource { get; init; }
|
||||
|
||||
public DateTime? PickupTime { get; init; }
|
||||
|
||||
public int? CustomerSenderAddressId { get; init; }
|
||||
}
|
||||
|
||||
private async Task<OrderParcelCreateOutput> CreateParcelCoreAsync(
|
||||
OrderParcelCreateInput input,
|
||||
int? operatorUserId = null)
|
||||
int? operatorUserId = null,
|
||||
ParcelCreateContext? createContext = null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(input.RequestId))
|
||||
{
|
||||
@ -88,7 +98,14 @@ public partial class OrderService
|
||||
country.ChineseName);
|
||||
|
||||
var serviceNo = await ResolveServiceNoAsync();
|
||||
var order = BuildDraftOrderEntity(input, product, country.CountryCode, country.ChineseName, snapshotBinding, serviceNo);
|
||||
var order = BuildDraftOrderEntity(
|
||||
input,
|
||||
product,
|
||||
country.CountryCode,
|
||||
country.ChineseName,
|
||||
snapshotBinding,
|
||||
serviceNo,
|
||||
createContext);
|
||||
order = await OrderRepository.InsertAsync(order);
|
||||
|
||||
var weights = BuildInitialWeightRows(order.Id, input);
|
||||
@ -1076,8 +1093,13 @@ public partial class OrderService
|
||||
string consigneeCountry,
|
||||
string consigneeCountryName,
|
||||
ProductSnapshotBinding snapshotBinding,
|
||||
string serviceNo)
|
||||
string serviceNo,
|
||||
ParcelCreateContext? createContext = null)
|
||||
{
|
||||
var orderSource = string.IsNullOrWhiteSpace(createContext?.OrderSource)
|
||||
? OrderSourceCodes.Manual
|
||||
: createContext!.OrderSource!.Trim();
|
||||
|
||||
return new Order
|
||||
{
|
||||
RequestId = input.RequestId,
|
||||
@ -1092,6 +1114,9 @@ public partial class OrderService
|
||||
ExportCustomsMode = input.ExportCustomsMode,
|
||||
WarehouseId = input.WarehouseId,
|
||||
CustomerId = input.CustomerId,
|
||||
OrderSource = orderSource,
|
||||
PickupTime = createContext?.PickupTime,
|
||||
CustomerSenderAddressId = createContext?.CustomerSenderAddressId,
|
||||
IsCod = input.IsCod ? (byte)1 : (byte)0,
|
||||
CodCurrency = input.CodCurrency,
|
||||
CodSum = input.CodSum,
|
||||
|
||||
84
src/Oms.Application/Services/Orders/OrderService.Pickup.cs
Normal file
84
src/Oms.Application/Services/Orders/OrderService.Pickup.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using Fake.Application;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Oms.Application.Dtos.Orders;
|
||||
using Oms.Application.Shared;
|
||||
using Oms.Domain.OrdersAggregate.Consts;
|
||||
using Oms.Domain.SystemAggregate.Entities;
|
||||
|
||||
namespace Oms.Application.Services.Orders;
|
||||
|
||||
public partial class OrderService
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建揽收订单(组装小包入参后复用创建核心)。
|
||||
/// </summary>
|
||||
[HttpPost("/order/pickup/create")]
|
||||
public async Task<OrderParcelCreateOutput> CreatePickupOrderAsync([FromBody] OrderPickupCreateInput input)
|
||||
{
|
||||
if (input.WarehouseId <= 0)
|
||||
{
|
||||
throw new BusinessException("仓库ID不能为空");
|
||||
}
|
||||
|
||||
if (input.SenderAddressId <= 0)
|
||||
{
|
||||
throw new BusinessException("发件地址不能为空");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(input.ProductCode))
|
||||
{
|
||||
throw new BusinessException("运输方式不能为空");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(input.CargoName))
|
||||
{
|
||||
throw new BusinessException("货物名称不能为空");
|
||||
}
|
||||
|
||||
if (input.ActualWeight <= 0)
|
||||
{
|
||||
throw new BusinessException("货物重量必须大于0");
|
||||
}
|
||||
|
||||
if (input.PickupTime == default)
|
||||
{
|
||||
throw new BusinessException("揽收时间不能为空");
|
||||
}
|
||||
|
||||
var customer = await OmsSessionHelper.ResolveSessionCustomerAsync(CustomerRepository, CurrentUser);
|
||||
var senderAddress = await CustomerSenderAddressRepository.GetOwnedAsync(input.SenderAddressId, customer.Id);
|
||||
if (senderAddress == null)
|
||||
{
|
||||
throw new BusinessException("发件地址不存在");
|
||||
}
|
||||
|
||||
var warehouse = await WarehouseAddressRepository.FirstOrDefaultAsync(x => x.Id == input.WarehouseId);
|
||||
if (warehouse == null)
|
||||
{
|
||||
throw new BusinessException("仓库不存在");
|
||||
}
|
||||
|
||||
string? senderCountryCode = null;
|
||||
if (senderAddress.Country.HasValue)
|
||||
{
|
||||
var country = await CountryRepository.FirstOrDefaultAsync(x => x.Id == senderAddress.Country.Value);
|
||||
senderCountryCode = country?.CountryCode;
|
||||
}
|
||||
|
||||
var parcelInput = PickupOrderParcelBuilder.Build(
|
||||
input,
|
||||
senderAddress,
|
||||
warehouse,
|
||||
customer.Id,
|
||||
senderCountryCode);
|
||||
|
||||
return await CreateParcelCoreAsync(
|
||||
parcelInput,
|
||||
createContext: new ParcelCreateContext
|
||||
{
|
||||
OrderSource = OrderSourceCodes.Pickup,
|
||||
PickupTime = input.PickupTime,
|
||||
CustomerSenderAddressId = senderAddress.Id
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,253 @@
|
||||
using System.Linq.Expressions;
|
||||
using Fake.Application;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Oms.Application.Dtos.Orders;
|
||||
using Oms.Application.Dtos.Shared;
|
||||
using Oms.Application.Shared;
|
||||
using Oms.Domain.OrdersAggregate.Entities;
|
||||
using Oms.Domain.OrdersAggregate.Repositories;
|
||||
|
||||
namespace Oms.Application.Services.Orders;
|
||||
|
||||
public partial class OrderService
|
||||
{
|
||||
private ICustomerSenderAddressRepository CustomerSenderAddressRepository =>
|
||||
LazyServiceProvider.GetRequiredService<ICustomerSenderAddressRepository>();
|
||||
|
||||
/// <summary>
|
||||
/// 发件地址分页列表。
|
||||
/// </summary>
|
||||
[HttpPost("/order/sender-address/list")]
|
||||
public async Task<OmsPageResult<CustomerSenderAddressOutput>> GetSenderAddressListAsync(
|
||||
[FromBody] CustomerSenderAddressPaginatedInput input)
|
||||
{
|
||||
var customer = await OmsSessionHelper.ResolveSessionCustomerAsync(CustomerRepository, CurrentUser);
|
||||
var pageIndex = OmsDocHelper.NormalizePage(input.Page);
|
||||
var pageSize = OmsDocHelper.NormalizePageSize(input.PageSize);
|
||||
var predicate = BuildSenderAddressPredicate(customer.Id, input);
|
||||
|
||||
var entities = await CustomerSenderAddressRepository.GetPagedListAsync(
|
||||
predicate,
|
||||
pageIndex,
|
||||
pageSize,
|
||||
new Dictionary<string, bool>
|
||||
{
|
||||
[nameof(CustomerSenderAddress.IsDefault)] = false,
|
||||
[nameof(CustomerSenderAddress.Id)] = false
|
||||
});
|
||||
var total = await CustomerSenderAddressRepository.CountAsync(predicate);
|
||||
var countryMap = await LoadCountryCodeMapAsync(entities.Select(x => x.Country).Where(x => x.HasValue).Select(x => x!.Value));
|
||||
|
||||
return new OmsPageResult<CustomerSenderAddressOutput>
|
||||
{
|
||||
Items = entities.Select(x => MapSenderAddressOutput(x, countryMap)).ToList(),
|
||||
Total = total
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发件地址详情。
|
||||
/// </summary>
|
||||
[HttpGet("/order/sender-address/detail")]
|
||||
public async Task<CustomerSenderAddressOutput> GetSenderAddressDetailAsync([FromQuery] int id)
|
||||
{
|
||||
var entity = await GetOwnedSenderAddressOrThrowAsync(id);
|
||||
var countryMap = await LoadCountryCodeMapAsync(entity.Country.HasValue ? [entity.Country.Value] : []);
|
||||
return MapSenderAddressOutput(entity, countryMap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增发件地址。
|
||||
/// </summary>
|
||||
[HttpPost("/order/sender-address/create")]
|
||||
public async Task<object> CreateSenderAddressAsync([FromBody] CreateCustomerSenderAddressInput input)
|
||||
{
|
||||
var customer = await OmsSessionHelper.ResolveSessionCustomerAsync(CustomerRepository, CurrentUser);
|
||||
ValidateSenderAddressInput(input);
|
||||
var countryId = await ResolveSenderCountryIdAsync(input.CountryCode);
|
||||
|
||||
if (input.IsDefault)
|
||||
{
|
||||
await CustomerSenderAddressRepository.ClearDefaultAsync(customer.Id);
|
||||
}
|
||||
|
||||
var entity = new CustomerSenderAddress
|
||||
{
|
||||
CustomerId = customer.Id,
|
||||
AddressName = TrimOrNull(input.AddressName),
|
||||
SenderName = input.SenderName.Trim(),
|
||||
Company = TrimOrNull(input.Company),
|
||||
Country = countryId,
|
||||
Province = TrimOrNull(input.Province),
|
||||
City = TrimOrNull(input.City),
|
||||
ZipCode = TrimOrNull(input.ZipCode),
|
||||
StreetAddress1 = input.StreetAddress1.Trim(),
|
||||
StreetAddress2 = TrimOrNull(input.StreetAddress2),
|
||||
StreetAddress3 = TrimOrNull(input.StreetAddress3),
|
||||
HouseNumber = TrimOrNull(input.HouseNumber),
|
||||
Phone = TrimOrNull(input.Phone),
|
||||
TelePhone = TrimOrNull(input.TelePhone),
|
||||
Email = TrimOrNull(input.Email),
|
||||
IsDefault = input.IsDefault ? (byte)1 : (byte)0
|
||||
};
|
||||
|
||||
entity = await CustomerSenderAddressRepository.InsertAsync(entity);
|
||||
return new { id = entity.Id };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新发件地址。
|
||||
/// </summary>
|
||||
[HttpPost("/order/sender-address/update")]
|
||||
public async Task UpdateSenderAddressAsync([FromBody] UpdateCustomerSenderAddressInput input)
|
||||
{
|
||||
var entity = await GetOwnedSenderAddressOrThrowAsync(input.Id);
|
||||
ValidateSenderAddressInput(input);
|
||||
var countryId = await ResolveSenderCountryIdAsync(input.CountryCode);
|
||||
|
||||
if (input.IsDefault)
|
||||
{
|
||||
await CustomerSenderAddressRepository.ClearDefaultAsync(entity.CustomerId, entity.Id);
|
||||
}
|
||||
|
||||
entity.AddressName = TrimOrNull(input.AddressName);
|
||||
entity.SenderName = input.SenderName.Trim();
|
||||
entity.Company = TrimOrNull(input.Company);
|
||||
entity.Country = countryId;
|
||||
entity.Province = TrimOrNull(input.Province);
|
||||
entity.City = TrimOrNull(input.City);
|
||||
entity.ZipCode = TrimOrNull(input.ZipCode);
|
||||
entity.StreetAddress1 = input.StreetAddress1.Trim();
|
||||
entity.StreetAddress2 = TrimOrNull(input.StreetAddress2);
|
||||
entity.StreetAddress3 = TrimOrNull(input.StreetAddress3);
|
||||
entity.HouseNumber = TrimOrNull(input.HouseNumber);
|
||||
entity.Phone = TrimOrNull(input.Phone);
|
||||
entity.TelePhone = TrimOrNull(input.TelePhone);
|
||||
entity.Email = TrimOrNull(input.Email);
|
||||
entity.IsDefault = input.IsDefault ? (byte)1 : (byte)0;
|
||||
|
||||
await CustomerSenderAddressRepository.UpdateAsync(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除发件地址(软删)。
|
||||
/// </summary>
|
||||
[HttpPost("/order/sender-address/delete")]
|
||||
public async Task DeleteSenderAddressAsync([FromBody] IdInput input)
|
||||
{
|
||||
var entity = await GetOwnedSenderAddressOrThrowAsync(input.Id);
|
||||
await CustomerSenderAddressRepository.DeleteAsync(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设为默认发件地址。
|
||||
/// </summary>
|
||||
[HttpPost("/order/sender-address/set-default")]
|
||||
public async Task SetDefaultSenderAddressAsync([FromBody] IdInput input)
|
||||
{
|
||||
var entity = await GetOwnedSenderAddressOrThrowAsync(input.Id);
|
||||
await CustomerSenderAddressRepository.ClearDefaultAsync(entity.CustomerId, entity.Id);
|
||||
entity.IsDefault = 1;
|
||||
await CustomerSenderAddressRepository.UpdateAsync(entity);
|
||||
}
|
||||
|
||||
private async Task<CustomerSenderAddress> GetOwnedSenderAddressOrThrowAsync(int id)
|
||||
{
|
||||
var customer = await OmsSessionHelper.ResolveSessionCustomerAsync(CustomerRepository, CurrentUser);
|
||||
var entity = await CustomerSenderAddressRepository.GetOwnedAsync(id, customer.Id);
|
||||
if (entity == null)
|
||||
{
|
||||
throw new BusinessException("发件地址不存在");
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
private static void ValidateSenderAddressInput(CreateCustomerSenderAddressInput input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input.SenderName))
|
||||
{
|
||||
throw new BusinessException("发件人姓名不能为空");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(input.StreetAddress1))
|
||||
{
|
||||
throw new BusinessException("发件地址不能为空");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<int?> ResolveSenderCountryIdAsync(string? countryCode)
|
||||
{
|
||||
var code = string.IsNullOrWhiteSpace(countryCode) ? "CN" : countryCode.Trim();
|
||||
var country = await GetCountryOrThrowAsync(code, "发件国家不存在");
|
||||
return country.Id;
|
||||
}
|
||||
|
||||
private static Expression<Func<CustomerSenderAddress, bool>> BuildSenderAddressPredicate(
|
||||
int customerId,
|
||||
CustomerSenderAddressPaginatedInput input)
|
||||
{
|
||||
var keyword = input.Keyword?.Trim();
|
||||
var onlyDefault = input.OnlyDefault;
|
||||
return x =>
|
||||
x.CustomerId == customerId
|
||||
&& (!onlyDefault || x.IsDefault == 1)
|
||||
&& (string.IsNullOrWhiteSpace(keyword)
|
||||
|| (x.AddressName ?? string.Empty).Contains(keyword)
|
||||
|| x.SenderName.Contains(keyword)
|
||||
|| (x.Phone ?? string.Empty).Contains(keyword)
|
||||
|| x.StreetAddress1.Contains(keyword));
|
||||
}
|
||||
|
||||
private async Task<Dictionary<int, string>> LoadCountryCodeMapAsync(IEnumerable<int> countryIds)
|
||||
{
|
||||
var ids = countryIds.Distinct().ToArray();
|
||||
if (ids.Length == 0)
|
||||
{
|
||||
return new Dictionary<int, string>();
|
||||
}
|
||||
|
||||
var countries = await CountryRepository.GetListAsync(x => ids.Contains(x.Id));
|
||||
return countries.ToDictionary(x => x.Id, x => x.CountryCode ?? string.Empty);
|
||||
}
|
||||
|
||||
private static CustomerSenderAddressOutput MapSenderAddressOutput(
|
||||
CustomerSenderAddress entity,
|
||||
IReadOnlyDictionary<int, string> countryMap)
|
||||
{
|
||||
string? countryCode = null;
|
||||
if (entity.Country.HasValue && countryMap.TryGetValue(entity.Country.Value, out var code))
|
||||
{
|
||||
countryCode = code;
|
||||
}
|
||||
|
||||
return new CustomerSenderAddressOutput
|
||||
{
|
||||
Id = entity.Id,
|
||||
CustomerId = entity.CustomerId,
|
||||
AddressName = entity.AddressName,
|
||||
SenderName = entity.SenderName,
|
||||
Company = entity.Company,
|
||||
Country = entity.Country,
|
||||
CountryCode = countryCode,
|
||||
Province = entity.Province,
|
||||
City = entity.City,
|
||||
ZipCode = entity.ZipCode,
|
||||
StreetAddress1 = entity.StreetAddress1,
|
||||
StreetAddress2 = entity.StreetAddress2,
|
||||
StreetAddress3 = entity.StreetAddress3,
|
||||
HouseNumber = entity.HouseNumber,
|
||||
Phone = entity.Phone,
|
||||
TelePhone = entity.TelePhone,
|
||||
Email = entity.Email,
|
||||
IsDefault = entity.IsDefault > 0,
|
||||
CreateTime = entity.CreateTime,
|
||||
UpdateTime = OmsDocHelper.NullIfDefault(entity.UpdateTime)
|
||||
};
|
||||
}
|
||||
|
||||
private static string? TrimOrNull(string? value)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||
}
|
||||
}
|
||||
@ -454,6 +454,7 @@ public partial class OrderService : ApplicationService
|
||||
{
|
||||
Status = input.Status,
|
||||
HandleStatus = input.HandleStatus,
|
||||
OrderSource = input.OrderSource,
|
||||
Keyword = input.Keyword,
|
||||
CreateTimeStart = input.CreateTimeStart,
|
||||
CreateTimeEnd = input.CreateTimeEnd,
|
||||
@ -464,6 +465,7 @@ public partial class OrderService : ApplicationService
|
||||
new()
|
||||
{
|
||||
Status = input.Status,
|
||||
OrderSource = input.OrderSource,
|
||||
Keyword = input.Keyword,
|
||||
CreateTimeStart = input.CreateTimeStart,
|
||||
CreateTimeEnd = input.CreateTimeEnd,
|
||||
|
||||
@ -0,0 +1,89 @@
|
||||
using Oms.Application.Dtos.Orders;
|
||||
using Oms.Domain.OrdersAggregate.Consts;
|
||||
using Oms.Domain.OrdersAggregate.Entities;
|
||||
using Oms.Domain.SystemAggregate.Entities;
|
||||
|
||||
namespace Oms.Application.Services.Orders;
|
||||
|
||||
/// <summary>
|
||||
/// 揽收订单 → 小包创建入参组装。
|
||||
/// </summary>
|
||||
public static class PickupOrderParcelBuilder
|
||||
{
|
||||
public static OrderParcelCreateInput Build(
|
||||
OrderPickupCreateInput input,
|
||||
CustomerSenderAddress senderAddress,
|
||||
WarehouseAddress warehouse,
|
||||
int customerId,
|
||||
string? senderCountryCode)
|
||||
{
|
||||
var cargoName = input.CargoName.Trim();
|
||||
var weight = input.ActualWeight;
|
||||
var clientNo = string.IsNullOrWhiteSpace(input.ClientNo)
|
||||
? $"PK{DateTime.Now:yyyyMMddHHmmss}{Random.Shared.Next(1000, 9999)}"
|
||||
: input.ClientNo.Trim();
|
||||
|
||||
var receiverName = string.IsNullOrWhiteSpace(warehouse.Recipient)
|
||||
? warehouse.WarehouseName
|
||||
: warehouse.Recipient!.Trim();
|
||||
var street = string.IsNullOrWhiteSpace(warehouse.ShippingAddress)
|
||||
? warehouse.WarehouseName
|
||||
: warehouse.ShippingAddress!.Trim();
|
||||
|
||||
return new OrderParcelCreateInput
|
||||
{
|
||||
RequestId = string.IsNullOrWhiteSpace(input.RequestId) ? null : input.RequestId.Trim(),
|
||||
ClientNo = clientNo,
|
||||
ProductCode = input.ProductCode.Trim(),
|
||||
WarehouseId = input.WarehouseId,
|
||||
CustomerId = customerId,
|
||||
Length = 1,
|
||||
Width = 1,
|
||||
Height = 1,
|
||||
ActualWeight = weight,
|
||||
CargoAttribute = string.IsNullOrWhiteSpace(input.CargoAttribute)
|
||||
? CargoAttributeCodes.General
|
||||
: input.CargoAttribute.Trim(),
|
||||
DeclaredCurrency = "CNY",
|
||||
DeclaredSum = 1,
|
||||
Quantity = 1,
|
||||
Sender = new OrderParcelSenderInput
|
||||
{
|
||||
SenderName = senderAddress.SenderName,
|
||||
Company = senderAddress.Company,
|
||||
Phone = senderAddress.Phone,
|
||||
TelePhone = senderAddress.TelePhone,
|
||||
Email = senderAddress.Email,
|
||||
CountryCode = senderCountryCode ?? "CN",
|
||||
Province = senderAddress.Province,
|
||||
City = senderAddress.City,
|
||||
ZipCode = senderAddress.ZipCode,
|
||||
StreetAddress1 = senderAddress.StreetAddress1,
|
||||
StreetAddress2 = senderAddress.StreetAddress2,
|
||||
StreetAddress3 = senderAddress.StreetAddress3,
|
||||
HouseNumber = senderAddress.HouseNumber
|
||||
},
|
||||
Receiver = new OrderParcelReceiverInput
|
||||
{
|
||||
RecipientsName = receiverName,
|
||||
Phone = warehouse.ContactPhone,
|
||||
CountryCode = "CN",
|
||||
ZipCode = "000000",
|
||||
StreetAddress1 = street
|
||||
},
|
||||
Items =
|
||||
[
|
||||
new OrderParcelItemInput
|
||||
{
|
||||
CnName = cargoName,
|
||||
EnName = cargoName,
|
||||
UnitPrice = 1,
|
||||
UnitWeight = weight,
|
||||
Quantity = 1,
|
||||
Amount = 1,
|
||||
Weight = weight
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
using System.Linq.Expressions;
|
||||
using Fake.Application;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Oms.Application.Dtos.Shared;
|
||||
using Oms.Application.Dtos.Warehouse;
|
||||
@ -184,6 +185,18 @@ public partial class WarehouseService
|
||||
return MapParcelOutput(parcel, savedItems);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上传包裹商品图片,返回可访问相对路径。
|
||||
/// </summary>
|
||||
[HttpPost("/warehouse/parcel/upload-image")]
|
||||
[Consumes("multipart/form-data")]
|
||||
public async Task<object> UploadParcelImageAsync(IFormFile file)
|
||||
{
|
||||
await ResolveMutationCustomerIdAsync(null);
|
||||
var path = await OmsFileUploadHelper.SaveWarehouseParcelImageAsync(file, WebHostEnvironment);
|
||||
return new { url = path, fileUrl = path };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除包裹。
|
||||
/// </summary>
|
||||
|
||||
@ -26,6 +26,12 @@ public static class OmsFileUploadHelper
|
||||
public static Task<string> SaveWarehouseSkuImageAsync(IFormFile file, IWebHostEnvironment env) =>
|
||||
SaveImageAsync(file, env, "sku", "SKU 图片");
|
||||
|
||||
/// <summary>
|
||||
/// 保存包裹商品图片。
|
||||
/// </summary>
|
||||
public static Task<string> SaveWarehouseParcelImageAsync(IFormFile file, IWebHostEnvironment env) =>
|
||||
SaveImageAsync(file, env, "parcel", "包裹商品图片");
|
||||
|
||||
private static async Task<string> SaveImageAsync(
|
||||
IFormFile? file,
|
||||
IWebHostEnvironment env,
|
||||
|
||||
22
src/Oms.Domain/OrdersAggregate/Consts/OrderSourceCodes.cs
Normal file
22
src/Oms.Domain/OrdersAggregate/Consts/OrderSourceCodes.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace Oms.Domain.OrdersAggregate.Consts;
|
||||
|
||||
/// <summary>
|
||||
/// 订单来源编码。
|
||||
/// </summary>
|
||||
public static class OrderSourceCodes
|
||||
{
|
||||
public const string Manual = "manual";
|
||||
public const string Pickup = "pickup";
|
||||
public const string Upload = "upload";
|
||||
public const string Batch = "batch";
|
||||
public const string Platform = "platform";
|
||||
|
||||
public static readonly HashSet<string> All = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
Manual,
|
||||
Pickup,
|
||||
Upload,
|
||||
Batch,
|
||||
Platform
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
using Oms.Domain.Shared;
|
||||
using SqlSugar;
|
||||
|
||||
namespace Oms.Domain.OrdersAggregate.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// 客户发件地址簿。
|
||||
/// </summary>
|
||||
[SugarTable("t_customer_sender_address")]
|
||||
public class CustomerSenderAddress : OmsAuditedAggregateRoot
|
||||
{
|
||||
public int CustomerId { get; set; }
|
||||
|
||||
public string? AddressName { get; set; }
|
||||
|
||||
public string SenderName { get; set; } = string.Empty;
|
||||
|
||||
public string? Company { get; set; }
|
||||
|
||||
public int? Country { get; set; }
|
||||
|
||||
public string? Province { get; set; }
|
||||
|
||||
public string? City { get; set; }
|
||||
|
||||
public string? ZipCode { get; set; }
|
||||
|
||||
public string StreetAddress1 { get; set; } = string.Empty;
|
||||
|
||||
public string? StreetAddress2 { get; set; }
|
||||
|
||||
public string? StreetAddress3 { get; set; }
|
||||
|
||||
public string? HouseNumber { get; set; }
|
||||
|
||||
public string? Phone { get; set; }
|
||||
|
||||
public string? TelePhone { get; set; }
|
||||
|
||||
public string? Email { get; set; }
|
||||
|
||||
public byte IsDefault { get; set; }
|
||||
}
|
||||
@ -87,6 +87,21 @@ public class Order : OmsAuditedAggregateRoot
|
||||
|
||||
public int? CustomerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 订单来源:manual / pickup / upload / batch / platform。
|
||||
/// </summary>
|
||||
public string? OrderSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预约揽收时间(OrderSource=pickup)。
|
||||
/// </summary>
|
||||
public DateTime? PickupTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时选用的发件地址簿 Id。
|
||||
/// </summary>
|
||||
public int? CustomerSenderAddressId { get; set; }
|
||||
|
||||
public string? OrganizationId { get; set; }
|
||||
|
||||
public int SecretLevel { get; set; } = 2;
|
||||
|
||||
@ -9,6 +9,8 @@ public class OrderListFilter
|
||||
|
||||
public string? HandleStatus { get; set; }
|
||||
|
||||
public string? OrderSource { get; set; }
|
||||
|
||||
public string? Keyword { get; set; }
|
||||
|
||||
public DateTime? CreateTimeStart { get; set; }
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
using Fake.Domain.Repositories;
|
||||
using Oms.Domain.OrdersAggregate.Entities;
|
||||
|
||||
namespace Oms.Domain.OrdersAggregate.Repositories;
|
||||
|
||||
public interface ICustomerSenderAddressRepository : IRepository<CustomerSenderAddress>
|
||||
{
|
||||
Task<CustomerSenderAddress?> GetOwnedAsync(int id, int customerId, CancellationToken cancellationToken = default);
|
||||
|
||||
Task ClearDefaultAsync(int customerId, int? excludeId = null, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
using Fake.DependencyInjection;
|
||||
using Oms.Domain.OrdersAggregate.Entities;
|
||||
using Oms.Domain.OrdersAggregate.Repositories;
|
||||
using Oms.SqlSugarCore.Repositories;
|
||||
|
||||
namespace Oms.SqlSugarCore.Orders;
|
||||
|
||||
public class CustomerSenderAddressRepository : OmsSqlSugarRepository<CustomerSenderAddress>, ICustomerSenderAddressRepository, IScopedDependency
|
||||
{
|
||||
public Task<CustomerSenderAddress?> GetOwnedAsync(int id, int customerId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return FirstOrDefaultAsync(x => x.Id == id && x.CustomerId == customerId, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task ClearDefaultAsync(int customerId, int? excludeId = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var list = await GetListAsync(
|
||||
x => x.CustomerId == customerId && x.IsDefault == 1
|
||||
&& (!excludeId.HasValue || x.Id != excludeId.Value),
|
||||
cancellationToken: cancellationToken);
|
||||
if (list.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var item in list)
|
||||
{
|
||||
item.IsDefault = 0;
|
||||
await UpdateAsync(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,6 +90,13 @@ public class OrderRepository : OmsSqlSugarRepository<Order>, IOrderRepository, I
|
||||
}
|
||||
}
|
||||
|
||||
var orderSource = filter.OrderSource?.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(orderSource)
|
||||
&& !string.Equals(orderSource, "all", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
query = query.Where(x => x.OrderSource == orderSource);
|
||||
}
|
||||
|
||||
query = OrderListScopeFilterApplier.Apply(query, filter);
|
||||
|
||||
return query;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user