feat: 店铺订单支持仓储代发(wms)建单,放宽创建状态限制
All checks were successful
Build and Deploy / deploy (push) Successful in 4m34s
All checks were successful
Build and Deploy / deploy (push) Successful in 4m34s
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
e85dfb80b5
commit
b7be742046
24
sql/migrate_sales_product_group_wms.sql
Normal file
24
sql/migrate_sales_product_group_wms.sql
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
-- 销售产品分组字典:仓储代发(wms)。可重复执行。
|
||||||
|
-- 用于店铺订单「创建代发订单」选品过滤;销售产品 ProductGroup 填 wms。
|
||||||
|
|
||||||
|
SET @db := DATABASE();
|
||||||
|
|
||||||
|
-- 确保字典类型存在
|
||||||
|
INSERT INTO `t_dict_type` (`DictType`, `Remark`, `TypeSort`, `IsDeleted`, `CreateUserId`)
|
||||||
|
SELECT 'sales_product_group', '销售产品分组', 0, 0, 0
|
||||||
|
FROM DUAL
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM `t_dict_type`
|
||||||
|
WHERE `DictType` = 'sales_product_group' AND `IsDeleted` = 0
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 写入 wms 字典项
|
||||||
|
INSERT INTO `t_dict` (`DictType`, `DictSort`, `DictCode`, `DictName`, `IsDeleted`, `CreateUserId`)
|
||||||
|
SELECT 'sales_product_group', 90, 'wms', '仓储代发', 0, 0
|
||||||
|
FROM DUAL
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM `t_dict`
|
||||||
|
WHERE `DictType` = 'sales_product_group'
|
||||||
|
AND `DictCode` = 'wms'
|
||||||
|
AND `IsDeleted` = 0
|
||||||
|
);
|
||||||
@ -16,12 +16,12 @@ public class DataPermStore(FakeRbacDbContext dbContext, DataPermContextCache cac
|
|||||||
PropertyNameCaseInsensitive = true
|
PropertyNameCaseInsensitive = true
|
||||||
};
|
};
|
||||||
|
|
||||||
// EF Core 原始 SQL 参数不接受 DBNull,空值直接传 null
|
// EF Core 原始 SQL 参数不接受 DBNull;object[] 元素类型为 object,运行时仍可传 null
|
||||||
private static object? SqlParam(object? value) => value;
|
private static object SqlParam(object? value) => value ?? null!;
|
||||||
|
|
||||||
public async Task<UserDataPermDto> GetUserDataPermAsync(Guid userId, CancellationToken cancellationToken = default)
|
public async Task<UserDataPermDto> GetUserDataPermAsync(Guid userId, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var user = await dbContext.Database.SqlQueryRaw<UserHeaderRow>($@"
|
var userSql = $@"
|
||||||
SELECT
|
SELECT
|
||||||
u.`Id` AS `UserId`,
|
u.`Id` AS `UserId`,
|
||||||
u.`Name` AS `UserName`,
|
u.`Name` AS `UserName`,
|
||||||
@ -31,11 +31,13 @@ public class DataPermStore(FakeRbacDbContext dbContext, DataPermContextCache cac
|
|||||||
LEFT JOIN `{FakeTables.Organization}` o ON o.`Id` = u.`OrganizationId` AND o.`IsDeleted` = 0
|
LEFT JOIN `{FakeTables.Organization}` o ON o.`Id` = u.`OrganizationId` AND o.`IsDeleted` = 0
|
||||||
WHERE u.`Id` = {{0}} AND u.`IsDeleted` = 0
|
WHERE u.`Id` = {{0}} AND u.`IsDeleted` = 0
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
", userId.ToString()).FirstOrDefaultAsync(cancellationToken)
|
";
|
||||||
|
var user = await dbContext.Database.SqlQueryRaw<UserHeaderRow>(userSql, userId.ToString())
|
||||||
|
.FirstOrDefaultAsync(cancellationToken)
|
||||||
?? throw new BusinessException("用户不存在");
|
?? throw new BusinessException("用户不存在");
|
||||||
|
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
var orgRows = await dbContext.Database.SqlQueryRaw<UserOrgPermRow>($@"
|
var orgSql = $@"
|
||||||
SELECT
|
SELECT
|
||||||
p.`Id` AS `Id`,
|
p.`Id` AS `Id`,
|
||||||
p.`OrganizationId` AS `OrganizationId`,
|
p.`OrganizationId` AS `OrganizationId`,
|
||||||
@ -47,7 +49,9 @@ public class DataPermStore(FakeRbacDbContext dbContext, DataPermContextCache cac
|
|||||||
FROM `user_org_perm` p
|
FROM `user_org_perm` p
|
||||||
LEFT JOIN `{FakeTables.Organization}` o ON o.`Id` = p.`OrganizationId` AND o.`IsDeleted` = 0
|
LEFT JOIN `{FakeTables.Organization}` o ON o.`Id` = p.`OrganizationId` AND o.`IsDeleted` = 0
|
||||||
WHERE p.`IsDeleted` = 0 AND p.`UserId` = {{0}}
|
WHERE p.`IsDeleted` = 0 AND p.`UserId` = {{0}}
|
||||||
", userId.ToString()).ToListAsync(cancellationToken);
|
";
|
||||||
|
var orgRows = await dbContext.Database.SqlQueryRaw<UserOrgPermRow>(orgSql, userId.ToString())
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
var scopeRows = await dbContext.Database.SqlQueryRaw<UserDataScopeRow>("""
|
var scopeRows = await dbContext.Database.SqlQueryRaw<UserDataScopeRow>("""
|
||||||
SELECT p.`Id` AS `Id`, p.`DataScope` AS `DataScope`, p.`DataResourceCode` AS `DataResourceCode`, p.`ExpireTime` AS `ExpireTime`
|
SELECT p.`Id` AS `Id`, p.`DataScope` AS `DataScope`, p.`DataResourceCode` AS `DataResourceCode`, p.`ExpireTime` AS `ExpireTime`
|
||||||
@ -128,20 +132,24 @@ public class DataPermStore(FakeRbacDbContext dbContext, DataPermContextCache cac
|
|||||||
|
|
||||||
public async Task<RoleDataPermDto> GetRoleDataPermAsync(Guid roleId, CancellationToken cancellationToken = default)
|
public async Task<RoleDataPermDto> GetRoleDataPermAsync(Guid roleId, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var role = await dbContext.Database.SqlQueryRaw<RoleHeaderRow>($@"
|
var roleSql = $@"
|
||||||
SELECT r.`Id` AS `RoleId`, r.`Name` AS `RoleName`
|
SELECT r.`Id` AS `RoleId`, r.`Name` AS `RoleName`
|
||||||
FROM `{FakeTables.Role}` r
|
FROM `{FakeTables.Role}` r
|
||||||
WHERE r.`Id` = {{0}} AND r.`IsDeleted` = 0
|
WHERE r.`Id` = {{0}} AND r.`IsDeleted` = 0
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
", roleId.ToString()).FirstOrDefaultAsync(cancellationToken)
|
";
|
||||||
|
var role = await dbContext.Database.SqlQueryRaw<RoleHeaderRow>(roleSql, roleId.ToString())
|
||||||
|
.FirstOrDefaultAsync(cancellationToken)
|
||||||
?? throw new BusinessException("角色不存在");
|
?? throw new BusinessException("角色不存在");
|
||||||
|
|
||||||
var orgRows = await dbContext.Database.SqlQueryRaw<RoleOrgPermRow>($@"
|
var orgSql = $@"
|
||||||
SELECT p.`Id` AS `Id`, p.`OrganizationId` AS `OrganizationId`, o.`Name` AS `OrganizationName`, p.`OrgScope` AS `OrgScope`
|
SELECT p.`Id` AS `Id`, p.`OrganizationId` AS `OrganizationId`, o.`Name` AS `OrganizationName`, p.`OrgScope` AS `OrgScope`
|
||||||
FROM `role_org_perm` p
|
FROM `role_org_perm` p
|
||||||
LEFT JOIN `{FakeTables.Organization}` o ON o.`Id` = p.`OrganizationId` AND o.`IsDeleted` = 0
|
LEFT JOIN `{FakeTables.Organization}` o ON o.`Id` = p.`OrganizationId` AND o.`IsDeleted` = 0
|
||||||
WHERE p.`IsDeleted` = 0 AND p.`RoleId` = {{0}}
|
WHERE p.`IsDeleted` = 0 AND p.`RoleId` = {{0}}
|
||||||
", roleId.ToString()).ToListAsync(cancellationToken);
|
";
|
||||||
|
var orgRows = await dbContext.Database.SqlQueryRaw<RoleOrgPermRow>(orgSql, roleId.ToString())
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
var scopeRows = await dbContext.Database.SqlQueryRaw<RoleDataScopeRow>("""
|
var scopeRows = await dbContext.Database.SqlQueryRaw<RoleDataScopeRow>("""
|
||||||
SELECT p.`Id` AS `Id`, p.`DataScope` AS `DataScope`, p.`DataResourceCode` AS `DataResourceCode`
|
SELECT p.`Id` AS `Id`, p.`DataScope` AS `DataScope`, p.`DataResourceCode` AS `DataResourceCode`
|
||||||
|
|||||||
@ -11,6 +11,9 @@ public class PlatformStorePageInput
|
|||||||
|
|
||||||
public int? CustomerId { get; set; }
|
public int? CustomerId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 平台编码:tiktok / temu / shopee / lazada;空或 all 表示全部。
|
||||||
|
/// </summary>
|
||||||
public string? PlatformCode { get; set; }
|
public string? PlatformCode { get; set; }
|
||||||
|
|
||||||
public string? StoreNickname { get; set; }
|
public string? StoreNickname { get; set; }
|
||||||
@ -147,11 +150,6 @@ public class PlatformStoreOrderListInput : PlatformStorePageInput
|
|||||||
{
|
{
|
||||||
public int? StoreId { get; set; }
|
public int? StoreId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 平台编码:tiktok / temu / shopee / lazada;空或 all 表示全部。
|
|
||||||
/// </summary>
|
|
||||||
public string? PlatformCode { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 履约状态:pending / toShip / shipped / canceled / suspended / completed;空或 all 表示全部。
|
/// 履约状态:pending / toShip / shipped / canceled / suspended / completed;空或 all 表示全部。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -104,12 +104,12 @@ public class UserProfileRepository(FakeRbacDbContext dbContext) : IScopedDepende
|
|||||||
sql,
|
sql,
|
||||||
[
|
[
|
||||||
userId.ToString(),
|
userId.ToString(),
|
||||||
// EF Core 参数不接受 DBNull,空值直接传 null 即可映射为 SQL NULL
|
// EF Core 参数不接受 DBNull;object[] 元素类型为 object,运行时仍可传 null
|
||||||
(object?)NormalizeOptional(profile.Phone),
|
SqlParam(NormalizeOptional(profile.Phone)),
|
||||||
(object?)NormalizeOptional(profile.EmployeeNo),
|
SqlParam(NormalizeOptional(profile.EmployeeNo)),
|
||||||
(object?)NormalizeOptional(profile.WechatId),
|
SqlParam(NormalizeOptional(profile.WechatId)),
|
||||||
(object?)NormalizeOptional(profile.WechatQrCode),
|
SqlParam(NormalizeOptional(profile.WechatQrCode)),
|
||||||
(object?)NormalizeOptional(profile.WecomKfUrl),
|
SqlParam(NormalizeOptional(profile.WecomKfUrl)),
|
||||||
profile.IsEnabled ? 1 : 0,
|
profile.IsEnabled ? 1 : 0,
|
||||||
],
|
],
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
@ -175,10 +175,10 @@ public class UserProfileRepository(FakeRbacDbContext dbContext) : IScopedDepende
|
|||||||
sql,
|
sql,
|
||||||
[
|
[
|
||||||
userId.ToString(),
|
userId.ToString(),
|
||||||
(object?)NormalizeOptional(profile.Phone),
|
SqlParam(NormalizeOptional(profile.Phone)),
|
||||||
(object?)NormalizeOptional(profile.WechatId),
|
SqlParam(NormalizeOptional(profile.WechatId)),
|
||||||
(object?)NormalizeOptional(profile.WechatQrCode),
|
SqlParam(NormalizeOptional(profile.WechatQrCode)),
|
||||||
(object?)NormalizeOptional(profile.WecomKfUrl),
|
SqlParam(NormalizeOptional(profile.WecomKfUrl)),
|
||||||
],
|
],
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
@ -255,6 +255,8 @@ public class UserProfileRepository(FakeRbacDbContext dbContext) : IScopedDepende
|
|||||||
return value.Trim();
|
return value.Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static object SqlParam(object? value) => value ?? null!;
|
||||||
|
|
||||||
private sealed class UserProfileRow
|
private sealed class UserProfileRow
|
||||||
{
|
{
|
||||||
public string? Phone { get; set; }
|
public string? Phone { get; set; }
|
||||||
|
|||||||
@ -204,9 +204,9 @@ public class CustomerService : ApplicationService
|
|||||||
// 同步 RBAC 登录邮箱,覆盖注册时的 @noemail.local 占位
|
// 同步 RBAC 登录邮箱,覆盖注册时的 @noemail.local 占位
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await RbacDbContext.Database.ExecuteSqlRawAsync(
|
var syncEmailSql =
|
||||||
$"UPDATE `{FakeTables.User}` SET `Email` = {{0}} WHERE `userid` = {{1}} AND `IsDeleted` = 0",
|
$"UPDATE `{FakeTables.User}` SET `Email` = {{0}} WHERE `userid` = {{1}} AND `IsDeleted` = 0";
|
||||||
email, customer.UserId);
|
await RbacDbContext.Database.ExecuteSqlRawAsync(syncEmailSql, email, customer.UserId);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -392,8 +392,8 @@ public class CustomerService : ApplicationService
|
|||||||
|
|
||||||
private async Task<Guid?> GetRbacUserGuidByMapIdAsync(int mapId)
|
private async Task<Guid?> GetRbacUserGuidByMapIdAsync(int mapId)
|
||||||
{
|
{
|
||||||
return await RbacDbContext.Database.SqlQueryRaw<Guid?>(
|
var sql = $"select `Id` as `Value` from `{FakeTables.User}` where `userid` = {{0}} and `IsDeleted` = 0";
|
||||||
$"select `Id` as `Value` from `{FakeTables.User}` where `userid` = {{0}} and `IsDeleted` = 0", mapId)
|
return await RbacDbContext.Database.SqlQueryRaw<Guid?>(sql, mapId)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,7 +442,7 @@ public class CustomerService : ApplicationService
|
|||||||
(string.IsNullOrWhiteSpace(keyword) ||
|
(string.IsNullOrWhiteSpace(keyword) ||
|
||||||
customer.CustomerCode.Contains(keyword) ||
|
customer.CustomerCode.Contains(keyword) ||
|
||||||
customer.CustomerName.Contains(keyword) ||
|
customer.CustomerName.Contains(keyword) ||
|
||||||
customer.Phone.Contains(keyword) ||
|
(customer.Phone ?? string.Empty).Contains(keyword) ||
|
||||||
(customer.Email ?? string.Empty).Contains(keyword)) &&
|
(customer.Email ?? string.Empty).Contains(keyword)) &&
|
||||||
(!hasStatusFilter || customer.Status == statusValue) &&
|
(!hasStatusFilter || customer.Status == statusValue) &&
|
||||||
(!hasCustomerGroupFilter || customer.CustomerGroup == customerGroupValue) &&
|
(!hasCustomerGroupFilter || customer.CustomerGroup == customerGroupValue) &&
|
||||||
@ -497,8 +497,8 @@ public class CustomerService : ApplicationService
|
|||||||
|
|
||||||
private async Task<int> GetRbacUserMapIdOrThrowAsync(Guid id)
|
private async Task<int> GetRbacUserMapIdOrThrowAsync(Guid id)
|
||||||
{
|
{
|
||||||
var userId = await RbacDbContext.Database.SqlQueryRaw<int?>(
|
var sql = $"select `userid` as `Value` from `{FakeTables.User}` where `Id` = {{0}}";
|
||||||
$"select `userid` as `Value` from `{FakeTables.User}` where `Id` = {{0}}", id)
|
var userId = await RbacDbContext.Database.SqlQueryRaw<int?>(sql, id)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (!userId.HasValue || userId.Value <= 0)
|
if (!userId.HasValue || userId.Value <= 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -17,7 +17,7 @@ namespace Oms.Application.Services.OpenApi;
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 仓配对外开放 API(Swagger 文档分组:warehouseOpenApi)。
|
/// 仓配对外开放 API(Swagger 文档分组:warehouseOpenApi)。
|
||||||
/// 鉴权:Authorization: Bearer {token}(账号密码换取,有效期 48 小时)。
|
/// 鉴权:Authorization: {token}(账号密码换取,有效期 48 小时;无需 Bearer 前缀)。
|
||||||
/// 与客户 HMAC 分组 openApi 相互独立。
|
/// 与客户 HMAC 分组 openApi 相互独立。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiExplorerSettings(GroupName = "warehouseOpenApi")]
|
[ApiExplorerSettings(GroupName = "warehouseOpenApi")]
|
||||||
@ -45,7 +45,7 @@ public class WarehouseOpenApiService : ApplicationService
|
|||||||
LazyServiceProvider.GetRequiredService<OutboundRecordService>();
|
LazyServiceProvider.GetRequiredService<OutboundRecordService>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取 Token(有效期 48 小时)。后续请求 Header:Authorization: Bearer {token}。
|
/// 获取 Token(有效期 48 小时)。后续请求 Header:Authorization: {token}(无需 Bearer)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpPost("/openapi/warehouse/token")]
|
[HttpPost("/openapi/warehouse/token")]
|
||||||
@ -75,7 +75,7 @@ public class WarehouseOpenApiService : ApplicationService
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 入库上传件重尺。与操作台「入库扫描」页「入库」按钮同源(POST /operation/inbound/create)。
|
/// 入库上传件重尺。与操作台「入库扫描」页「入库」按钮同源(POST /operation/inbound/create)。
|
||||||
/// 写入收货件重尺、入库记录,并触发入库计费/自动触发。重量默认 kg,长度默认 cm。
|
/// 写入收货件重尺、入库记录,并触发入库计费/自动触发。重量默认 kg,长度默认 cm。
|
||||||
/// Header:Authorization: Bearer {token}。
|
/// Header:Authorization: {token}(无需 Bearer)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[HttpPost("/openapi/warehouse/inbound")]
|
[HttpPost("/openapi/warehouse/inbound")]
|
||||||
@ -86,7 +86,7 @@ public class WarehouseOpenApiService : ApplicationService
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 上传件重尺。mode:0=入库(同「入库」按钮),1=出库(同「出库」按钮)。
|
/// 上传件重尺。mode:0=入库(同「入库」按钮),1=出库(同「出库」按钮)。
|
||||||
/// Header:Authorization: Bearer {token}。
|
/// Header:Authorization: {token}(无需 Bearer)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[HttpPost("/openapi/warehouse/weight/upload")]
|
[HttpPost("/openapi/warehouse/weight/upload")]
|
||||||
@ -164,7 +164,7 @@ public class WarehouseOpenApiService : ApplicationService
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 上传红外图片。Base64 解码后转存 COS,写入订单附件 fileType=infraredImage(Source=upload),
|
/// 上传红外图片。Base64 解码后转存 COS,写入订单附件 fileType=infraredImage(Source=upload),
|
||||||
/// 与订单详情「附件」Tab →「红外图片」→「点击上传」(POST /order/files/upload)同源展示。
|
/// 与订单详情「附件」Tab →「红外图片」→「点击上传」(POST /order/files/upload)同源展示。
|
||||||
/// 成功无响应体。Header:Authorization: Bearer {token}。
|
/// 成功无响应体。Header:Authorization: {token}(无需 Bearer)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[HttpPost("/openapi/warehouse/image/upload")]
|
[HttpPost("/openapi/warehouse/image/upload")]
|
||||||
|
|||||||
@ -525,12 +525,12 @@ internal static class ParcelBillingCalculator
|
|||||||
priceSnapshot.Prices.Select(p => (p.Status, p.Details)));
|
priceSnapshot.Prices.Select(p => (p.Status, p.Details)));
|
||||||
|
|
||||||
private static IReadOnlyList<string> CollectDistinctDetailZoneKeys(
|
private static IReadOnlyList<string> CollectDistinctDetailZoneKeys(
|
||||||
IEnumerable<(byte Status, List<ChannelPriceDetailOutput>? Details)> prices)
|
IEnumerable<(byte Status, List<ChannelPriceDetailOutput> Details)> prices)
|
||||||
{
|
{
|
||||||
var zones = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
var zones = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
foreach (var price in prices.Where(x => EnableStatusRules.IsEnabled(x.Status)))
|
foreach (var price in prices.Where(x => EnableStatusRules.IsEnabled(x.Status)))
|
||||||
{
|
{
|
||||||
foreach (var detail in price.Details ?? [])
|
foreach (var detail in price.Details)
|
||||||
{
|
{
|
||||||
var factors = ParseFactorDictionary(detail.FactorValuesJson);
|
var factors = ParseFactorDictionary(detail.FactorValuesJson);
|
||||||
if (TryGetZoneFactorText(factors, out var zone))
|
if (TryGetZoneFactorText(factors, out var zone))
|
||||||
|
|||||||
@ -5,6 +5,7 @@ using Oms.Application.Dtos.Orders;
|
|||||||
using Oms.Application.Dtos.PlatformStore;
|
using Oms.Application.Dtos.PlatformStore;
|
||||||
using Oms.Application.Dtos.Shared;
|
using Oms.Application.Dtos.Shared;
|
||||||
using Oms.Application.Shared;
|
using Oms.Application.Shared;
|
||||||
|
using Oms.Domain.ChannelsAggregate.Consts;
|
||||||
using Oms.Domain.ChannelsAggregate.Repositories;
|
using Oms.Domain.ChannelsAggregate.Repositories;
|
||||||
using Oms.Domain.CustomersAggregate.Repositories;
|
using Oms.Domain.CustomersAggregate.Repositories;
|
||||||
using Oms.Domain.OrdersAggregate.Consts;
|
using Oms.Domain.OrdersAggregate.Consts;
|
||||||
@ -409,7 +410,8 @@ public class PlatformStoreOrderService : ApplicationService
|
|||||||
input.ProductCode,
|
input.ProductCode,
|
||||||
input.WarehouseId,
|
input.WarehouseId,
|
||||||
input.Items,
|
input.Items,
|
||||||
requireBind: false);
|
requireBind: false,
|
||||||
|
requireWmsProductGroup: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -429,7 +431,30 @@ public class PlatformStoreOrderService : ApplicationService
|
|||||||
input.ProductCode,
|
input.ProductCode,
|
||||||
input.WarehouseId,
|
input.WarehouseId,
|
||||||
input.Items,
|
input.Items,
|
||||||
requireBind: true);
|
requireBind: true,
|
||||||
|
requireWmsProductGroup: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 批量创建仓储代发订单:与代发运单同链路(建单+绑定+产品计费),
|
||||||
|
/// 但产品必须属于分组 wms(仓储代发)。
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("/order/platform-store-order/batch-create-fulfill-wms")]
|
||||||
|
public async Task<PlatformStoreOrderBatchCreateLogisticsOutput> BatchCreateFulfillWmsAsync(
|
||||||
|
PlatformStoreOrderBatchCreateFulfillInput input)
|
||||||
|
{
|
||||||
|
if (input.WarehouseId <= 0)
|
||||||
|
{
|
||||||
|
throw new BusinessException("请指定发货仓库");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await BatchCreateLogisticsInternalAsync(
|
||||||
|
input.CustomerId,
|
||||||
|
input.ProductCode,
|
||||||
|
input.WarehouseId,
|
||||||
|
input.Items,
|
||||||
|
requireBind: true,
|
||||||
|
requireWmsProductGroup: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<PlatformStoreOrderBatchCreateLogisticsOutput> BatchCreateLogisticsInternalAsync(
|
private async Task<PlatformStoreOrderBatchCreateLogisticsOutput> BatchCreateLogisticsInternalAsync(
|
||||||
@ -437,7 +462,8 @@ public class PlatformStoreOrderService : ApplicationService
|
|||||||
string? productCode,
|
string? productCode,
|
||||||
int? warehouseId,
|
int? warehouseId,
|
||||||
List<PlatformStoreOrderBatchCreateItemInput> items,
|
List<PlatformStoreOrderBatchCreateItemInput> items,
|
||||||
bool requireBind)
|
bool requireBind,
|
||||||
|
bool requireWmsProductGroup = false)
|
||||||
{
|
{
|
||||||
if (items == null || items.Count == 0)
|
if (items == null || items.Count == 0)
|
||||||
{
|
{
|
||||||
@ -451,14 +477,14 @@ public class PlatformStoreOrderService : ApplicationService
|
|||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(productCode))
|
if (string.IsNullOrWhiteSpace(productCode))
|
||||||
{
|
{
|
||||||
throw new BusinessException("请选择运输方式");
|
throw new BusinessException(requireWmsProductGroup ? "请选择仓储代发产品" : "请选择运输方式");
|
||||||
}
|
}
|
||||||
|
|
||||||
var customerId = await WarehouseCustomerHelper.ResolveRequiredCustomerIdAsync(
|
var customerId = await WarehouseCustomerHelper.ResolveRequiredCustomerIdAsync(
|
||||||
CustomerRepository,
|
CustomerRepository,
|
||||||
CurrentUser,
|
CurrentUser,
|
||||||
customerIdInput);
|
customerIdInput);
|
||||||
var resolvedProductCode = await ResolveProductCodeRequiredAsync(productCode);
|
var resolvedProductCode = await ResolveProductCodeRequiredAsync(productCode, requireWmsProductGroup);
|
||||||
var batchNo = $"PSL{DateTime.Now:yyyyMMddHHmmss}";
|
var batchNo = $"PSL{DateTime.Now:yyyyMMddHHmmss}";
|
||||||
var output = new PlatformStoreOrderBatchCreateLogisticsOutput { BatchNo = batchNo };
|
var output = new PlatformStoreOrderBatchCreateLogisticsOutput { BatchNo = batchNo };
|
||||||
|
|
||||||
@ -488,9 +514,11 @@ public class PlatformStoreOrderService : ApplicationService
|
|||||||
itemInput.Receiver,
|
itemInput.Receiver,
|
||||||
requireBind ? itemInput.BindItems : null);
|
requireBind ? itemInput.BindItems : null);
|
||||||
item.Success = true;
|
item.Success = true;
|
||||||
item.Message = requireBind
|
item.Message = requireWmsProductGroup
|
||||||
? $"已创建物流单 {result.ServiceNo} 并绑定"
|
? $"已创建代发订单 {result.ServiceNo} 并绑定"
|
||||||
: $"已创建物流单 {result.ServiceNo}";
|
: requireBind
|
||||||
|
? $"已创建物流单 {result.ServiceNo} 并绑定"
|
||||||
|
: $"已创建物流单 {result.ServiceNo}";
|
||||||
item.LinkedOrderId = result.OrderId;
|
item.LinkedOrderId = result.OrderId;
|
||||||
item.ServiceNo = result.ServiceNo;
|
item.ServiceNo = result.ServiceNo;
|
||||||
output.SuccessCount++;
|
output.SuccessCount++;
|
||||||
@ -523,7 +551,7 @@ public class PlatformStoreOrderService : ApplicationService
|
|||||||
List<OrderStockBindSaveItemInput>? bindItems)
|
List<OrderStockBindSaveItemInput>? bindItems)
|
||||||
{
|
{
|
||||||
var order = await GetOrderOrThrowAsync(customerId, storeOrderId);
|
var order = await GetOrderOrThrowAsync(customerId, storeOrderId);
|
||||||
await EnsureMatchableAsync(order);
|
await EnsureCanCreateLogisticsAsync(order);
|
||||||
|
|
||||||
var lines = await StoreOrderLineRepository.GetListAsync(x => x.StoreOrderId == order.Id && !x.IsShipExcluded);
|
var lines = await StoreOrderLineRepository.GetListAsync(x => x.StoreOrderId == order.Id && !x.IsShipExcluded);
|
||||||
if (lines.Count == 0)
|
if (lines.Count == 0)
|
||||||
@ -639,7 +667,9 @@ public class PlatformStoreOrderService : ApplicationService
|
|||||||
await OrderRepository.UpdateAsync(logisticsOrder);
|
await OrderRepository.UpdateAsync(logisticsOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> ResolveProductCodeRequiredAsync(string productCode)
|
private async Task<string> ResolveProductCodeRequiredAsync(
|
||||||
|
string productCode,
|
||||||
|
bool requireWmsProductGroup = false)
|
||||||
{
|
{
|
||||||
var normalized = productCode.Trim();
|
var normalized = productCode.Trim();
|
||||||
var product = await SalesProductRepository.GetByProductCodeAsync(normalized);
|
var product = await SalesProductRepository.GetByProductCodeAsync(normalized);
|
||||||
@ -648,6 +678,15 @@ public class PlatformStoreOrderService : ApplicationService
|
|||||||
throw new BusinessException("销售产品不存在或未启用");
|
throw new BusinessException("销售产品不存在或未启用");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (requireWmsProductGroup
|
||||||
|
&& !string.Equals(
|
||||||
|
product.ProductGroup?.Trim(),
|
||||||
|
SalesProductGroupCodes.Wms,
|
||||||
|
StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
throw new BusinessException("仓储代发订单仅可选择产品分组为「仓储代发(wms)」的销售产品");
|
||||||
|
}
|
||||||
|
|
||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -734,6 +773,26 @@ public class PlatformStoreOrderService : ApplicationService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建运单/代发:不限制履约状态;拦截搁置与未删除的已关联物流单。
|
||||||
|
/// </summary>
|
||||||
|
private async Task EnsureCanCreateLogisticsAsync(SalesStoreOrder order)
|
||||||
|
{
|
||||||
|
if (order.IsSuspended)
|
||||||
|
{
|
||||||
|
throw new BusinessException("订单已搁置,不能创建");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (order.LinkedOrderId is > 0)
|
||||||
|
{
|
||||||
|
var linked = await OrderRepository.FirstOrDefaultAsync(x => x.Id == order.LinkedOrderId.Value);
|
||||||
|
if (linked != null && linked.OrderStatus != OrderStatusCodes.Deleted)
|
||||||
|
{
|
||||||
|
throw new BusinessException($"已关联小包订单 {linked.ServiceNo},不能重复创建");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task EnsureMatchableAsync(SalesStoreOrder order)
|
private async Task EnsureMatchableAsync(SalesStoreOrder order)
|
||||||
{
|
{
|
||||||
if (order.IsSuspended)
|
if (order.IsSuspended)
|
||||||
|
|||||||
@ -277,7 +277,7 @@ public class NoticeService : ApplicationService
|
|||||||
x.ParentCustomerId == null &&
|
x.ParentCustomerId == null &&
|
||||||
(x.CustomerName.Contains(pattern) ||
|
(x.CustomerName.Contains(pattern) ||
|
||||||
x.CustomerCode.Contains(pattern) ||
|
x.CustomerCode.Contains(pattern) ||
|
||||||
x.Phone.Contains(pattern));
|
(x.Phone != null && x.Phone.Contains(pattern)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var total = await CustomerRepository.CountAsync(predicate);
|
var total = await CustomerRepository.CountAsync(predicate);
|
||||||
|
|||||||
@ -167,14 +167,14 @@ public static class OmsRbacRegistrationGuard
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hit = await rbacDbContext.Database
|
var sql = $@"
|
||||||
.SqlQueryRaw<int?>(
|
|
||||||
$@"
|
|
||||||
SELECT 1 AS Value
|
SELECT 1 AS Value
|
||||||
FROM `{FakeTables.Organization}`
|
FROM `{FakeTables.Organization}`
|
||||||
WHERE `Id` = {{0}} AND `IsDeleted` = 0
|
WHERE `Id` = {{0}} AND `IsDeleted` = 0
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
", organizationId.Value.ToString())
|
";
|
||||||
|
var hit = await rbacDbContext.Database
|
||||||
|
.SqlQueryRaw<int?>(sql, organizationId.Value.ToString())
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
|
|
||||||
if (!hit.HasValue)
|
if (!hit.HasValue)
|
||||||
@ -201,14 +201,14 @@ public static class OmsRbacRegistrationGuard
|
|||||||
|
|
||||||
foreach (var roleId in ids)
|
foreach (var roleId in ids)
|
||||||
{
|
{
|
||||||
var hit = await rbacDbContext.Database
|
var sql = $@"
|
||||||
.SqlQueryRaw<int?>(
|
|
||||||
$@"
|
|
||||||
SELECT 1 AS Value
|
SELECT 1 AS Value
|
||||||
FROM `{FakeTables.Role}`
|
FROM `{FakeTables.Role}`
|
||||||
WHERE `Id` = {{0}} AND `IsDeleted` = 0
|
WHERE `Id` = {{0}} AND `IsDeleted` = 0
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
", roleId.ToString())
|
";
|
||||||
|
var hit = await rbacDbContext.Database
|
||||||
|
.SqlQueryRaw<int?>(sql, roleId.ToString())
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
|
|
||||||
if (!hit.HasValue)
|
if (!hit.HasValue)
|
||||||
@ -492,19 +492,20 @@ public static class OmsRbacRegistrationGuard
|
|||||||
// 唯一索引不按 IsDeleted 区分,软删除账号仍会触发 Duplicate entry。
|
// 唯一索引不按 IsDeleted 区分,软删除账号仍会触发 Duplicate entry。
|
||||||
if (excludeUserId is Guid id && id != Guid.Empty)
|
if (excludeUserId is Guid id && id != Guid.Empty)
|
||||||
{
|
{
|
||||||
|
var sql = $"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `Account` = {{0}} AND `Id` <> {{1}} LIMIT 1";
|
||||||
var hit = await rbacDbContext.Database
|
var hit = await rbacDbContext.Database
|
||||||
.SqlQueryRaw<int?>(
|
.SqlQueryRaw<int?>(sql, account, id.ToString())
|
||||||
$"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `Account` = {{0}} AND `Id` <> {{1}} LIMIT 1",
|
|
||||||
account, id.ToString())
|
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
return hit.HasValue;
|
return hit.HasValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var any = await rbacDbContext.Database
|
{
|
||||||
.SqlQueryRaw<int?>(
|
var sql = $"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `Account` = {{0}} LIMIT 1";
|
||||||
$"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `Account` = {{0}} LIMIT 1", account)
|
var any = await rbacDbContext.Database
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.SqlQueryRaw<int?>(sql, account)
|
||||||
return any.HasValue;
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
|
return any.HasValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<bool> ExistsUserByEmailAsync(
|
private static async Task<bool> ExistsUserByEmailAsync(
|
||||||
@ -515,19 +516,20 @@ public static class OmsRbacRegistrationGuard
|
|||||||
{
|
{
|
||||||
if (excludeUserId is Guid id && id != Guid.Empty)
|
if (excludeUserId is Guid id && id != Guid.Empty)
|
||||||
{
|
{
|
||||||
|
var sql = $"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `Email` = {{0}} AND `Id` <> {{1}} LIMIT 1";
|
||||||
var hit = await rbacDbContext.Database
|
var hit = await rbacDbContext.Database
|
||||||
.SqlQueryRaw<int?>(
|
.SqlQueryRaw<int?>(sql, email, id.ToString())
|
||||||
$"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `Email` = {{0}} AND `Id` <> {{1}} LIMIT 1",
|
|
||||||
email, id.ToString())
|
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
return hit.HasValue;
|
return hit.HasValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var any = await rbacDbContext.Database
|
{
|
||||||
.SqlQueryRaw<int?>(
|
var sql = $"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `Email` = {{0}} LIMIT 1";
|
||||||
$"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `Email` = {{0}} LIMIT 1", email)
|
var any = await rbacDbContext.Database
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.SqlQueryRaw<int?>(sql, email)
|
||||||
return any.HasValue;
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
|
return any.HasValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<bool> ExistsUserByPhoneAsync(
|
private static async Task<bool> ExistsUserByPhoneAsync(
|
||||||
@ -538,19 +540,20 @@ public static class OmsRbacRegistrationGuard
|
|||||||
{
|
{
|
||||||
if (excludeUserId is Guid id && id != Guid.Empty)
|
if (excludeUserId is Guid id && id != Guid.Empty)
|
||||||
{
|
{
|
||||||
|
var sql = $"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `Phone` = {{0}} AND `Id` <> {{1}} LIMIT 1";
|
||||||
var hit = await rbacDbContext.Database
|
var hit = await rbacDbContext.Database
|
||||||
.SqlQueryRaw<int?>(
|
.SqlQueryRaw<int?>(sql, phone, id.ToString())
|
||||||
$"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `Phone` = {{0}} AND `Id` <> {{1}} LIMIT 1",
|
|
||||||
phone, id.ToString())
|
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
return hit.HasValue;
|
return hit.HasValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var any = await rbacDbContext.Database
|
{
|
||||||
.SqlQueryRaw<int?>(
|
var sql = $"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `Phone` = {{0}} LIMIT 1";
|
||||||
$"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `Phone` = {{0}} LIMIT 1", phone)
|
var any = await rbacDbContext.Database
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.SqlQueryRaw<int?>(sql, phone)
|
||||||
return any.HasValue;
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
|
return any.HasValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<bool> ExistsUserByWechatIdAsync(
|
private static async Task<bool> ExistsUserByWechatIdAsync(
|
||||||
@ -561,19 +564,20 @@ public static class OmsRbacRegistrationGuard
|
|||||||
{
|
{
|
||||||
if (excludeUserId is Guid id && id != Guid.Empty)
|
if (excludeUserId is Guid id && id != Guid.Empty)
|
||||||
{
|
{
|
||||||
|
var sql = $"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `WechatId` = {{0}} AND `Id` <> {{1}} LIMIT 1";
|
||||||
var hit = await rbacDbContext.Database
|
var hit = await rbacDbContext.Database
|
||||||
.SqlQueryRaw<int?>(
|
.SqlQueryRaw<int?>(sql, wechatId, id.ToString())
|
||||||
$"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `WechatId` = {{0}} AND `Id` <> {{1}} LIMIT 1",
|
|
||||||
wechatId, id.ToString())
|
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
return hit.HasValue;
|
return hit.HasValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var any = await rbacDbContext.Database
|
{
|
||||||
.SqlQueryRaw<int?>(
|
var sql = $"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `WechatId` = {{0}} LIMIT 1";
|
||||||
$"SELECT 1 AS Value FROM `{FakeTables.User}` WHERE `WechatId` = {{0}} LIMIT 1", wechatId)
|
var any = await rbacDbContext.Database
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.SqlQueryRaw<int?>(sql, wechatId)
|
||||||
return any.HasValue;
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
|
return any.HasValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ExtractDeepMessage(Exception ex)
|
private static string ExtractDeepMessage(Exception ex)
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
namespace Oms.Domain.ChannelsAggregate.Consts;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 销售产品分组编码(字典 sales_product_group.DictCode)。
|
||||||
|
/// </summary>
|
||||||
|
public static class SalesProductGroupCodes
|
||||||
|
{
|
||||||
|
/// <summary>仓储代发(贴单出库 / 仓内履约计费)。</summary>
|
||||||
|
public const string Wms = "wms";
|
||||||
|
}
|
||||||
@ -43,7 +43,7 @@ public static class OrderFileTypeCodes
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !string.Equals(fileType.Trim(), DownstreamLabel, StringComparison.OrdinalIgnoreCase);
|
return !string.Equals(fileType!.Trim(), DownstreamLabel, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string Normalize(string fileType) => fileType.Trim();
|
public static string Normalize(string fileType) => fileType.Trim();
|
||||||
|
|||||||
@ -140,7 +140,7 @@ public class CustomerRepository : OmsSqlSugarRepository<Customer>, ICustomerRepo
|
|||||||
query = query.Where((g, c) =>
|
query = query.Where((g, c) =>
|
||||||
c.CustomerCode.Contains(kw) ||
|
c.CustomerCode.Contains(kw) ||
|
||||||
c.CustomerName.Contains(kw) ||
|
c.CustomerName.Contains(kw) ||
|
||||||
c.Phone.Contains(kw));
|
(c.Phone != null && c.Phone.Contains(kw)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var total = await query.CountAsync(cancellationToken);
|
var total = await query.CountAsync(cancellationToken);
|
||||||
|
|||||||
@ -908,15 +908,15 @@ public partial class AuthService(
|
|||||||
|
|
||||||
private async Task<Guid?> GetRbacUserGuidByMapIdAsync(int userId, CancellationToken cancellationToken)
|
private async Task<Guid?> GetRbacUserGuidByMapIdAsync(int userId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return await rbacDbContext.Database.SqlQueryRaw<Guid?>(
|
var sql = $"select `Id` as `Value` from `{FakeTables.User}` where `IsDeleted` = 0 and `userid` = {{0}}";
|
||||||
$"select `Id` as `Value` from `{FakeTables.User}` where `IsDeleted` = 0 and `userid` = {{0}}", userId)
|
return await rbacDbContext.Database.SqlQueryRaw<Guid?>(sql, userId)
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int?> GetMapIdByUserGuidAsync(Guid userId, CancellationToken cancellationToken)
|
private async Task<int?> GetMapIdByUserGuidAsync(Guid userId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return await rbacDbContext.Database.SqlQueryRaw<int?>(
|
var sql = $"select `userid` as `Value` from `{FakeTables.User}` where `Id` = {{0}}";
|
||||||
$"select `userid` as `Value` from `{FakeTables.User}` where `Id` = {{0}}", userId)
|
return await rbacDbContext.Database.SqlQueryRaw<int?>(sql, userId)
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,36 @@
|
|||||||
|
namespace SimpleAdmin.Api.Auth;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 仓配开放 API:Authorization 直接传 JWT(不要求 Bearer 前缀)。
|
||||||
|
/// 在进入 JwtBearer 前补上 Bearer,以便复用现有鉴权。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WarehouseOpenApiAuthHeaderMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
public Task InvokeAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
if (context.Request.Path.StartsWithSegments("/openapi/warehouse"))
|
||||||
|
{
|
||||||
|
NormalizeAuthorizationHeader(context.Request.Headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void NormalizeAuthorizationHeader(IHeaderDictionary headers)
|
||||||
|
{
|
||||||
|
var auth = headers.Authorization.ToString();
|
||||||
|
if (string.IsNullOrWhiteSpace(auth))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auth = auth.Trim();
|
||||||
|
if (auth.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无 scheme:Authorization: {token}
|
||||||
|
headers.Authorization = "Bearer " + auth;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -201,6 +201,9 @@ public class SimpleAdminApiModule : FakeModule
|
|||||||
await next();
|
await next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 仓配开放 API:Authorization 直接传 token(可不带 Bearer)
|
||||||
|
app.UseMiddleware<WarehouseOpenApiAuthHeaderMiddleware>();
|
||||||
|
|
||||||
// 认证和授权
|
// 认证和授权
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|||||||
@ -22,8 +22,8 @@ public class SimpleJwtService(IOptions<FakeJwtOptions> jwtOptions, UserService u
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var claims = await base.GenerateClaimsByUserIdAsync(userId, cancellationToken);
|
var claims = await base.GenerateClaimsByUserIdAsync(userId, cancellationToken);
|
||||||
var mapId = await rbacDbContext.Database.SqlQueryRaw<int?>(
|
var sql = $"select `userid` as `Value` from `{FakeTables.User}` where `Id` = {{0}}";
|
||||||
$"select `userid` as `Value` from `{FakeTables.User}` where `Id` = {{0}}", userId)
|
var mapId = await rbacDbContext.Database.SqlQueryRaw<int?>(sql, userId)
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
if (mapId.HasValue)
|
if (mapId.HasValue)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -325,8 +325,8 @@ public class WeChatCustomerService(
|
|||||||
|
|
||||||
private async Task<int> GetRbacUserMapIdOrThrowAsync(Guid id, CancellationToken cancellationToken)
|
private async Task<int> GetRbacUserMapIdOrThrowAsync(Guid id, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var userId = await rbacDbContext.Database.SqlQueryRaw<int?>(
|
var sql = $"select `userid` as `Value` from `{FakeTables.User}` where `Id` = {{0}}";
|
||||||
$"select `userid` as `Value` from `{FakeTables.User}` where `Id` = {{0}}", id)
|
var userId = await rbacDbContext.Database.SqlQueryRaw<int?>(sql, id)
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
if (!userId.HasValue || userId.Value <= 0)
|
if (!userId.HasValue || userId.Value <= 0)
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user