102 lines
4.9 KiB
Transact-SQL
102 lines
4.9 KiB
Transact-SQL
-- ============================================================================
|
||
-- 本地开发种子:root OU + 通知收件用户
|
||
--
|
||
-- 用途
|
||
-- appsettings.local.json 的 AppConfig:Portal:EditionOUMappings 里,EditionId="root"
|
||
-- 映射到 OU `3A0971B1-75B7-C43A-F39D-3CED32E0DF30`。该 ID 来自 dev 环境(这份配置是
|
||
-- Nacos 改造前的 dev 快照),本地种子库里并没有这个 OU。
|
||
--
|
||
-- 不跑这个脚本会怎样
|
||
-- 门户订单支付后自动建租户会失败,且报错极具误导性:
|
||
-- CustomTenantController.CreateAsync 最后一步「发邮件给 root OU」
|
||
-- → _organizationUnitRepository.FindAsync(ouid) 返回 null
|
||
-- → GetMembersAsync(null) 在 EF 表达式求值时抛 NullReferenceException
|
||
-- → 接口 500 → OrderService.CreateTenant 捕获 → 整个事务回滚
|
||
-- 现象是:订单已支付(PaymentStatus=200),但 Orders.TargetTenantID 为 NULL、
|
||
-- SaasTenants 里查无此租户,AMLPortal_TenantEventQueues 留下一条 Status=300(重试中)。
|
||
-- 日志里只有 "An exception was thrown while attempting to evaluate a LINQ query
|
||
-- parameter expression",很难联想到是缺一条 OU 数据。
|
||
-- (GetParentAgentList 里也有同一段 root OU 逻辑,同样依赖它。)
|
||
--
|
||
-- 何时执行
|
||
-- 任何一次重建本地库之后,都要跑一遍:
|
||
-- · `docker compose -f docker-compose.local.yml down -v`(EF 迁移 + 代码种子重建)
|
||
-- · 或 RESTORE `AML-local-dev.bak`(目前本地库的实际数据来源)
|
||
-- 两条路都不会带出这条 OU:它既不在代码种子里(属环境数据),那份 .bak 里也没有。
|
||
--
|
||
-- 执行方式
|
||
-- docker exec -i aml-mssql /opt/mssql-tools18/bin/sqlcmd \
|
||
-- -S localhost -U sa -P "Aml@Local2026" -C -d AbpAML \
|
||
-- -i /dev/stdin < seed-root-ou.sql
|
||
--
|
||
-- 或先 docker cp 进容器再 -i 指定路径。
|
||
--
|
||
-- 幂等:可重复执行,已存在则跳过。
|
||
-- ============================================================================
|
||
|
||
SET NOCOUNT ON;
|
||
|
||
-- 收件邮箱:租户创建成功的通知会真实发送到这里(经 iCS(stag) → smtp.yandex.com,
|
||
-- 不走 appsettings 里的 Abp.Mailing.Smtp,那个 127.0.0.1:25 本地并不存在)。
|
||
-- 想换收件人改这里即可。
|
||
DECLARE @NotifyEmail nvarchar(256) = N'f-r-x@163.com';
|
||
DECLARE @UserName nvarchar(256) = N'local-sales-admin';
|
||
|
||
-- 必须与 appsettings.local.json 中 EditionOUMappings[EditionId="root"].OUIDs[0] 一致
|
||
DECLARE @OuId uniqueidentifier = '3A0971B1-75B7-C43A-F39D-3CED32E0DF30';
|
||
DECLARE @UserId uniqueidentifier;
|
||
|
||
BEGIN TRAN;
|
||
|
||
-- 1) root OU(host 级:TenantId / ParentId 均为 NULL)
|
||
-- Code 只需在 host 范围内不与现有 OU 冲突;种子数据已占用 00001~00003。
|
||
IF NOT EXISTS (SELECT 1 FROM AbpOrganizationUnits WHERE Id = @OuId)
|
||
BEGIN
|
||
INSERT INTO AbpOrganizationUnits
|
||
(Id, TenantId, ParentId, Code, DisplayName, ExtraProperties, ConcurrencyStamp, CreationTime, IsDeleted)
|
||
VALUES
|
||
(@OuId, NULL, NULL, N'00099', N'Local Root OU (sales admin)', N'{}',
|
||
CONVERT(nvarchar(40), NEWID()), GETDATE(), 0);
|
||
PRINT '[seed] AbpOrganizationUnits: root OU created';
|
||
END
|
||
ELSE
|
||
PRINT '[seed] AbpOrganizationUnits: root OU already exists, skipped';
|
||
|
||
-- 2) 通知收件用户(host 级,不设密码 —— 仅用于被 GetMembersAsync 查到并取 Email/UserName)
|
||
SELECT @UserId = Id FROM AbpUsers WHERE TenantId IS NULL AND NormalizedEmail = UPPER(@NotifyEmail);
|
||
|
||
IF @UserId IS NULL
|
||
BEGIN
|
||
SET @UserId = NEWID();
|
||
INSERT INTO AbpUsers
|
||
(Id, TenantId, UserName, NormalizedUserName, Name, Email, NormalizedEmail, EmailConfirmed,
|
||
PasswordHash, SecurityStamp, IsExternal, PhoneNumberConfirmed, TwoFactorEnabled,
|
||
LockoutEnabled, AccessFailedCount, ExtraProperties, ConcurrencyStamp, CreationTime, IsDeleted)
|
||
VALUES
|
||
(@UserId, NULL, @UserName, UPPER(@UserName), N'Local Sales Admin', @NotifyEmail, UPPER(@NotifyEmail), 1,
|
||
NULL, CONVERT(nvarchar(40), NEWID()), 0, 0, 0,
|
||
0, 0, N'{}', CONVERT(nvarchar(40), NEWID()), GETDATE(), 0);
|
||
PRINT '[seed] AbpUsers: notify user created';
|
||
END
|
||
ELSE
|
||
PRINT '[seed] AbpUsers: notify user already exists, skipped';
|
||
|
||
-- 3) 用户挂到 OU 下(FindAllParentAgentsFromOU 靠这层关联找人)
|
||
IF NOT EXISTS (SELECT 1 FROM AbpUserOrganizationUnits WHERE UserId = @UserId AND OrganizationUnitId = @OuId)
|
||
BEGIN
|
||
INSERT INTO AbpUserOrganizationUnits (UserId, OrganizationUnitId, TenantId, CreationTime)
|
||
VALUES (@UserId, @OuId, NULL, GETDATE());
|
||
PRINT '[seed] AbpUserOrganizationUnits: membership created';
|
||
END
|
||
ELSE
|
||
PRINT '[seed] AbpUserOrganizationUnits: membership already exists, skipped';
|
||
|
||
COMMIT;
|
||
|
||
-- 验证:应返回恰好一行
|
||
SELECT u.UserName, u.Email, ou.DisplayName, ou.Code
|
||
FROM AbpUsers u
|
||
JOIN AbpUserOrganizationUnits uo ON uo.UserId = u.Id
|
||
JOIN AbpOrganizationUnits ou ON ou.Id = uo.OrganizationUnitId
|
||
WHERE ou.Id = @OuId;
|