AML/docker-compose-local-dev/seed-root-ou.sql

102 lines
4.9 KiB
Transact-SQL
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

-- ============================================================================
-- 本地开发种子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 OUhost 级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;