8ec0910cdf
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 6m36s
- Updated GenerateJwtTokenService to include IApplicationDbContext for accessing user addresses. - Added logic to check if a user has an address and included this information as a claim in the generated JWT token. - Improved overall token generation process by incorporating additional user data for better context.
258 lines
8.4 KiB
Transact-SQL
258 lines
8.4 KiB
Transact-SQL
/*
|
|
One-time cleanup: ClubMemberships without a successful UserPackagePurchases row.
|
|
|
|
Goal after this script:
|
|
every non-deleted ClubMembership user has >= 1 successful UPP
|
|
(TransactionId IS NULL OR Transaction.PaymentStatus = 0 / Success)
|
|
|
|
Priority for missing rows:
|
|
1) ManualPayments Approved + successful Transaction -> reuse same TransactionId
|
|
PurchaseMethod = Manual (3)
|
|
2) else ClubMembershipCycles / membership dates -> create synthetic Success Transaction + UPP
|
|
PurchaseMethod priority:
|
|
Cycle.PurchaseMethod (if not None)
|
|
-> ClubMembership.PurchaseMethod (if not None)
|
|
-> Users.PackagePurchaseMethod (if not None)
|
|
-> Manual (3) as last resort
|
|
|
|
Safe to re-run (skips users who already have a successful UPP).
|
|
|
|
Run against CMS database once, then verify Orphans left = 0.
|
|
*/
|
|
|
|
SET NOCOUNT ON;
|
|
SET XACT_ABORT ON;
|
|
|
|
BEGIN TRAN;
|
|
|
|
------------------------------------------------------------
|
|
-- 1) From ManualPayments (reuse existing Success transaction)
|
|
------------------------------------------------------------
|
|
;WITH OrphanClubUsers AS (
|
|
SELECT m.UserId,
|
|
COALESCE(NULLIF(m.LastPackageId, 0), NULLIF(m.FirstPackageId, 0)) AS PackageIdHint
|
|
FROM CMS.ClubMemberships m
|
|
WHERE m.IsDeleted = 0
|
|
AND NOT EXISTS (
|
|
SELECT 1
|
|
FROM CMS.UserPackagePurchases upp
|
|
LEFT JOIN CMS.Transactions t ON t.Id = upp.TransactionId AND t.IsDeleted = 0
|
|
WHERE upp.UserId = m.UserId
|
|
AND upp.IsDeleted = 0
|
|
AND (upp.TransactionId IS NULL OR t.PaymentStatus = 0)
|
|
)
|
|
),
|
|
BestManual AS (
|
|
SELECT mp.UserId,
|
|
mp.Id AS ManualPaymentId,
|
|
mp.Amount,
|
|
mp.TransactionId,
|
|
COALESCE(mp.ApprovedAt, mp.Created) AS PurchasedAt,
|
|
ROW_NUMBER() OVER (
|
|
PARTITION BY mp.UserId
|
|
ORDER BY COALESCE(mp.ApprovedAt, mp.Created) DESC, mp.Id DESC
|
|
) AS rn
|
|
FROM CMS.ManualPayments mp
|
|
INNER JOIN CMS.Transactions t ON t.Id = mp.TransactionId AND t.IsDeleted = 0 AND t.PaymentStatus = 0
|
|
WHERE mp.IsDeleted = 0
|
|
AND mp.Status = 1 -- Approved
|
|
AND mp.TransactionId IS NOT NULL
|
|
),
|
|
BasePkg AS (
|
|
SELECT TOP (1) Id AS PackageId, Price
|
|
FROM CMS.Packages
|
|
WHERE IsDeleted = 0 AND IsBasePackage = 1
|
|
ORDER BY Id
|
|
)
|
|
INSERT INTO CMS.UserPackagePurchases
|
|
(
|
|
UserId, PackageId, PurchaseMethod, PurchasedAt, Amount,
|
|
OrderId, TransactionId, Created, CreatedBy, LastModified, LastModifiedBy, IsDeleted
|
|
)
|
|
SELECT
|
|
o.UserId,
|
|
COALESCE(o.PackageIdHint, b.PackageId),
|
|
3, -- Manual
|
|
bm.PurchasedAt,
|
|
bm.Amount,
|
|
NULL,
|
|
bm.TransactionId,
|
|
SYSUTCDATETIME(),
|
|
NULL,
|
|
SYSUTCDATETIME(),
|
|
NULL,
|
|
0
|
|
FROM OrphanClubUsers o
|
|
INNER JOIN BestManual bm ON bm.UserId = o.UserId AND bm.rn = 1
|
|
CROSS JOIN BasePkg b
|
|
WHERE COALESCE(o.PackageIdHint, b.PackageId) IS NOT NULL;
|
|
|
|
DECLARE @FromManual INT = @@ROWCOUNT;
|
|
PRINT CONCAT('Inserted from ManualPayments: ', @FromManual);
|
|
|
|
------------------------------------------------------------
|
|
-- 2) Remaining orphans from Cycle / membership dates
|
|
------------------------------------------------------------
|
|
;WITH OrphanClubUsers AS (
|
|
SELECT
|
|
m.UserId,
|
|
m.PurchaseMethod,
|
|
u.PackagePurchaseMethod AS UserPurchaseMethod,
|
|
COALESCE(NULLIF(m.LastPackageId, 0), NULLIF(m.FirstPackageId, 0)) AS MembPackageId,
|
|
COALESCE(m.FirstActivationDate, m.LastActivationDate, m.ActivatedAt, m.Created) AS MembPurchasedAt
|
|
FROM CMS.ClubMemberships m
|
|
INNER JOIN CMS.Users u ON u.Id = m.UserId AND u.IsDeleted = 0
|
|
WHERE m.IsDeleted = 0
|
|
AND NOT EXISTS (
|
|
SELECT 1
|
|
FROM CMS.UserPackagePurchases upp
|
|
LEFT JOIN CMS.Transactions t ON t.Id = upp.TransactionId AND t.IsDeleted = 0
|
|
WHERE upp.UserId = m.UserId
|
|
AND upp.IsDeleted = 0
|
|
AND (upp.TransactionId IS NULL OR t.PaymentStatus = 0)
|
|
)
|
|
),
|
|
BestCycle AS (
|
|
SELECT
|
|
c.UserId,
|
|
c.PackageId,
|
|
c.PackageAmount,
|
|
c.PurchaseMethod,
|
|
c.PackagePurchasedAt,
|
|
ROW_NUMBER() OVER (PARTITION BY c.UserId ORDER BY c.CycleNumber ASC, c.Id ASC) AS rn
|
|
FROM CMS.ClubMembershipCycles c
|
|
WHERE c.IsDeleted = 0
|
|
),
|
|
BasePkg AS (
|
|
SELECT TOP (1) Id AS PackageId, Price
|
|
FROM CMS.Packages
|
|
WHERE IsDeleted = 0 AND IsBasePackage = 1
|
|
ORDER BY Id
|
|
),
|
|
ToInsert AS (
|
|
SELECT
|
|
o.UserId,
|
|
COALESCE(NULLIF(bc.PackageId, 0), o.MembPackageId, b.PackageId) AS PackageId,
|
|
CASE
|
|
WHEN bc.PurchaseMethod IS NOT NULL AND bc.PurchaseMethod <> 0 THEN bc.PurchaseMethod
|
|
WHEN o.PurchaseMethod IS NOT NULL AND o.PurchaseMethod <> 0 THEN o.PurchaseMethod
|
|
WHEN o.UserPurchaseMethod IS NOT NULL AND o.UserPurchaseMethod <> 0 THEN o.UserPurchaseMethod
|
|
ELSE 3 -- Manual (last resort)
|
|
END AS PurchaseMethod,
|
|
COALESCE(bc.PackagePurchasedAt, o.MembPurchasedAt, SYSUTCDATETIME()) AS PurchasedAt,
|
|
CASE
|
|
WHEN bc.PackageAmount IS NOT NULL AND bc.PackageAmount > 0 THEN bc.PackageAmount
|
|
ELSE COALESCE(p.Price, b.Price, 0)
|
|
END AS Amount
|
|
FROM OrphanClubUsers o
|
|
LEFT JOIN BestCycle bc ON bc.UserId = o.UserId AND bc.rn = 1
|
|
CROSS JOIN BasePkg b
|
|
LEFT JOIN CMS.Packages p ON p.Id = COALESCE(NULLIF(bc.PackageId, 0), o.MembPackageId, b.PackageId) AND p.IsDeleted = 0
|
|
)
|
|
SELECT *
|
|
INTO #BackfillCandidates
|
|
FROM ToInsert
|
|
WHERE PackageId IS NOT NULL AND Amount > 0;
|
|
|
|
DECLARE @Remaining INT = (SELECT COUNT(*) FROM #BackfillCandidates);
|
|
PRINT CONCAT('Remaining orphans to synthetic backfill: ', @Remaining);
|
|
|
|
DECLARE @UserId BIGINT, @PackageId BIGINT, @PurchaseMethod INT, @PurchasedAt DATETIME2, @Amount BIGINT;
|
|
DECLARE @TxId BIGINT;
|
|
|
|
DECLARE cur CURSOR LOCAL FAST_FORWARD FOR
|
|
SELECT UserId, PackageId, PurchaseMethod, PurchasedAt, Amount
|
|
FROM #BackfillCandidates;
|
|
|
|
OPEN cur;
|
|
FETCH NEXT FROM cur INTO @UserId, @PackageId, @PurchaseMethod, @PurchasedAt, @Amount;
|
|
|
|
DECLARE @Synthetic INT = 0;
|
|
|
|
WHILE @@FETCH_STATUS = 0
|
|
BEGIN
|
|
INSERT INTO CMS.Transactions
|
|
(
|
|
Amount, Description, PaymentStatus, PaymentDate, RefId, Type,
|
|
Created, CreatedBy, LastModified, LastModifiedBy, IsDeleted
|
|
)
|
|
VALUES
|
|
(
|
|
@Amount,
|
|
CONCAT(N'بکفیل عضویت باشگاه — UserId ', @UserId),
|
|
0, -- Success
|
|
@PurchasedAt,
|
|
CONCAT('UPP-BACKFILL-', @UserId, '-', @PackageId),
|
|
2, -- DepositExternal1
|
|
SYSUTCDATETIME(), NULL, SYSUTCDATETIME(), NULL, 0
|
|
);
|
|
|
|
SET @TxId = SCOPE_IDENTITY();
|
|
|
|
INSERT INTO CMS.UserPackagePurchases
|
|
(
|
|
UserId, PackageId, PurchaseMethod, PurchasedAt, Amount,
|
|
OrderId, TransactionId, Created, CreatedBy, LastModified, LastModifiedBy, IsDeleted
|
|
)
|
|
VALUES
|
|
(
|
|
@UserId, @PackageId, @PurchaseMethod, @PurchasedAt, @Amount,
|
|
NULL, @TxId, SYSUTCDATETIME(), NULL, SYSUTCDATETIME(), NULL, 0
|
|
);
|
|
|
|
SET @Synthetic += 1;
|
|
FETCH NEXT FROM cur INTO @UserId, @PackageId, @PurchaseMethod, @PurchasedAt, @Amount;
|
|
END
|
|
|
|
CLOSE cur;
|
|
DEALLOCATE cur;
|
|
|
|
PRINT CONCAT('Inserted synthetic UPP rows: ', @Synthetic);
|
|
|
|
DROP TABLE #BackfillCandidates;
|
|
|
|
------------------------------------------------------------
|
|
-- Verify invariant
|
|
------------------------------------------------------------
|
|
DECLARE @ClubCount INT =
|
|
(
|
|
SELECT COUNT(*) FROM CMS.ClubMemberships WHERE IsDeleted = 0
|
|
);
|
|
|
|
DECLARE @OrphansLeft INT =
|
|
(
|
|
SELECT COUNT(*)
|
|
FROM CMS.ClubMemberships m
|
|
WHERE m.IsDeleted = 0
|
|
AND NOT EXISTS (
|
|
SELECT 1
|
|
FROM CMS.UserPackagePurchases upp
|
|
LEFT JOIN CMS.Transactions t ON t.Id = upp.TransactionId AND t.IsDeleted = 0
|
|
WHERE upp.UserId = m.UserId
|
|
AND upp.IsDeleted = 0
|
|
AND (upp.TransactionId IS NULL OR t.PaymentStatus = 0)
|
|
)
|
|
);
|
|
|
|
DECLARE @SuccessfulBuyers INT =
|
|
(
|
|
SELECT COUNT(DISTINCT upp.UserId)
|
|
FROM CMS.UserPackagePurchases upp
|
|
LEFT JOIN CMS.Transactions t ON t.Id = upp.TransactionId AND t.IsDeleted = 0
|
|
WHERE upp.IsDeleted = 0
|
|
AND (upp.TransactionId IS NULL OR t.PaymentStatus = 0)
|
|
);
|
|
|
|
PRINT CONCAT('ClubMemberships: ', @ClubCount);
|
|
PRINT CONCAT('Distinct successful UPP users: ', @SuccessfulBuyers);
|
|
PRINT CONCAT('Orphans left (must be 0): ', @OrphansLeft);
|
|
|
|
IF @OrphansLeft > 0
|
|
BEGIN
|
|
ROLLBACK;
|
|
THROW 50001, 'Backfill incomplete — orphans remain; transaction rolled back.', 1;
|
|
END
|
|
|
|
COMMIT;
|
|
PRINT 'Backfill committed successfully.';
|