commit
a5caccbf58
19
db.sql
19
db.sql
@ -40,6 +40,7 @@ DROP TABLE IF EXISTS `C_inbound_box_line`;
|
|||||||
DROP TABLE IF EXISTS `C_inbound_box`;
|
DROP TABLE IF EXISTS `C_inbound_box`;
|
||||||
DROP TABLE IF EXISTS `C_inbound_notice`;
|
DROP TABLE IF EXISTS `C_inbound_notice`;
|
||||||
DROP TABLE IF EXISTS `C_parcel_item`;
|
DROP TABLE IF EXISTS `C_parcel_item`;
|
||||||
|
DROP TABLE IF EXISTS `C_parcel_arrival`;
|
||||||
DROP TABLE IF EXISTS `C_parcel`;
|
DROP TABLE IF EXISTS `C_parcel`;
|
||||||
DROP TABLE IF EXISTS `C_inventory`;
|
DROP TABLE IF EXISTS `C_inventory`;
|
||||||
DROP TABLE IF EXISTS `C_sku_platform_map`;
|
DROP TABLE IF EXISTS `C_sku_platform_map`;
|
||||||
@ -1357,10 +1358,28 @@ CREATE TABLE `C_parcel` (
|
|||||||
KEY `idx_c_parcel_status` (`CustomerId`, `LifecycleStatus`)
|
KEY `idx_c_parcel_status` (`CustomerId`, `LifecycleStatus`)
|
||||||
) COMMENT='仓储-客户包裹';
|
) COMMENT='仓储-客户包裹';
|
||||||
|
|
||||||
|
CREATE TABLE `C_parcel_arrival` (
|
||||||
|
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||||
|
`CustomerId` INT NOT NULL COMMENT '客户ID',
|
||||||
|
`WarehouseId` INT NOT NULL COMMENT '仓库ID',
|
||||||
|
`ParcelId` INT NULL COMMENT '关联包裹 C_parcel.Id',
|
||||||
|
`TrackingNo` VARCHAR(64) NOT NULL COMMENT '国内快递单号',
|
||||||
|
`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_c_parcel_arrival_tracking` (`CustomerId`, `TrackingNo`, `WarehouseId`),
|
||||||
|
KEY `idx_c_parcel_arrival_ctime` (`CreateTime`)
|
||||||
|
) COMMENT='仓储-快递到仓记录';
|
||||||
|
|
||||||
CREATE TABLE `C_parcel_item` (
|
CREATE TABLE `C_parcel_item` (
|
||||||
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||||
`ParcelId` INT NOT NULL COMMENT '包裹ID → C_parcel.Id',
|
`ParcelId` INT NOT NULL COMMENT '包裹ID → C_parcel.Id',
|
||||||
`ProductName` VARCHAR(512) NULL COMMENT '商品名称',
|
`ProductName` VARCHAR(512) NULL COMMENT '商品名称',
|
||||||
|
`AttributeText` VARCHAR(256) NULL COMMENT '规格属性',
|
||||||
`Quantity` INT NOT NULL DEFAULT 0 COMMENT '数量',
|
`Quantity` INT NOT NULL DEFAULT 0 COMMENT '数量',
|
||||||
`SkuId` INT NULL COMMENT '库内SKU ID → C_sku.Id',
|
`SkuId` INT NULL COMMENT '库内SKU ID → C_sku.Id',
|
||||||
`SkuCode` VARCHAR(64) NULL COMMENT '冗余SKU编码',
|
`SkuCode` VARCHAR(64) NULL COMMENT '冗余SKU编码',
|
||||||
|
|||||||
20
sql/C_parcel_arrival.sql
Normal file
20
sql/C_parcel_arrival.sql
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
-- 已有库增量:快递到仓表 C_parcel_arrival
|
||||||
|
-- 对应功能:操作 → 快递到仓;全量结构见根目录 db.sql
|
||||||
|
-- 可重复执行(IF NOT EXISTS)
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `C_parcel_arrival` (
|
||||||
|
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||||
|
`CustomerId` INT NOT NULL COMMENT '客户ID',
|
||||||
|
`WarehouseId` INT NOT NULL COMMENT '仓库ID',
|
||||||
|
`ParcelId` INT NULL COMMENT '关联包裹 C_parcel.Id',
|
||||||
|
`TrackingNo` VARCHAR(64) NOT NULL COMMENT '国内快递单号',
|
||||||
|
`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_c_parcel_arrival_tracking` (`CustomerId`, `TrackingNo`, `WarehouseId`),
|
||||||
|
KEY `idx_c_parcel_arrival_ctime` (`CreateTime`)
|
||||||
|
) COMMENT='仓储-快递到仓记录';
|
||||||
20
sql/C_parcel_item_AttributeText.sql
Normal file
20
sql/C_parcel_item_AttributeText.sql
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
-- 已有库增量:包裹明细增加属性字段 AttributeText
|
||||||
|
-- 可重复执行
|
||||||
|
|
||||||
|
SET @col_exists := (
|
||||||
|
SELECT COUNT(1)
|
||||||
|
FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'C_parcel_item'
|
||||||
|
AND COLUMN_NAME = 'AttributeText'
|
||||||
|
);
|
||||||
|
|
||||||
|
SET @sql := IF(
|
||||||
|
@col_exists = 0,
|
||||||
|
'ALTER TABLE `C_parcel_item` ADD COLUMN `AttributeText` VARCHAR(256) NULL COMMENT ''规格属性'' AFTER `ProductName`',
|
||||||
|
'SELECT ''C_parcel_item.AttributeText already exists'' AS info'
|
||||||
|
);
|
||||||
|
|
||||||
|
PREPARE stmt FROM @sql;
|
||||||
|
EXECUTE stmt;
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
@ -3,5 +3,5 @@
|
|||||||
OMS / 共用库的建表脚本已统一到仓库根目录 `db.sql`。
|
OMS / 共用库的建表脚本已统一到仓库根目录 `db.sql`。
|
||||||
|
|
||||||
- 新环境:直接执行 `db.sql`(会 DROP 后重建,勿在有生产数据的库上执行)
|
- 新环境:直接执行 `db.sql`(会 DROP 后重建,勿在有生产数据的库上执行)
|
||||||
- 已有库:用 diff 对比 `db.sql` 与现库结构后手工 ALTER,不再维护 `migrate_*.sql`
|
- 已有库:用 diff 对比 `db.sql` 与现库结构后手工 ALTER;本目录下单个表脚本(如 `C_parcel_arrival.sql`、`C_parcel_item_AttributeText.sql`)可直接在已有库执行增量建表
|
||||||
- 数据权限表(`user_org_perm` 等)若 RBAC 与 OMS 分库,请只在 RBAC 库执行 `db.sql` 末尾对应段落
|
- 数据权限表(`user_org_perm` 等)若 RBAC 与 OMS 分库,请只在 RBAC 库执行 `db.sql` 末尾对应段落
|
||||||
|
|||||||
@ -325,6 +325,8 @@ public class WarehouseParcelItemSaveInput
|
|||||||
|
|
||||||
public string? ProductName { get; set; }
|
public string? ProductName { get; set; }
|
||||||
|
|
||||||
|
public string? AttributeText { get; set; }
|
||||||
|
|
||||||
public int Quantity { get; set; }
|
public int Quantity { get; set; }
|
||||||
|
|
||||||
public string? Remark { get; set; }
|
public string? Remark { get; set; }
|
||||||
@ -336,6 +338,8 @@ public class WarehouseParcelItemOutput
|
|||||||
|
|
||||||
public string? ProductName { get; set; }
|
public string? ProductName { get; set; }
|
||||||
|
|
||||||
|
public string? AttributeText { get; set; }
|
||||||
|
|
||||||
public int Quantity { get; set; }
|
public int Quantity { get; set; }
|
||||||
|
|
||||||
public int? SkuId { get; set; }
|
public int? SkuId { get; set; }
|
||||||
@ -466,3 +470,44 @@ public class WarehouseInventoryRelocateInput
|
|||||||
[Range(1, int.MaxValue)]
|
[Range(1, int.MaxValue)]
|
||||||
public int Qty { get; set; }
|
public int Qty { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Parcel arrival(快递到仓)---
|
||||||
|
|
||||||
|
public class WarehouseParcelArrivalListInput : WarehousePageInput
|
||||||
|
{
|
||||||
|
public int? WarehouseId { get; set; }
|
||||||
|
|
||||||
|
public string? Keyword { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WarehouseParcelArrivalCreateInput
|
||||||
|
{
|
||||||
|
public int? CustomerId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int WarehouseId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string TrackingNo { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WarehouseParcelArrivalOutput
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public int CustomerId { get; set; }
|
||||||
|
|
||||||
|
public int WarehouseId { get; set; }
|
||||||
|
|
||||||
|
public string? WarehouseName { get; set; }
|
||||||
|
|
||||||
|
public int? ParcelId { get; set; }
|
||||||
|
|
||||||
|
public string TrackingNo { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public DateTime CreateTime { get; set; }
|
||||||
|
|
||||||
|
public int CreateUserId { get; set; }
|
||||||
|
|
||||||
|
public string? CreateBy { get; set; }
|
||||||
|
}
|
||||||
|
|||||||
@ -128,6 +128,9 @@ public partial class WarehouseService
|
|||||||
{
|
{
|
||||||
ParcelId = parcel.Id,
|
ParcelId = parcel.Id,
|
||||||
ProductName = string.IsNullOrWhiteSpace(itemInput.ProductName) ? null : itemInput.ProductName.Trim(),
|
ProductName = string.IsNullOrWhiteSpace(itemInput.ProductName) ? null : itemInput.ProductName.Trim(),
|
||||||
|
AttributeText = string.IsNullOrWhiteSpace(itemInput.AttributeText)
|
||||||
|
? null
|
||||||
|
: itemInput.AttributeText.Trim(),
|
||||||
Quantity = itemInput.Quantity,
|
Quantity = itemInput.Quantity,
|
||||||
Remark = string.IsNullOrWhiteSpace(itemInput.Remark) ? null : itemInput.Remark.Trim()
|
Remark = string.IsNullOrWhiteSpace(itemInput.Remark) ? null : itemInput.Remark.Trim()
|
||||||
};
|
};
|
||||||
@ -247,6 +250,7 @@ public partial class WarehouseService
|
|||||||
{
|
{
|
||||||
Id = item.Id,
|
Id = item.Id,
|
||||||
ProductName = item.ProductName,
|
ProductName = item.ProductName,
|
||||||
|
AttributeText = item.AttributeText,
|
||||||
Quantity = item.Quantity,
|
Quantity = item.Quantity,
|
||||||
SkuId = item.SkuId,
|
SkuId = item.SkuId,
|
||||||
SkuCode = item.SkuCode,
|
SkuCode = item.SkuCode,
|
||||||
|
|||||||
@ -0,0 +1,165 @@
|
|||||||
|
using System.Linq.Expressions;
|
||||||
|
using Fake.Application;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Oms.Application.Dtos.Shared;
|
||||||
|
using Oms.Application.Dtos.Warehouse;
|
||||||
|
using Oms.Application.Shared;
|
||||||
|
using Oms.Domain.SystemAggregate.Repositories;
|
||||||
|
using Oms.Domain.WarehouseAggregate.Consts;
|
||||||
|
using Oms.Domain.WarehouseAggregate.Entities;
|
||||||
|
using Oms.Domain.WarehouseAggregate.Repositories;
|
||||||
|
|
||||||
|
namespace Oms.Application.Services.Warehouse;
|
||||||
|
|
||||||
|
public partial class WarehouseService
|
||||||
|
{
|
||||||
|
private ICParcelArrivalRepository ParcelArrivalRepository =>
|
||||||
|
LazyServiceProvider.GetRequiredService<ICParcelArrivalRepository>();
|
||||||
|
|
||||||
|
private IWarehouseAddressRepository WarehouseAddressRepository =>
|
||||||
|
LazyServiceProvider.GetRequiredService<IWarehouseAddressRepository>();
|
||||||
|
|
||||||
|
private OmsRbacUserDisplayNameResolver UserDisplayNameResolver =>
|
||||||
|
LazyServiceProvider.GetRequiredService<OmsRbacUserDisplayNameResolver>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 快递到仓记录列表。
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("/warehouse/parcel-arrival/list")]
|
||||||
|
public async Task<OmsPageResult<WarehouseParcelArrivalOutput>> GetParcelArrivalListAsync(
|
||||||
|
WarehouseParcelArrivalListInput input)
|
||||||
|
{
|
||||||
|
var customerId = await ResolveListCustomerIdAsync(input.CustomerId);
|
||||||
|
var (hasCustomerFilter, customerIdValue) = CustomerFilter(customerId);
|
||||||
|
var keyword = input.Keyword?.Trim();
|
||||||
|
var hasKeyword = !string.IsNullOrWhiteSpace(keyword);
|
||||||
|
var hasWarehouseFilter = input.WarehouseId is > 0;
|
||||||
|
var warehouseIdValue = input.WarehouseId.GetValueOrDefault();
|
||||||
|
|
||||||
|
Expression<Func<CParcelArrival, bool>> predicate = x =>
|
||||||
|
(!hasCustomerFilter || x.CustomerId == customerIdValue)
|
||||||
|
&& (!hasWarehouseFilter || x.WarehouseId == warehouseIdValue)
|
||||||
|
&& (!hasKeyword || x.TrackingNo.Contains(keyword!));
|
||||||
|
|
||||||
|
var pageIndex = OmsDocHelper.NormalizePage(input.Page);
|
||||||
|
var pageSize = OmsDocHelper.NormalizePageSize(input.PageSize);
|
||||||
|
var entities = await ParcelArrivalRepository.GetPagedListAsync(
|
||||||
|
predicate,
|
||||||
|
pageIndex,
|
||||||
|
pageSize,
|
||||||
|
new Dictionary<string, bool>
|
||||||
|
{
|
||||||
|
[nameof(CParcelArrival.CreateTime)] = false,
|
||||||
|
[nameof(CParcelArrival.Id)] = false
|
||||||
|
});
|
||||||
|
var total = await ParcelArrivalRepository.CountAsync(predicate);
|
||||||
|
|
||||||
|
var warehouseIds = entities.Select(x => x.WarehouseId).Distinct().ToArray();
|
||||||
|
var warehouses = warehouseIds.Length == 0
|
||||||
|
? []
|
||||||
|
: await WarehouseAddressRepository.GetListAsync(x => warehouseIds.Contains(x.Id));
|
||||||
|
var warehouseNameMap = warehouses.ToDictionary(x => x.Id, x => x.WarehouseName);
|
||||||
|
|
||||||
|
var userIds = entities.Select(x => x.CreateUserId).Distinct().ToArray();
|
||||||
|
var userNameMap = await UserDisplayNameResolver.ResolveManyAsync(userIds);
|
||||||
|
|
||||||
|
return new OmsPageResult<WarehouseParcelArrivalOutput>
|
||||||
|
{
|
||||||
|
Items = entities.Select(x => new WarehouseParcelArrivalOutput
|
||||||
|
{
|
||||||
|
Id = x.Id,
|
||||||
|
CustomerId = x.CustomerId,
|
||||||
|
WarehouseId = x.WarehouseId,
|
||||||
|
WarehouseName = warehouseNameMap.GetValueOrDefault(x.WarehouseId),
|
||||||
|
ParcelId = x.ParcelId,
|
||||||
|
TrackingNo = x.TrackingNo,
|
||||||
|
CreateTime = x.CreateTime,
|
||||||
|
CreateUserId = x.CreateUserId,
|
||||||
|
CreateBy = OmsRbacUserDisplayNameResolver.FormatUserId(x.CreateUserId, userNameMap)
|
||||||
|
}).ToList(),
|
||||||
|
Total = total
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 快递到仓:写入到仓记录,并将对应「我的包裹」状态改为 arrived。
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("/warehouse/parcel-arrival/create")]
|
||||||
|
public async Task<WarehouseParcelArrivalOutput> CreateParcelArrivalAsync(WarehouseParcelArrivalCreateInput input)
|
||||||
|
{
|
||||||
|
var customerId = await ResolveMutationCustomerIdAsync(input.CustomerId);
|
||||||
|
if (input.WarehouseId <= 0)
|
||||||
|
{
|
||||||
|
throw new BusinessException("仓库ID不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
var trackingNo = input.TrackingNo.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(trackingNo))
|
||||||
|
{
|
||||||
|
throw new BusinessException("国内快递单号不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
var warehouse = await WarehouseAddressRepository.FirstOrDefaultAsync(x => x.Id == input.WarehouseId)
|
||||||
|
?? throw new BusinessException("仓库不存在");
|
||||||
|
|
||||||
|
var parcel = await ParcelRepository.FirstOrDefaultAsync(x =>
|
||||||
|
x.CustomerId == customerId
|
||||||
|
&& x.WarehouseId == input.WarehouseId
|
||||||
|
&& x.TrackingNo == trackingNo);
|
||||||
|
if (parcel == null)
|
||||||
|
{
|
||||||
|
throw new BusinessException("未找到对应包裹,请确认快递单号与仓库");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parcel.LifecycleStatus == WarehouseParcelLifecycleCodes.Stored)
|
||||||
|
{
|
||||||
|
throw new BusinessException("包裹已上架,无法再到仓");
|
||||||
|
}
|
||||||
|
|
||||||
|
parcel.LifecycleStatus = WarehouseParcelLifecycleCodes.Arrived;
|
||||||
|
parcel.ArrivalTime ??= DateTime.Now;
|
||||||
|
await ParcelRepository.UpdateAsync(parcel);
|
||||||
|
|
||||||
|
var entity = new CParcelArrival
|
||||||
|
{
|
||||||
|
CustomerId = customerId,
|
||||||
|
WarehouseId = input.WarehouseId,
|
||||||
|
ParcelId = parcel.Id,
|
||||||
|
TrackingNo = trackingNo
|
||||||
|
};
|
||||||
|
await ParcelArrivalRepository.InsertAsync(entity);
|
||||||
|
|
||||||
|
var userNameMap = await UserDisplayNameResolver.ResolveManyAsync([entity.CreateUserId]);
|
||||||
|
return new WarehouseParcelArrivalOutput
|
||||||
|
{
|
||||||
|
Id = entity.Id,
|
||||||
|
CustomerId = entity.CustomerId,
|
||||||
|
WarehouseId = entity.WarehouseId,
|
||||||
|
WarehouseName = warehouse.WarehouseName,
|
||||||
|
ParcelId = entity.ParcelId,
|
||||||
|
TrackingNo = entity.TrackingNo,
|
||||||
|
CreateTime = entity.CreateTime,
|
||||||
|
CreateUserId = entity.CreateUserId,
|
||||||
|
CreateBy = OmsRbacUserDisplayNameResolver.FormatUserId(entity.CreateUserId, userNameMap)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除快递到仓记录(不回退包裹状态)。
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("/warehouse/parcel-arrival/delete")]
|
||||||
|
public async Task DeleteParcelArrivalAsync(IdInput input)
|
||||||
|
{
|
||||||
|
var customerId = await ResolveListCustomerIdAsync(null);
|
||||||
|
var entity = customerId.HasValue
|
||||||
|
? await ParcelArrivalRepository.FirstOrDefaultAsync(x =>
|
||||||
|
x.Id == input.Id && x.CustomerId == customerId.Value)
|
||||||
|
: await ParcelArrivalRepository.FirstOrDefaultAsync(x => x.Id == input.Id);
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
throw new BusinessException("到仓记录不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
await ParcelArrivalRepository.DeleteAsync(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using Fake.Application;
|
using Fake.Application;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Oms.Application.Dtos.Shared;
|
using Oms.Application.Dtos.Shared;
|
||||||
using Oms.Application.Dtos.Warehouse;
|
using Oms.Application.Dtos.Warehouse;
|
||||||
@ -9,6 +11,9 @@ namespace Oms.Application.Services.Warehouse;
|
|||||||
|
|
||||||
public partial class WarehouseService
|
public partial class WarehouseService
|
||||||
{
|
{
|
||||||
|
private IWebHostEnvironment WebHostEnvironment =>
|
||||||
|
LazyServiceProvider.GetRequiredService<IWebHostEnvironment>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查询 SKU 列表。
|
/// 查询 SKU 列表。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -142,6 +147,18 @@ public partial class WarehouseService
|
|||||||
await SkuRepository.DeleteAsync(entity);
|
await SkuRepository.DeleteAsync(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上传 SKU 图片,返回可访问相对路径。
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("/warehouse/sku/upload-image")]
|
||||||
|
[Consumes("multipart/form-data")]
|
||||||
|
public async Task<object> UploadSkuImageAsync(IFormFile file)
|
||||||
|
{
|
||||||
|
await ResolveMutationCustomerIdAsync(null);
|
||||||
|
var path = await OmsFileUploadHelper.SaveWarehouseSkuImageAsync(file, WebHostEnvironment);
|
||||||
|
return new { url = path, fileUrl = path };
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 绑定平台 SKU 与库内 SKU 映射。
|
/// 绑定平台 SKU 与库内 SKU 映射。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -17,16 +17,34 @@ public static class OmsFileUploadHelper
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 保存充值截图,返回以 /uploads 开头的相对访问路径。
|
/// 保存充值截图,返回以 /uploads 开头的相对访问路径。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<string> SaveRechargeScreenshotAsync(IFormFile? file, IWebHostEnvironment env)
|
public static Task<string> SaveRechargeScreenshotAsync(IFormFile? file, IWebHostEnvironment env) =>
|
||||||
|
SaveImageAsync(file, env, "recharge", "充值截图");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存仓储 SKU 图片。
|
||||||
|
/// </summary>
|
||||||
|
public static Task<string> SaveWarehouseSkuImageAsync(IFormFile file, IWebHostEnvironment env) =>
|
||||||
|
SaveImageAsync(file, env, "sku", "SKU 图片");
|
||||||
|
|
||||||
|
private static async Task<string> SaveImageAsync(
|
||||||
|
IFormFile? file,
|
||||||
|
IWebHostEnvironment env,
|
||||||
|
string folder,
|
||||||
|
string label)
|
||||||
{
|
{
|
||||||
if (file == null || file.Length <= 0)
|
if (file == null || file.Length <= 0)
|
||||||
{
|
{
|
||||||
return "0";
|
if (folder == "recharge")
|
||||||
|
{
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new BusinessException($"{label}不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.Length > MaxImageBytes)
|
if (file.Length > MaxImageBytes)
|
||||||
{
|
{
|
||||||
throw new BusinessException("充值截图大小不能超过 5MB");
|
throw new BusinessException($"{label}大小不能超过 5MB");
|
||||||
}
|
}
|
||||||
|
|
||||||
var ext = Path.GetExtension(file.FileName);
|
var ext = Path.GetExtension(file.FileName);
|
||||||
@ -37,7 +55,7 @@ public static class OmsFileUploadHelper
|
|||||||
|
|
||||||
if (!ImageExtensions.Contains(ext))
|
if (!ImageExtensions.Contains(ext))
|
||||||
{
|
{
|
||||||
throw new BusinessException("充值截图仅支持 jpg、png、webp、gif、bmp 格式");
|
throw new BusinessException($"{label}仅支持 jpg、png、webp、gif、bmp 格式");
|
||||||
}
|
}
|
||||||
|
|
||||||
var webRoot = env.WebRootPath;
|
var webRoot = env.WebRootPath;
|
||||||
@ -47,7 +65,7 @@ public static class OmsFileUploadHelper
|
|||||||
}
|
}
|
||||||
|
|
||||||
var monthFolder = DateTime.Now.ToString("yyyyMM");
|
var monthFolder = DateTime.Now.ToString("yyyyMM");
|
||||||
var relativeDir = Path.Combine("uploads", "recharge", monthFolder);
|
var relativeDir = Path.Combine("uploads", folder, monthFolder);
|
||||||
var absoluteDir = Path.Combine(webRoot, relativeDir);
|
var absoluteDir = Path.Combine(webRoot, relativeDir);
|
||||||
Directory.CreateDirectory(absoluteDir);
|
Directory.CreateDirectory(absoluteDir);
|
||||||
|
|
||||||
@ -58,7 +76,7 @@ public static class OmsFileUploadHelper
|
|||||||
await file.CopyToAsync(stream);
|
await file.CopyToAsync(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $"/uploads/recharge/{monthFolder}/{fileName}".Replace('\\', '/');
|
return $"/uploads/{folder}/{monthFolder}/{fileName}".Replace('\\', '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly HashSet<string> ExcelExtensions =
|
private static readonly HashSet<string> ExcelExtensions =
|
||||||
|
|||||||
21
src/Oms.Domain/WarehouseAggregate/Entities/CParcelArrival.cs
Normal file
21
src/Oms.Domain/WarehouseAggregate/Entities/CParcelArrival.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Oms.Domain.Shared;
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace Oms.Domain.WarehouseAggregate.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 快递到仓扫描记录。
|
||||||
|
/// </summary>
|
||||||
|
[SugarTable("C_parcel_arrival")]
|
||||||
|
public class CParcelArrival : OmsAuditedAggregateRoot
|
||||||
|
{
|
||||||
|
public int CustomerId { get; set; }
|
||||||
|
|
||||||
|
public int WarehouseId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>关联我的包裹(可为空,兼容历史)。</summary>
|
||||||
|
public int? ParcelId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>国内快递单号。</summary>
|
||||||
|
public string TrackingNo { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
@ -10,6 +10,9 @@ public class CParcelItem : OmsAuditedAggregateRoot
|
|||||||
|
|
||||||
public string? ProductName { get; set; }
|
public string? ProductName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>规格属性。</summary>
|
||||||
|
public string? AttributeText { get; set; }
|
||||||
|
|
||||||
public int Quantity { get; set; }
|
public int Quantity { get; set; }
|
||||||
|
|
||||||
public int? SkuId { get; set; }
|
public int? SkuId { get; set; }
|
||||||
|
|||||||
@ -19,4 +19,6 @@ public interface ICParcelRepository : IRepository<CParcel>;
|
|||||||
|
|
||||||
public interface ICParcelItemRepository : IRepository<CParcelItem>;
|
public interface ICParcelItemRepository : IRepository<CParcelItem>;
|
||||||
|
|
||||||
|
public interface ICParcelArrivalRepository : IRepository<CParcelArrival>;
|
||||||
|
|
||||||
public interface ICSkuPlatformMapRepository : IRepository<CSkuPlatformMap>;
|
public interface ICSkuPlatformMapRepository : IRepository<CSkuPlatformMap>;
|
||||||
|
|||||||
@ -21,4 +21,6 @@ public class CParcelRepository : OmsSqlSugarRepository<CParcel>, ICParcelReposit
|
|||||||
|
|
||||||
public class CParcelItemRepository : OmsSqlSugarRepository<CParcelItem>, ICParcelItemRepository, IScopedDependency;
|
public class CParcelItemRepository : OmsSqlSugarRepository<CParcelItem>, ICParcelItemRepository, IScopedDependency;
|
||||||
|
|
||||||
|
public class CParcelArrivalRepository : OmsSqlSugarRepository<CParcelArrival>, ICParcelArrivalRepository, IScopedDependency;
|
||||||
|
|
||||||
public class CSkuPlatformMapRepository : OmsSqlSugarRepository<CSkuPlatformMap>, ICSkuPlatformMapRepository, IScopedDependency;
|
public class CSkuPlatformMapRepository : OmsSqlSugarRepository<CSkuPlatformMap>, ICSkuPlatformMapRepository, IScopedDependency;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user