Compare commits
78 Commits
9b82b45e46
...
production
| Author | SHA1 | Date | |
|---|---|---|---|
| ea7ba3bd3f | |||
| fe7179f6e9 | |||
| f79affb44d | |||
| 749cfeb8f4 | |||
| 6f573c0a29 | |||
| 39bbe59bbc | |||
| 5c852e499d | |||
| 59ed3857d4 | |||
| c814cd9118 | |||
| 436e5d7aca | |||
| 053c5a7c4f | |||
| 82944e11d0 | |||
| bb20ad2c4e | |||
| 99aab3beff | |||
| 19be5dc9dc | |||
| 5c1e3cf601 | |||
| d82b7c28a8 | |||
| 3df738e496 | |||
| 3a9c3438e6 | |||
| 12ebdc9e87 | |||
| 9f1bc2bcdb | |||
| 354ba1dd1b | |||
| 3b9958a700 | |||
| 9c562b12c7 | |||
| 3c9957463d | |||
| 4df71107a9 | |||
| a7fc959d3e | |||
| d96558aa4b | |||
| a2d13c72c3 | |||
| 4e8caa6e2c | |||
| 37a017998c | |||
| 16acb8f3b5 | |||
| 34d2354fd4 | |||
| be2b22c50a | |||
| b260fd56fa | |||
| 1e3ba03c5c | |||
| ee7d29015f | |||
| b053d86006 | |||
| 15044c66cb | |||
| 0983cdf88f | |||
| b8120dd1b9 | |||
| 7ce5539793 | |||
| 9a4f1e6401 | |||
| 091a04f3aa | |||
| 62a0f8594d | |||
| fb81aff095 | |||
| 491b8a0d0f | |||
| be80136ae8 | |||
| 5e9984f0e5 | |||
| 1d07ae4f71 | |||
| 775a9bd75d | |||
| 2620a24597 | |||
| def3b94d89 | |||
| 3ebe0f9f48 | |||
| f4e24f7449 | |||
| 540a2ce4fc | |||
| e57f01965a | |||
| f97c4b0812 | |||
| fd32e15192 | |||
| 4456e504d4 | |||
| cec1e74f6c | |||
| dd9ac48474 | |||
| eb1b249869 | |||
| e8e964f9a0 | |||
| 9050c00297 | |||
| 6048824b33 | |||
| 62a247f097 | |||
| af12f996f1 | |||
| 3b0d9eadd3 | |||
| db07838ecb | |||
| 167991890c | |||
| 1a425f0d93 | |||
| 6f8aefc2ce | |||
| 2bd5c7db78 | |||
| 0728ec2b76 | |||
| 1425fb187b | |||
| 81d2b39ee1 | |||
| fce194a195 |
@@ -1,46 +0,0 @@
|
||||
|
||||
name: Push nuget and docker image Actions Workflow
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- stage_new
|
||||
jobs:
|
||||
Deploy:
|
||||
runs-on: windows
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://git.afrino.co/actions/checkout@v3
|
||||
- name: Setup dotnet
|
||||
uses: https://git.afrino.co/actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
|
||||
- name: Remove Package Source
|
||||
run: dotnet nuget remove source FourSat
|
||||
continue-on-error: true
|
||||
- name: Add Package Source
|
||||
run: dotnet nuget add source --name FourSat --username systemuser --password sZSA7PTiv3pUSQZ https://git.afrino.co/api/packages/FourSat/nuget/index.json --store-password-in-clear-text
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore ".\src\CMSMicroservice.WebApi\CMSMicroservice.WebApi.csproj"
|
||||
- name: Build
|
||||
run: dotnet build ".\src\CMSMicroservice.WebApi\CMSMicroservice.WebApi.csproj" --configuration Release --no-restore
|
||||
- name: Test
|
||||
run: dotnet test ".\src\CMSMicroservice.WebApi\CMSMicroservice.WebApi.csproj" --no-restore --verbosity normal
|
||||
- name: Recycle Apppool
|
||||
run: |
|
||||
& "C:\Windows\System32\inetsrv\appcmd.exe" recycle apppool /apppool.name:cms.kbs1.ir
|
||||
shell: powershell
|
||||
- name: Stop Website
|
||||
run: |
|
||||
& "C:\Windows\System32\inetsrv\appcmd.exe" stop site /site.name:cms.kbs1.ir
|
||||
shell: powershell
|
||||
- name: Publish
|
||||
run: dotnet publish ".\src\CMSMicroservice.WebApi\CMSMicroservice.WebApi.csproj" -c Release -o publish
|
||||
- name: Copy Publish To IIS Directory
|
||||
run: Get-ChildItem -Path "publish\*" | Copy-Item -Destination "E:\kbs1.ir\cms.kbs1.ir\" -Recurse -Force
|
||||
- name: Start Website
|
||||
run: |
|
||||
& "C:\Windows\System32\inetsrv\appcmd.exe" start site /site.name:cms.kbs1.ir
|
||||
shell: powershell
|
||||
@@ -93,11 +93,11 @@ jobs:
|
||||
|
||||
# Apply config (Secret) first, then deployment
|
||||
sshpass -e ssh -o StrictHostKeyChecking=no root@${{ env.K8S_SERVER }} "
|
||||
kubectl apply -f /tmp/cms-config.yaml &&
|
||||
kubectl apply -f /tmp/cms-deployment.yaml &&
|
||||
kubectl apply -n kbs-stage -f /tmp/cms-config.yaml &&
|
||||
kubectl apply -n kbs-stage -f /tmp/cms-deployment.yaml &&
|
||||
crictl rmi ${REGISTRY}/${IMAGE_NAME}:latest 2>/dev/null || true &&
|
||||
kubectl rollout restart deployment/cms &&
|
||||
kubectl rollout status deployment/cms --timeout=180s &&
|
||||
kubectl -n kbs-stage rollout restart deployment/cms &&
|
||||
kubectl -n kbs-stage rollout status deployment/cms --timeout=180s &&
|
||||
rm -f /tmp/cms-config.yaml /tmp/cms-deployment.yaml
|
||||
"
|
||||
echo "✅ Deployed!"
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
done
|
||||
|
||||
if ! docker info >/dev/null 2>&1; then
|
||||
echo "❌ Docker daemon failed to start after 3 minutes"
|
||||
echo "❌ Docker daemon failed to start"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -75,8 +75,10 @@ jobs:
|
||||
|
||||
- name: Build Docker Image
|
||||
run: |
|
||||
DOCKER_BUILDKIT=0 docker build --network host -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
|
||||
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:prod .
|
||||
DOCKER_BUILDKIT=0 docker build --network host \
|
||||
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
|
||||
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:prod \
|
||||
.
|
||||
|
||||
- name: Push to Registry
|
||||
run: |
|
||||
|
||||
@@ -33,12 +33,12 @@ stringData:
|
||||
"DefaultEmail": "info@kbs2.ir",
|
||||
"Packages": {
|
||||
"4": {
|
||||
"GatewayAmountToman": 9700000,
|
||||
"WalletCreditToman": 13000000
|
||||
"GatewayAmountToman": 11200000,
|
||||
"WalletCreditToman": 12656000
|
||||
},
|
||||
"1": {
|
||||
"GatewayAmountToman": 97000000,
|
||||
"WalletCreditToman": 130000000
|
||||
"GatewayAmountToman": 11200000,
|
||||
"WalletCreditToman": 12656000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: cms-appsettings
|
||||
namespace: default
|
||||
namespace: kbs-stage
|
||||
labels:
|
||||
app: cms
|
||||
environment: staging
|
||||
@@ -34,12 +34,12 @@ stringData:
|
||||
"DefaultEmail": "info@kbs2.ir",
|
||||
"Packages": {
|
||||
"4": {
|
||||
"GatewayAmountToman": 1000,
|
||||
"WalletCreditToman": 13000000
|
||||
"GatewayAmountToman": 11200000,
|
||||
"WalletCreditToman": 12656000
|
||||
},
|
||||
"1": {
|
||||
"GatewayAmountToman": 1000,
|
||||
"WalletCreditToman": 130000000
|
||||
"GatewayAmountToman": 11200000,
|
||||
"WalletCreditToman": 12656000
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -53,7 +53,7 @@ stringData:
|
||||
"JwtAudience": "https://localhost",
|
||||
"JwtExpiryInDays": 5,
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Data Source=185.252.31.44,31433; Initial Catalog=Foursat;User ID=sa;Password=87zH26nbqT;Connection Timeout=300000;MultipleActiveResultSets=True;Encrypt=False",
|
||||
"DefaultConnection": "Data Source=mssql-svc.platform.svc.cluster.local,1433; Initial Catalog=Foursat;User ID=sa;Password=87zH26nbqT;Connection Timeout=300000;MultipleActiveResultSets=True;Encrypt=False",
|
||||
"providerName": "System.Data.SqlClient"
|
||||
},
|
||||
"Otp": {
|
||||
|
||||
@@ -4,12 +4,14 @@ apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: cms-uploads-pvc
|
||||
namespace: default
|
||||
namespace: kbs-stage
|
||||
labels:
|
||||
app: cms
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: migrated-local
|
||||
volumeName: pv-cms-uploads-migrated
|
||||
resources:
|
||||
requests:
|
||||
storage: 20Gi
|
||||
@@ -20,7 +22,7 @@ apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: cms
|
||||
namespace: default
|
||||
namespace: kbs-stage
|
||||
labels:
|
||||
app: cms
|
||||
spec:
|
||||
@@ -33,10 +35,12 @@ spec:
|
||||
labels:
|
||||
app: cms
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: gitea-registry-secret
|
||||
containers:
|
||||
- name: cms
|
||||
image: 185.252.31.44:30080/admin/cms:latest
|
||||
imagePullPolicy: Always
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
@@ -89,7 +93,7 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: cms-svc
|
||||
namespace: default
|
||||
namespace: kbs-stage
|
||||
spec:
|
||||
selector:
|
||||
app: cms
|
||||
@@ -107,7 +111,7 @@ apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: cms-ingress
|
||||
namespace: default
|
||||
namespace: kbs-stage
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "nginx"
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
@@ -115,6 +119,7 @@ metadata:
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
tls:
|
||||
- hosts:
|
||||
- cms.se.kbs1.ir
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
Cleanup all side-effects of a test FrontOffice user (register + package + club + wallet + invitations).
|
||||
|
||||
Usage:
|
||||
EXEC dbo.usp_CleanupTestUser @Mobile = N'09037437157', @DryRun = 1;
|
||||
EXEC dbo.usp_CleanupTestUser @Mobile = N'09037437157';
|
||||
EXEC dbo.usp_CleanupTestUser @Mobile = N'09...', @ReparentChildren = 1;
|
||||
EXEC dbo.usp_CleanupTestUser @Mobile = N'09...', @ReversePool = 1, @AbortIfPoolCalculated = 1;
|
||||
|
||||
Pool reverse: for each ClubMembershipCycle, subtract Packages.ActivationFee
|
||||
(fallback: ClubMembership.InitialContribution) from CMS.WeeklyCommissionPools
|
||||
for matching WeekDefinition × PackageId.
|
||||
|
||||
Does NOT delete pool rows (shared). Does NOT claw back commission already paid to OTHER users.
|
||||
Does NOT touch: Packages master, Contracts master, WeekDefinitions, Roles, catalogs.
|
||||
*/
|
||||
CREATE OR ALTER PROCEDURE dbo.usp_CleanupTestUser
|
||||
@UserId BIGINT = NULL,
|
||||
@Mobile NVARCHAR(20) = NULL,
|
||||
@HardDeleteUser BIT = 1,
|
||||
@ReparentChildren BIT = 0,
|
||||
@ReversePool BIT = 1,
|
||||
@AbortIfPoolCalculated BIT = 0,
|
||||
@DryRun BIT = 0
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON;
|
||||
SET QUOTED_IDENTIFIER ON;
|
||||
SET ANSI_NULLS ON;
|
||||
|
||||
IF @UserId IS NULL AND (@Mobile IS NULL OR LTRIM(RTRIM(@Mobile)) = N'')
|
||||
BEGIN
|
||||
RAISERROR(N'Provide @UserId and/or @Mobile.', 16, 1);
|
||||
RETURN;
|
||||
END;
|
||||
|
||||
DECLARE @MobileNorm NVARCHAR(20) = NULL;
|
||||
IF @Mobile IS NOT NULL
|
||||
BEGIN
|
||||
SET @MobileNorm = LTRIM(RTRIM(@Mobile));
|
||||
IF LEFT(@MobileNorm, 3) = N'+98' SET @MobileNorm = N'0' + SUBSTRING(@MobileNorm, 4, 20);
|
||||
IF LEFT(@MobileNorm, 2) = N'98' AND LEN(@MobileNorm) = 12 SET @MobileNorm = N'0' + SUBSTRING(@MobileNorm, 3, 20);
|
||||
IF LEFT(@MobileNorm, 1) = N'9' AND LEN(@MobileNorm) = 10 SET @MobileNorm = N'0' + @MobileNorm;
|
||||
END;
|
||||
|
||||
IF @UserId IS NULL
|
||||
BEGIN
|
||||
SELECT TOP (1) @UserId = Id
|
||||
FROM CMS.Users
|
||||
WHERE Mobile = @MobileNorm
|
||||
OR Mobile = @Mobile
|
||||
OR REPLACE(Mobile, N'+98', N'0') = @MobileNorm
|
||||
ORDER BY IsDeleted ASC, Id DESC;
|
||||
END;
|
||||
|
||||
IF @UserId IS NULL
|
||||
BEGIN
|
||||
RAISERROR(N'User not found for given @UserId/@Mobile.', 16, 1);
|
||||
RETURN;
|
||||
END;
|
||||
|
||||
DECLARE @UserMobile NVARCHAR(20);
|
||||
SELECT @UserMobile = Mobile FROM CMS.Users WHERE Id = @UserId;
|
||||
IF @UserMobile IS NULL
|
||||
BEGIN
|
||||
RAISERROR(N'UserId %I64d not found.', 16, 1, @UserId);
|
||||
RETURN;
|
||||
END;
|
||||
|
||||
DECLARE @ChildCount INT =
|
||||
(SELECT COUNT(*) FROM CMS.Users WHERE NetworkParentId = @UserId AND IsDeleted = 0);
|
||||
|
||||
IF @ChildCount > 0 AND @ReparentChildren = 0 AND @HardDeleteUser = 1
|
||||
BEGIN
|
||||
RAISERROR(N'User has %d active network children. Pass @ReparentChildren=1 or soft-delete only.', 16, 1, @ChildCount);
|
||||
RETURN;
|
||||
END;
|
||||
|
||||
PRINT N'=== usp_CleanupTestUser ===';
|
||||
PRINT N'UserId=' + CAST(@UserId AS NVARCHAR(20)) + N' Mobile=' + ISNULL(@UserMobile, N'?')
|
||||
+ N' DryRun=' + CAST(@DryRun AS NVARCHAR(1))
|
||||
+ N' ReversePool=' + CAST(@ReversePool AS NVARCHAR(1));
|
||||
|
||||
DECLARE @Wallets TABLE (WalletId BIGINT PRIMARY KEY);
|
||||
INSERT INTO @Wallets (WalletId)
|
||||
SELECT Id FROM CMS.UserWallets WHERE UserId = @UserId;
|
||||
|
||||
DECLARE @Tx TABLE (TransactionId BIGINT PRIMARY KEY);
|
||||
INSERT INTO @Tx (TransactionId)
|
||||
SELECT DISTINCT x.TransactionId
|
||||
FROM (
|
||||
SELECT TransactionId FROM CMS.UserPackagePurchases WHERE UserId = @UserId AND TransactionId IS NOT NULL
|
||||
UNION
|
||||
SELECT TransactionId FROM CMS.PaymentTransactions WHERE UserId = @UserId AND TransactionId IS NOT NULL
|
||||
UNION
|
||||
SELECT TransactionId FROM CMS.UserOrders WHERE UserId = @UserId AND TransactionId IS NOT NULL
|
||||
UNION
|
||||
SELECT TransactionId FROM CMS.ManualPayments WHERE UserId = @UserId AND TransactionId IS NOT NULL
|
||||
UNION
|
||||
SELECT TransactionId FROM CMS.DayaLoanContracts WHERE UserId = @UserId AND TransactionId IS NOT NULL
|
||||
UNION
|
||||
SELECT TransactionId FROM CMS.DiscountOrders WHERE UserId = @UserId AND TransactionId IS NOT NULL
|
||||
UNION
|
||||
SELECT h.RefrenceId
|
||||
FROM CMS.UserWalletHistories h
|
||||
INNER JOIN @Wallets w ON w.WalletId = h.WalletId
|
||||
WHERE h.RefrenceId IS NOT NULL
|
||||
) x
|
||||
WHERE x.TransactionId IS NOT NULL;
|
||||
|
||||
DECLARE @Orders TABLE (OrderId BIGINT PRIMARY KEY);
|
||||
INSERT INTO @Orders (OrderId)
|
||||
SELECT Id FROM CMS.UserOrders WHERE UserId = @UserId;
|
||||
|
||||
DECLARE @DiscountOrders TABLE (DiscountOrderId BIGINT PRIMARY KEY);
|
||||
INSERT INTO @DiscountOrders (DiscountOrderId)
|
||||
SELECT Id FROM CMS.DiscountOrders WHERE UserId = @UserId;
|
||||
|
||||
DECLARE @ClubMemberships TABLE (ClubMembershipId BIGINT PRIMARY KEY);
|
||||
INSERT INTO @ClubMemberships (ClubMembershipId)
|
||||
SELECT Id FROM CMS.ClubMemberships WHERE UserId = @UserId;
|
||||
|
||||
DECLARE @Cycles TABLE (CycleId BIGINT PRIMARY KEY);
|
||||
INSERT INTO @Cycles (CycleId)
|
||||
SELECT Id FROM CMS.ClubMembershipCycles WHERE UserId = @UserId
|
||||
UNION
|
||||
SELECT Id FROM CMS.ClubMembershipCycles WHERE ClubMembershipId IN (SELECT ClubMembershipId FROM @ClubMemberships);
|
||||
|
||||
DECLARE @Payouts TABLE (PayoutId BIGINT PRIMARY KEY);
|
||||
INSERT INTO @Payouts (PayoutId)
|
||||
SELECT Id FROM CMS.UserCommissionPayouts WHERE UserId = @UserId;
|
||||
|
||||
DECLARE @PoolReversals TABLE (
|
||||
CycleId BIGINT,
|
||||
PackageId BIGINT,
|
||||
WeekDefinitionId BIGINT NULL,
|
||||
PoolId BIGINT NULL,
|
||||
Fee BIGINT,
|
||||
PoolAmountBefore BIGINT NULL,
|
||||
IsCalculated BIT NULL,
|
||||
Note NVARCHAR(200)
|
||||
);
|
||||
|
||||
INSERT INTO @PoolReversals (CycleId, PackageId, WeekDefinitionId, PoolId, Fee, PoolAmountBefore, IsCalculated, Note)
|
||||
SELECT
|
||||
c.Id,
|
||||
c.PackageId,
|
||||
wd.Id,
|
||||
wp.Id,
|
||||
ISNULL(NULLIF(cm.InitialContribution, 0), ISNULL(p.ActivationFee, 0)),
|
||||
wp.TotalPoolAmount,
|
||||
wp.IsCalculated,
|
||||
CASE
|
||||
WHEN wd.Id IS NULL THEN N'NO_WEEK_MATCH'
|
||||
WHEN wp.Id IS NULL THEN N'NO_POOL_ROW'
|
||||
WHEN wp.IsCalculated = 1 THEN N'POOL_ALREADY_CALCULATED'
|
||||
ELSE N'OK'
|
||||
END
|
||||
FROM CMS.ClubMembershipCycles c
|
||||
LEFT JOIN CMS.ClubMemberships cm ON cm.Id = c.ClubMembershipId
|
||||
LEFT JOIN CMS.Packages p ON p.Id = c.PackageId
|
||||
LEFT JOIN CMS.WeekDefinitions wd
|
||||
ON CAST(c.PackagePurchasedAt AS DATE) >= CAST(wd.StartDate AS DATE)
|
||||
AND CAST(c.PackagePurchasedAt AS DATE) <= CAST(wd.EndDate AS DATE)
|
||||
AND wd.IsDeleted = 0
|
||||
LEFT JOIN CMS.WeeklyCommissionPools wp
|
||||
ON wp.WeekDefinitionId = wd.Id
|
||||
AND wp.PackageId = c.PackageId
|
||||
AND wp.IsDeleted = 0
|
||||
WHERE c.Id IN (SELECT CycleId FROM @Cycles);
|
||||
|
||||
SELECT N'Users' AS Entity, COUNT(*) AS Cnt FROM CMS.Users WHERE Id = @UserId
|
||||
UNION ALL SELECT N'OtpTokens', COUNT(*) FROM CMS.OtpTokens WHERE Mobile IN (@UserMobile, @MobileNorm, @Mobile)
|
||||
UNION ALL SELECT N'UserInvitations', COUNT(*) FROM CMS.UserInvitations WHERE InviterUserId = @UserId OR InviteeUserId = @UserId
|
||||
UNION ALL SELECT N'ClubMemberships', COUNT(*) FROM @ClubMemberships
|
||||
UNION ALL SELECT N'ClubMembershipCycles', COUNT(*) FROM @Cycles
|
||||
UNION ALL SELECT N'UserPackagePurchases', COUNT(*) FROM CMS.UserPackagePurchases WHERE UserId = @UserId
|
||||
UNION ALL SELECT N'PaymentTransactions', COUNT(*) FROM CMS.PaymentTransactions WHERE UserId = @UserId OR Mobile = @UserMobile
|
||||
UNION ALL SELECT N'UserWallets', COUNT(*) FROM @Wallets
|
||||
UNION ALL SELECT N'UserWalletHistories', COUNT(*) FROM CMS.UserWalletHistories WHERE WalletId IN (SELECT WalletId FROM @Wallets)
|
||||
UNION ALL SELECT N'Transactions(collected)', COUNT(*) FROM @Tx
|
||||
UNION ALL SELECT N'NetworkChildren', @ChildCount
|
||||
UNION ALL SELECT N'UserOrders', COUNT(*) FROM @Orders
|
||||
UNION ALL SELECT N'DiscountOrders', COUNT(*) FROM @DiscountOrders
|
||||
UNION ALL SELECT N'UserCommissionPayouts', COUNT(*) FROM @Payouts
|
||||
UNION ALL SELECT N'PoolReversals(planned)', COUNT(*) FROM @PoolReversals WHERE Fee > 0;
|
||||
|
||||
SELECT N'--- Pool reverse plan ---' AS Info;
|
||||
SELECT * FROM @PoolReversals;
|
||||
|
||||
IF @ReversePool = 1 AND @AbortIfPoolCalculated = 1
|
||||
AND EXISTS (SELECT 1 FROM @PoolReversals WHERE IsCalculated = 1 AND Fee > 0)
|
||||
BEGIN
|
||||
RAISERROR(N'One or more target weekly pools are already calculated. Aborting (@AbortIfPoolCalculated=1).', 16, 1);
|
||||
RETURN;
|
||||
END;
|
||||
|
||||
IF @DryRun = 1
|
||||
BEGIN
|
||||
PRINT N'DryRun=1 — no changes committed.';
|
||||
RETURN;
|
||||
END;
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
BEGIN TRY
|
||||
/* ---- Reverse weekly pool charges FIRST (needs cycle + package data) ---- */
|
||||
IF @ReversePool = 1
|
||||
BEGIN
|
||||
UPDATE wp
|
||||
SET wp.TotalPoolAmount = CASE
|
||||
WHEN wp.TotalPoolAmount >= r.Fee THEN wp.TotalPoolAmount - r.Fee
|
||||
ELSE 0
|
||||
END,
|
||||
wp.LastModified = SYSUTCDATETIME()
|
||||
FROM CMS.WeeklyCommissionPools wp
|
||||
INNER JOIN (
|
||||
SELECT PoolId, SUM(Fee) AS Fee
|
||||
FROM @PoolReversals
|
||||
WHERE PoolId IS NOT NULL AND Fee > 0
|
||||
GROUP BY PoolId
|
||||
) r ON r.PoolId = wp.Id;
|
||||
|
||||
PRINT N'Pool reverse applied for ' + CAST(@@ROWCOUNT AS NVARCHAR(20)) + N' pool row(s).';
|
||||
END;
|
||||
|
||||
/* ---- Club ---- */
|
||||
DELETE h FROM CMS.ClubMembershipCycleHistories h
|
||||
WHERE h.UserId = @UserId OR h.ClubMembershipCycleId IN (SELECT CycleId FROM @Cycles);
|
||||
|
||||
DELETE FROM CMS.ClubMembershipCycles
|
||||
WHERE Id IN (SELECT CycleId FROM @Cycles) OR UserId = @UserId;
|
||||
|
||||
DELETE FROM CMS.ClubMembershipHistories WHERE UserId = @UserId
|
||||
OR ClubMembershipId IN (SELECT ClubMembershipId FROM @ClubMemberships);
|
||||
|
||||
DELETE FROM CMS.UserClubFeatures WHERE UserId = @UserId
|
||||
OR ClubMembershipId IN (SELECT ClubMembershipId FROM @ClubMemberships);
|
||||
|
||||
DELETE FROM CMS.ClubMemberships WHERE UserId = @UserId;
|
||||
|
||||
/* ---- Commission payouts of THIS user only ---- */
|
||||
DELETE h FROM CMS.CommissionPayoutHistories h
|
||||
WHERE h.UserId = @UserId OR h.UserCommissionPayoutId IN (SELECT PayoutId FROM @Payouts);
|
||||
|
||||
DELETE FROM CMS.UserCommissionPayouts WHERE UserId = @UserId;
|
||||
|
||||
/* ---- Network ---- */
|
||||
DELETE FROM CMS.NetworkWeeklyBalances WHERE UserId = @UserId;
|
||||
DELETE FROM CMS.NetworkMembershipHistories
|
||||
WHERE UserId = @UserId OR OldParentId = @UserId OR NewParentId = @UserId;
|
||||
|
||||
IF @ChildCount > 0 AND @ReparentChildren = 1
|
||||
UPDATE CMS.Users SET NetworkParentId = NULL, LastModified = SYSUTCDATETIME()
|
||||
WHERE NetworkParentId = @UserId;
|
||||
|
||||
/* ---- Invitations ---- */
|
||||
DELETE FROM CMS.UserInvitations
|
||||
WHERE InviterUserId = @UserId OR InviteeUserId = @UserId;
|
||||
|
||||
/* ---- Package / payment ---- */
|
||||
DELETE FROM CMS.UserPackagePurchases WHERE UserId = @UserId;
|
||||
DELETE FROM CMS.PaymentTransactions
|
||||
WHERE UserId = @UserId OR Mobile = @UserMobile OR Mobile = @MobileNorm;
|
||||
|
||||
/* ---- Manual / Daya ---- */
|
||||
DELETE FROM CMS.ManualPayments WHERE UserId = @UserId;
|
||||
DELETE FROM CMS.DayaLoanContracts WHERE UserId = @UserId;
|
||||
|
||||
/* ---- Discount shop ---- */
|
||||
DELETE d FROM CMS.DiscountOrderDetails d
|
||||
WHERE d.DiscountOrderId IN (SELECT DiscountOrderId FROM @DiscountOrders);
|
||||
DELETE FROM CMS.DiscountOrders WHERE UserId = @UserId;
|
||||
DELETE FROM CMS.DiscountShoppingCarts WHERE UserId = @UserId;
|
||||
|
||||
/* ---- Stock ---- */
|
||||
DELETE FROM CMS.StockMovements
|
||||
WHERE PerformedByUserId = @UserId
|
||||
OR OrderId IN (SELECT OrderId FROM @Orders)
|
||||
OR DiscountOrderId IN (SELECT DiscountOrderId FROM @DiscountOrders);
|
||||
|
||||
/* ---- Orders ---- */
|
||||
DELETE FROM CMS.FactorDetails WHERE OrderId IN (SELECT OrderId FROM @Orders);
|
||||
DELETE FROM CMS.OrderVATs WHERE OrderId IN (SELECT OrderId FROM @Orders);
|
||||
DELETE FROM CMS.UserOrders WHERE UserId = @UserId;
|
||||
|
||||
/* ---- Wallet ---- */
|
||||
DELETE FROM CMS.UserWalletHistories WHERE WalletId IN (SELECT WalletId FROM @Wallets);
|
||||
DELETE FROM CMS.UserWallets WHERE UserId = @UserId;
|
||||
|
||||
/* ---- Cart / address / roles / contracts ---- */
|
||||
DELETE FROM CMS.UserCarts WHERE UserId = @UserId;
|
||||
DELETE FROM CMS.UserAddresses WHERE UserId = @UserId;
|
||||
DELETE FROM CMS.UserRoles WHERE UserId = @UserId;
|
||||
DELETE FROM CMS.UserContracts WHERE UserId = @UserId;
|
||||
|
||||
/* ---- Content authored by test user ---- */
|
||||
UPDATE CMS.BlogPosts SET IsDeleted = 1, LastModified = SYSUTCDATETIME()
|
||||
WHERE AuthorUserId = @UserId AND IsDeleted = 0;
|
||||
|
||||
UPDATE CMS.PublicMessages SET IsDeleted = 1, LastModified = SYSUTCDATETIME()
|
||||
WHERE CreatedByUserId = @UserId AND IsDeleted = 0;
|
||||
|
||||
/* ---- Transactions ---- */
|
||||
DELETE t
|
||||
FROM CMS.Transactions t
|
||||
WHERE t.Id IN (SELECT TransactionId FROM @Tx)
|
||||
AND NOT EXISTS (SELECT 1 FROM CMS.UserPackagePurchases p WHERE p.TransactionId = t.Id)
|
||||
AND NOT EXISTS (SELECT 1 FROM CMS.PaymentTransactions p WHERE p.TransactionId = t.Id)
|
||||
AND NOT EXISTS (SELECT 1 FROM CMS.UserOrders o WHERE o.TransactionId = t.Id)
|
||||
AND NOT EXISTS (SELECT 1 FROM CMS.ManualPayments m WHERE m.TransactionId = t.Id)
|
||||
AND NOT EXISTS (SELECT 1 FROM CMS.DayaLoanContracts d WHERE d.TransactionId = t.Id)
|
||||
AND NOT EXISTS (SELECT 1 FROM CMS.DiscountOrders d WHERE d.TransactionId = t.Id);
|
||||
|
||||
/* ---- OTP ---- */
|
||||
DELETE FROM CMS.OtpTokens
|
||||
WHERE Mobile IN (@UserMobile, @MobileNorm, ISNULL(@Mobile, N''));
|
||||
|
||||
/* ---- User ---- */
|
||||
IF @HardDeleteUser = 1
|
||||
DELETE FROM CMS.Users WHERE Id = @UserId;
|
||||
ELSE
|
||||
UPDATE CMS.Users
|
||||
SET IsDeleted = 1, LastModified = SYSUTCDATETIME()
|
||||
WHERE Id = @UserId;
|
||||
|
||||
COMMIT TRANSACTION;
|
||||
|
||||
PRINT N'Cleanup completed successfully.';
|
||||
SELECT N'DONE' AS Status, @UserId AS UserId, @UserMobile AS Mobile,
|
||||
@HardDeleteUser AS HardDeleted, @ReversePool AS PoolReversed;
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
|
||||
DECLARE @Err NVARCHAR(4000) = ERROR_MESSAGE();
|
||||
RAISERROR(N'Cleanup failed: %s', 16, 1, @Err);
|
||||
END CATCH
|
||||
END;
|
||||
GO
|
||||
+1
@@ -43,4 +43,5 @@ public class GetAllPaymentTransactionByFilterResponseModel
|
||||
public long? TransactionId { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public DateTimeOffset Created { get; set; }
|
||||
public string? UserFullName { get; set; }
|
||||
}
|
||||
|
||||
+34
-21
@@ -19,30 +19,43 @@ public class GetAllPaymentTransactionByFilterQueryHandler
|
||||
.ApplyPaymentTransactionFilter(request.Filter)
|
||||
.ApplyOrder(sortBy: request.SortBy ?? "-Created");
|
||||
|
||||
var models = await (
|
||||
from x in query.PaginatedListAsync(paginationState: request.PaginationState)
|
||||
join u in _context.Users.AsNoTracking() on x.UserId equals u.Id into ug
|
||||
from u in ug.DefaultIfEmpty()
|
||||
select new GetAllPaymentTransactionByFilterResponseModel
|
||||
{
|
||||
Id = x.Id,
|
||||
UserId = x.UserId,
|
||||
Mobile = x.Mobile,
|
||||
GatewayProvider = x.GatewayProvider,
|
||||
Amount = x.Amount,
|
||||
Authority = x.Authority,
|
||||
PaymentStatus = x.PaymentStatus,
|
||||
VerificationStatusCode = x.VerificationStatusCode,
|
||||
VerificationStatusMessage = x.VerificationStatusMessage,
|
||||
RefId = x.RefId,
|
||||
CardPan = x.CardPan,
|
||||
OrderId = x.OrderId,
|
||||
TransactionId = x.TransactionId,
|
||||
Description = x.Description,
|
||||
Created = x.Created,
|
||||
UserFullName = u != null
|
||||
? ((u.FirstName ?? "") + " " + (u.LastName ?? "")).Trim()
|
||||
: null
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
foreach (var model in models)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(model.UserFullName))
|
||||
model.UserFullName = "—";
|
||||
}
|
||||
|
||||
return new GetAllPaymentTransactionByFilterResponseDto
|
||||
{
|
||||
MetaData = await query.GetMetaData(request.PaginationState, cancellationToken),
|
||||
Models = await query
|
||||
.PaginatedListAsync(paginationState: request.PaginationState)
|
||||
.Select(x => new GetAllPaymentTransactionByFilterResponseModel
|
||||
{
|
||||
Id = x.Id,
|
||||
UserId = x.UserId,
|
||||
Mobile = x.Mobile,
|
||||
GatewayProvider = x.GatewayProvider,
|
||||
Amount = x.Amount,
|
||||
Authority = x.Authority,
|
||||
PaymentStatus = x.PaymentStatus,
|
||||
VerificationStatusCode = x.VerificationStatusCode,
|
||||
VerificationStatusMessage = x.VerificationStatusMessage,
|
||||
RefId = x.RefId,
|
||||
CardPan = x.CardPan,
|
||||
OrderId = x.OrderId,
|
||||
TransactionId = x.TransactionId,
|
||||
Description = x.Description,
|
||||
Created = x.Created
|
||||
})
|
||||
.ToListAsync(cancellationToken)
|
||||
Models = models
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+99
-14
@@ -1,3 +1,5 @@
|
||||
using CMSMicroservice.Application.Common;
|
||||
|
||||
namespace CMSMicroservice.Application.TransactionsCQ.Queries.GetAllTransactionsByFilter;
|
||||
|
||||
public class GetAllTransactionsByFilterQueryHandler : IRequestHandler<GetAllTransactionsByFilterQuery, GetAllTransactionsByFilterResponseDto>
|
||||
@@ -16,23 +18,106 @@ public class GetAllTransactionsByFilterQueryHandler : IRequestHandler<GetAllTran
|
||||
.ApplyTransactionFilter(request.Filter)
|
||||
.ApplyOrder(sortBy: request.SortBy ?? "-Created");
|
||||
|
||||
var models = await query
|
||||
.PaginatedListAsync(paginationState: request.PaginationState)
|
||||
.Select(x => new GetAllTransactionsByFilterResponseModel
|
||||
{
|
||||
Id = x.Id,
|
||||
Amount = x.Amount,
|
||||
Description = x.Description,
|
||||
PaymentStatus = x.PaymentStatus,
|
||||
PaymentDate = x.PaymentDate,
|
||||
RefId = x.RefId,
|
||||
Type = x.Type,
|
||||
Created = x.Created
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
await PopulateUserInfoAsync(models, cancellationToken);
|
||||
|
||||
return new GetAllTransactionsByFilterResponseDto
|
||||
{
|
||||
MetaData = await query.GetMetaData(request.PaginationState, cancellationToken),
|
||||
Models = await query
|
||||
.PaginatedListAsync(paginationState: request.PaginationState)
|
||||
.Select(x => new GetAllTransactionsByFilterResponseModel
|
||||
{
|
||||
Id = x.Id,
|
||||
Amount = x.Amount,
|
||||
Description = x.Description,
|
||||
PaymentStatus = x.PaymentStatus,
|
||||
PaymentDate = x.PaymentDate,
|
||||
RefId = x.RefId,
|
||||
Type = x.Type,
|
||||
Created = x.Created
|
||||
})
|
||||
.ToListAsync(cancellationToken)
|
||||
Models = models
|
||||
};
|
||||
}
|
||||
|
||||
private async Task PopulateUserInfoAsync(
|
||||
List<GetAllTransactionsByFilterResponseModel> models,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (models.Count == 0)
|
||||
return;
|
||||
|
||||
var txIds = models.Select(m => m.Id).ToList();
|
||||
var userByTx = new Dictionary<long, long>();
|
||||
|
||||
void Merge(IEnumerable<(long TxId, long UserId)> rows)
|
||||
{
|
||||
foreach (var (txId, userId) in rows)
|
||||
{
|
||||
if (!userByTx.ContainsKey(txId))
|
||||
userByTx[txId] = userId;
|
||||
}
|
||||
}
|
||||
|
||||
// Priority: PaymentTransactions → UserPackagePurchases → UserOrders → ManualPayments
|
||||
var fromPt = await _context.PaymentTransactions.AsNoTracking()
|
||||
.Where(x => x.TransactionId != null && txIds.Contains(x.TransactionId.Value) && x.UserId != null)
|
||||
.Select(x => new { TxId = x.TransactionId!.Value, UserId = x.UserId!.Value })
|
||||
.ToListAsync(cancellationToken);
|
||||
Merge(fromPt.Select(x => (x.TxId, x.UserId)));
|
||||
|
||||
var fromUpp = await _context.UserPackagePurchases.AsNoTracking()
|
||||
.Where(x => x.TransactionId != null && txIds.Contains(x.TransactionId.Value))
|
||||
.Select(x => new { TxId = x.TransactionId!.Value, UserId = x.UserId })
|
||||
.ToListAsync(cancellationToken);
|
||||
Merge(fromUpp.Select(x => (x.TxId, x.UserId)));
|
||||
|
||||
var fromOrders = await _context.UserOrders.AsNoTracking()
|
||||
.Where(x => x.TransactionId != null && txIds.Contains(x.TransactionId.Value))
|
||||
.Select(x => new { TxId = x.TransactionId!.Value, UserId = x.UserId })
|
||||
.ToListAsync(cancellationToken);
|
||||
Merge(fromOrders.Select(x => (x.TxId, x.UserId)));
|
||||
|
||||
var fromManual = await _context.ManualPayments.AsNoTracking()
|
||||
.Where(x => x.TransactionId != null && txIds.Contains(x.TransactionId.Value))
|
||||
.Select(x => new { TxId = x.TransactionId!.Value, UserId = x.UserId })
|
||||
.ToListAsync(cancellationToken);
|
||||
Merge(fromManual.Select(x => (x.TxId, x.UserId)));
|
||||
|
||||
if (userByTx.Count == 0)
|
||||
{
|
||||
foreach (var model in models)
|
||||
model.UserFullName = "—";
|
||||
return;
|
||||
}
|
||||
|
||||
var userIds = userByTx.Values.Distinct().ToList();
|
||||
var users = await _context.Users.AsNoTracking()
|
||||
.Where(u => userIds.Contains(u.Id))
|
||||
.Select(u => new { u.Id, u.FirstName, u.LastName, u.Mobile })
|
||||
.ToDictionaryAsync(u => u.Id, cancellationToken);
|
||||
|
||||
foreach (var model in models)
|
||||
{
|
||||
if (!userByTx.TryGetValue(model.Id, out var userId))
|
||||
{
|
||||
model.UserFullName = "—";
|
||||
continue;
|
||||
}
|
||||
|
||||
model.UserId = userId;
|
||||
if (users.TryGetValue(userId, out var user))
|
||||
{
|
||||
model.Mobile = user.Mobile;
|
||||
var name = UserDisplayName.From(user.FirstName, user.LastName, user.Mobile, user.Id);
|
||||
model.UserFullName = string.IsNullOrWhiteSpace(name) ? "—" : name;
|
||||
}
|
||||
else
|
||||
{
|
||||
model.UserFullName = "—";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -25,4 +25,7 @@ public class GetAllTransactionsByFilterResponseDto
|
||||
//
|
||||
public TransactionType Type { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
public long? UserId { get; set; }
|
||||
public string? UserFullName { get; set; }
|
||||
public string? Mobile { get; set; }
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260222160755_u21")]
|
||||
[Migration("20260222155925_u21")]
|
||||
partial class u21
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -0,0 +1,136 @@
|
||||
-- =============================================
|
||||
-- Stored Procedure: GetNetworkTree
|
||||
-- Description: دریافت درخت شبکه باینری با CTE recursive
|
||||
-- Author: FourSat Team
|
||||
-- Updated: 2026-04-30 — اضافه کردن IsNewActivation، PackageName، PackageId
|
||||
-- منطق هفتهبندی از ActivatedAt به PackagePurchasedAt تغییر کرد
|
||||
-- =============================================
|
||||
|
||||
CREATE OR ALTER PROCEDURE [CMS].[GetNetworkTree]
|
||||
@RootUserId BIGINT,
|
||||
@MaxDepth INT = 100,
|
||||
@IsClubActive BIT = NULL,
|
||||
@ActivationWeekDefinitionId BIGINT = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
-- تاریخ شروع و پایان هفته فیلتر
|
||||
DECLARE @WeekStartDate DATETIME = NULL;
|
||||
DECLARE @WeekEndDate DATETIME = NULL;
|
||||
|
||||
IF @ActivationWeekDefinitionId IS NOT NULL
|
||||
BEGIN
|
||||
SELECT @WeekStartDate = StartDate, @WeekEndDate = EndDate
|
||||
FROM [CMS].[WeekDefinitions]
|
||||
WHERE Id = @ActivationWeekDefinitionId;
|
||||
END
|
||||
|
||||
-- CTE برای پیمایش درخت باینری به صورت recursive
|
||||
;WITH NetworkTreeCTE AS (
|
||||
-- Base case: ریشه درخت
|
||||
SELECT
|
||||
u.Id AS UserId,
|
||||
u.Mobile,
|
||||
u.FirstName,
|
||||
u.LastName,
|
||||
u.ReferralCode,
|
||||
u.LegPosition,
|
||||
u.NetworkParentId AS ParentId,
|
||||
0 AS NetworkLevel,
|
||||
u.Created AS UserCreated
|
||||
FROM [CMS].[Users] u
|
||||
WHERE u.Id = @RootUserId
|
||||
AND u.IsDeleted = 0
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Recursive case: فرزندان
|
||||
SELECT
|
||||
u.Id AS UserId,
|
||||
u.Mobile,
|
||||
u.FirstName,
|
||||
u.LastName,
|
||||
u.ReferralCode,
|
||||
u.LegPosition,
|
||||
u.NetworkParentId AS ParentId,
|
||||
parent.NetworkLevel + 1 AS NetworkLevel,
|
||||
u.Created AS UserCreated
|
||||
FROM [CMS].[Users] u
|
||||
INNER JOIN NetworkTreeCTE parent ON u.NetworkParentId = parent.UserId
|
||||
WHERE u.IsDeleted = 0
|
||||
AND parent.NetworkLevel < @MaxDepth
|
||||
)
|
||||
SELECT
|
||||
t.UserId,
|
||||
t.Mobile,
|
||||
t.FirstName,
|
||||
t.LastName,
|
||||
t.ReferralCode,
|
||||
t.LegPosition,
|
||||
t.ParentId,
|
||||
t.NetworkLevel,
|
||||
cm.ActivatedAt AS ClubActivatedAt,
|
||||
ISNULL(cm.IsActive, 0) AS IsClubActive,
|
||||
|
||||
-- هفته فعالسازی بر اساس Cycle جاری (PackagePurchasedAt)
|
||||
wd.Id AS ActivationWeekDefinitionId,
|
||||
wd.DisplayName AS ActivationWeekDisplayName,
|
||||
|
||||
-- آیا کاربر در هفته هدف Cycle داشته؟
|
||||
CASE WHEN cc_target.UserId IS NOT NULL THEN 1 ELSE 0 END AS IsActivatedInTargetWeek,
|
||||
|
||||
-- نوع فعالسازی: 1=جدید، 0=تمدید/خرید مجدد، NULL=بدون Cycle
|
||||
CASE
|
||||
WHEN cc_eff.CycleNumber IS NULL THEN NULL
|
||||
WHEN cc_eff.CycleNumber = 1 THEN 1
|
||||
ELSE 0
|
||||
END AS IsNewActivation,
|
||||
|
||||
-- نام پکیج موثر (هفته هدف اگر فیلتر باشد، وگرنه پکیج فعلی)
|
||||
ISNULL(p_eff.Title, '') AS PackageName,
|
||||
cc_eff.PackageId AS PackageId,
|
||||
|
||||
t.UserCreated
|
||||
FROM NetworkTreeCTE t
|
||||
LEFT JOIN [CMS].[ClubMemberships] cm ON cm.UserId = t.UserId AND cm.IsDeleted = 0
|
||||
|
||||
-- Cycle جاری — برای نمایش هفته و پکیج وقتی فیلتر هفته نیست
|
||||
OUTER APPLY (
|
||||
SELECT TOP 1 CycleNumber, PackageId, PackagePurchasedAt
|
||||
FROM [CMS].[ClubMembershipCycles]
|
||||
WHERE UserId = t.UserId AND IsCurrentCycle = 1
|
||||
) cc_current
|
||||
|
||||
-- Cycle هفته هدف — برای IsActivatedInTargetWeek و نوع فعالسازی
|
||||
OUTER APPLY (
|
||||
SELECT TOP 1 UserId, CycleNumber, PackageId
|
||||
FROM [CMS].[ClubMembershipCycles]
|
||||
WHERE UserId = t.UserId
|
||||
AND @WeekStartDate IS NOT NULL
|
||||
AND PackagePurchasedAt >= @WeekStartDate
|
||||
AND PackagePurchasedAt < @WeekEndDate
|
||||
ORDER BY CycleNumber ASC
|
||||
) cc_target
|
||||
|
||||
-- Cycle موثر: اگر هفته فیلتر داریم → cc_target، وگرنه → cc_current
|
||||
CROSS APPLY (
|
||||
SELECT
|
||||
CASE WHEN @WeekStartDate IS NOT NULL AND cc_target.UserId IS NOT NULL
|
||||
THEN cc_target.CycleNumber ELSE cc_current.CycleNumber END AS CycleNumber,
|
||||
CASE WHEN @WeekStartDate IS NOT NULL AND cc_target.UserId IS NOT NULL
|
||||
THEN cc_target.PackageId ELSE cc_current.PackageId END AS PackageId
|
||||
) cc_eff
|
||||
|
||||
-- پکیج موثر
|
||||
LEFT JOIN [CMS].[Packages] p_eff ON p_eff.Id = cc_eff.PackageId AND p_eff.IsDeleted = 0
|
||||
|
||||
-- هفته بر اساس PackagePurchasedAt سایکل جاری
|
||||
LEFT JOIN [CMS].[WeekDefinitions] wd
|
||||
ON cc_current.PackagePurchasedAt >= wd.StartDate
|
||||
AND cc_current.PackagePurchasedAt < wd.EndDate
|
||||
|
||||
WHERE (@IsClubActive IS NULL OR ISNULL(cm.IsActive, 0) = @IsClubActive OR t.NetworkLevel = 0)
|
||||
ORDER BY t.NetworkLevel, t.ParentId, t.LegPosition
|
||||
OPTION (MAXRECURSION 0);
|
||||
END
|
||||
|
||||
@@ -58,7 +58,9 @@ public class TwentyCardPaymentService : IPaymentGatewayService
|
||||
{
|
||||
try
|
||||
{
|
||||
var amount = ((long)request.Amount).ToString();
|
||||
// ۲۰کارت amount را به ریال میگیرد (تومان × ۱۰)
|
||||
var amountRial = ((long)request.Amount) * 10L;
|
||||
var amount = amountRial.ToString();
|
||||
var payerName = string.IsNullOrWhiteSpace(request.Name)
|
||||
? (string.IsNullOrWhiteSpace(request.Mobile) ? "کاربر" : request.Mobile)
|
||||
: request.Name.Trim();
|
||||
@@ -85,8 +87,8 @@ public class TwentyCardPaymentService : IPaymentGatewayService
|
||||
AddFormField(content, "national_id", request.NationalId);
|
||||
|
||||
_logger.LogInformation(
|
||||
"TwentyCard payment request: Amount={Amount} Toman, User={UserId}, Invoice={Invoice}",
|
||||
amount, request.UserId, invoice);
|
||||
"TwentyCard payment request: Amount={AmountToman} Toman ({AmountRial} Rial), User={UserId}, Invoice={Invoice}",
|
||||
(long)request.Amount, amountRial, request.UserId, invoice);
|
||||
|
||||
var response = await _httpClient.PostAsync("api/pgw/merchant/request", content, cancellationToken);
|
||||
var responseBody = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
@@ -169,7 +171,7 @@ public class TwentyCardPaymentService : IPaymentGatewayService
|
||||
["terminal_username"] = _terminalUsername,
|
||||
["terminal_password"] = _terminalPassword,
|
||||
["authority"] = refId,
|
||||
["amount"] = ((long)amountInToman).ToString()
|
||||
["amount"] = (((long)amountInToman) * 10L).ToString()
|
||||
});
|
||||
|
||||
var response = await _httpClient.PostAsync("api/pgw/merchant/verify", content, cancellationToken);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>0.0.213</Version>
|
||||
<Version>0.0.214</Version>
|
||||
<DebugType>None</DebugType>
|
||||
<DebugSymbols>False</DebugSymbols>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
|
||||
@@ -66,6 +66,7 @@ message PaymentTransactionModel
|
||||
google.protobuf.Int64Value transaction_id = 13;
|
||||
string description = 14;
|
||||
google.protobuf.Timestamp created_at = 15;
|
||||
string user_full_name = 16;
|
||||
}
|
||||
|
||||
message GetPaymentTransactionSummaryRequest
|
||||
|
||||
@@ -192,6 +192,9 @@ message GetAllTransactionsByFilterResponseModel
|
||||
messages.TransactionType type = 7;
|
||||
}
|
||||
google.protobuf.Timestamp created = 8;
|
||||
google.protobuf.Int64Value user_id = 9;
|
||||
string user_full_name = 10;
|
||||
google.protobuf.StringValue mobile = 11;
|
||||
}
|
||||
|
||||
message GetTransactionSummaryRequest
|
||||
|
||||
@@ -46,7 +46,8 @@ public class PaymentTransactionProfile : IRegister
|
||||
.Map(dest => dest.OrderId, src => src.OrderId ?? string.Empty)
|
||||
.Map(dest => dest.TransactionId, src => src.TransactionId)
|
||||
.Map(dest => dest.Description, src => src.Description ?? string.Empty)
|
||||
.Map(dest => dest.CreatedAt, src => Timestamp.FromDateTimeOffset(src.Created));
|
||||
.Map(dest => dest.CreatedAt, src => Timestamp.FromDateTimeOffset(src.Created))
|
||||
.Map(dest => dest.UserFullName, src => src.UserFullName ?? string.Empty);
|
||||
|
||||
config.NewConfig<ProtoPt.GetPaymentTransactionSummaryRequest, AppPtSummary.GetPaymentTransactionSummaryQuery>()
|
||||
.Map(dest => dest.Filter, src => src.Filter);
|
||||
|
||||
@@ -40,7 +40,10 @@ public class TransactionsProfile : IRegister
|
||||
.Map(dest => dest.PaymentDate, src => src.PaymentDate.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(src.PaymentDate.Value, DateTimeKind.Utc)) : null)
|
||||
.Map(dest => dest.RefId, src => src.RefId)
|
||||
.Map(dest => dest.Type, src => src.Type)
|
||||
.Map(dest => dest.Created, src => Timestamp.FromDateTime(DateTime.SpecifyKind(src.Created, DateTimeKind.Utc)));
|
||||
.Map(dest => dest.Created, src => Timestamp.FromDateTime(DateTime.SpecifyKind(src.Created, DateTimeKind.Utc)))
|
||||
.Map(dest => dest.UserId, src => src.UserId)
|
||||
.Map(dest => dest.UserFullName, src => src.UserFullName ?? string.Empty)
|
||||
.Map(dest => dest.Mobile, src => src.Mobile);
|
||||
|
||||
config.NewConfig<ProtoTx.GetTransactionSummaryRequest, AppTxSummary.GetTransactionSummaryQuery>()
|
||||
.Map(dest => dest.Filter, src => src.Filter);
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"PaymentProvider": "zarinpal",
|
||||
"ZarinPal": {
|
||||
"MerchantId": "4225d555-5fa9-4df0-9b61-1ce152cbbba8",
|
||||
"UseSandbox": false
|
||||
},
|
||||
"TwentyCard": {
|
||||
"BaseUrl": "https://gateway.20card.ir/ipg",
|
||||
"TerminalId": "165674",
|
||||
"TerminalUsername": "ea5y1biehi",
|
||||
"TerminalPassword": "SaLamAT@1404!",
|
||||
"CreditPortalUrl": "https://userpanel.20card.ir/",
|
||||
"DefaultEmail": "info@kbs2.ir",
|
||||
"Packages": {
|
||||
"4": {
|
||||
"GatewayAmountToman": 9700000,
|
||||
"WalletCreditToman": 13000000
|
||||
},
|
||||
"1": {
|
||||
"GatewayAmountToman": 97000000,
|
||||
"WalletCreditToman": 130000000
|
||||
}
|
||||
}
|
||||
},
|
||||
"CmsBaseUrl": "https://cms.kbs2.ir",
|
||||
"FrontOfficeBaseUrl": "https://kbs2.ir",
|
||||
"JwtSecurityKey": "TvlZVx5TJaHs8e9HgUdGzhGP2CIidoI444nAj+8+g7c=",
|
||||
"JwtIssuer": "https://localhost",
|
||||
"JwtAudience": "https://localhost",
|
||||
"JwtExpiryInDays": 5,
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Server=mssql-svc;Database=KBS;User Id=sa;Password=YourStrong@Passw0rd;TrustServerCertificate=True;",
|
||||
"providerName": "System.Data.SqlClient"
|
||||
},
|
||||
"Otp": {
|
||||
"Secret": "K2w8k1h1mH2Qz1kqWk0c8kQ2Pq8q9H1eE2nqN1qQ8x7M=",
|
||||
"MasterCode": "1122334455"
|
||||
},
|
||||
"Monitoring": {
|
||||
"SentryEnabled": false,
|
||||
"SentryDsn": "",
|
||||
"SlackEnabled": false,
|
||||
"SlackWebhookUrl": "",
|
||||
"EmailAlertsEnabled": false,
|
||||
"AdminEmails": [
|
||||
"admin@example.com"
|
||||
],
|
||||
"SmsNotificationsEnabled": false,
|
||||
"SmsApiKey": "",
|
||||
"SmsGatewayUrl": ""
|
||||
},
|
||||
"Email": {
|
||||
"Enabled": true,
|
||||
"SmtpHost": "smtp.gmail.com",
|
||||
"SmtpPort": 587,
|
||||
"SmtpUsername": "your-email@gmail.com",
|
||||
"SmtpPassword": "your-app-password",
|
||||
"FromEmail": "noreply@foursat.com",
|
||||
"FromName": "FourSat CMS",
|
||||
"EnableSsl": true
|
||||
},
|
||||
"Sms": {
|
||||
"Enabled": true,
|
||||
"Provider": "Kavenegar",
|
||||
"KavenegarApiKey": "43676A4E786A6C50452F2F6252507939346A5145764B33566B456454374E657A614468706658656D6534593D",
|
||||
"Sender": "1000001110100"
|
||||
},
|
||||
"DayaPayment": {
|
||||
"BaseUrl": "https://api.daya.ir",
|
||||
"ApiKey": "YOUR_DAYA_API_KEY"
|
||||
},
|
||||
"DayaApi": {
|
||||
"UseMock": false,
|
||||
"BaseAddress": "https://Dayadiamond.ir",
|
||||
"MerchantPermissionKey": "56146364$04sXjethI5WxhItR1Q9xnmFdJzl2BB8Bclsq8dAy7YVSZp3vtt-wP7ivrcCvmKLq",
|
||||
"CacheDurationMinutes": 20
|
||||
},
|
||||
"Chatika": {
|
||||
"Enabled": true,
|
||||
"BaseUrl": "https://api.chatika.ir",
|
||||
"ApiKey": "tIukvL8dnV4cB3yVWcCD9Xyfbj8rBxm5wPt2mLyJCgTsBBoMTWjt6mFEqQwpw-er"
|
||||
},
|
||||
"BackgroundJobs": {
|
||||
"WeeklyCommissionCalculation": {
|
||||
"Enabled": false,
|
||||
"CronExpression": "5 0 * * 0"
|
||||
}
|
||||
},
|
||||
"SeedWorkers": {
|
||||
"MagicWalletCycleSeed": {
|
||||
"Enabled": true
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Kestrel": {
|
||||
"EndpointDefaults": {
|
||||
"Protocols": "Http2"
|
||||
}
|
||||
},
|
||||
"Authentication": {
|
||||
"Authority": "https://ids.domain.com/",
|
||||
"Audience": "domain_api"
|
||||
},
|
||||
"Seq": {
|
||||
"ServerUrl": "http://seq-svc:5341",
|
||||
"ApiKey": "oxpvpUzU1pZxMS4s3Fqq"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,12 @@
|
||||
"DefaultEmail": "info@kbs2.ir",
|
||||
"Packages": {
|
||||
"4": {
|
||||
"GatewayAmountToman": 1000,
|
||||
"WalletCreditToman": 13000000
|
||||
"GatewayAmountToman": 11200000,
|
||||
"WalletCreditToman": 12656000
|
||||
},
|
||||
"1": {
|
||||
"GatewayAmountToman": 1000,
|
||||
"WalletCreditToman": 130000000
|
||||
"GatewayAmountToman": 11200000,
|
||||
"WalletCreditToman": 12656000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"PaymentProvider": "zarinpal",
|
||||
"ZarinPal": {
|
||||
"MerchantId": "4225d555-5fa9-4df0-9b61-1ce152cbbba8",
|
||||
"UseSandbox": true
|
||||
"UseSandbox": false
|
||||
},
|
||||
"TwentyCard": {
|
||||
"BaseUrl": "https://gateway.20card.ir/ipg",
|
||||
@@ -13,31 +13,23 @@
|
||||
"DefaultEmail": "info@kbs2.ir",
|
||||
"Packages": {
|
||||
"4": {
|
||||
"GatewayAmountToman": 9700000,
|
||||
"WalletCreditToman": 13000000
|
||||
"GatewayAmountToman": 11200000,
|
||||
"WalletCreditToman": 12656000
|
||||
},
|
||||
"1": {
|
||||
"GatewayAmountToman": 97000000,
|
||||
"WalletCreditToman": 130000000
|
||||
"GatewayAmountToman": 11200000,
|
||||
"WalletCreditToman": 12656000
|
||||
}
|
||||
}
|
||||
},
|
||||
"CmsBaseUrl": "https://localhost:32846",
|
||||
"FrontOfficeBaseUrl": "http://localhost:5268",
|
||||
"FMS": {
|
||||
"Address": "https://dl.afrino.co"
|
||||
},
|
||||
"CmsBaseUrl": "https://cms.kbs2.ir",
|
||||
"FrontOfficeBaseUrl": "https://kbs2.ir",
|
||||
"JwtSecurityKey": "TvlZVx5TJaHs8e9HgUdGzhGP2CIidoI444nAj+8+g7c=",
|
||||
"JwtIssuer": "https://localhost",
|
||||
"JwtAudience": "https://localhost",
|
||||
"JwtExpiryInDays": 5,
|
||||
"Kestrel": {
|
||||
"EndpointDefaults": {
|
||||
"Protocols": "Http1AndHttp2"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Data Source=185.252.31.44,31433; Initial Catalog=Foursat;User ID=sa;Password=87zH26nbqT;Connection Timeout=300000;MultipleActiveResultSets=True;Encrypt=False",
|
||||
"DefaultConnection": "Server=45.149.79.127,31433;Database=KBS;User Id=sa;Password=YourStrong@Passw0rd;TrustServerCertificate=True;",
|
||||
"providerName": "System.Data.SqlClient"
|
||||
},
|
||||
"Otp": {
|
||||
@@ -45,12 +37,12 @@
|
||||
},
|
||||
"Monitoring": {
|
||||
"SentryEnabled": false,
|
||||
"SentryDsn": "",
|
||||
"SentryDsn": "",
|
||||
"SlackEnabled": false,
|
||||
"SlackWebhookUrl": "",
|
||||
"EmailAlertsEnabled": false,
|
||||
"AdminEmails": [
|
||||
"admin@example.com"
|
||||
"admin@example.com"
|
||||
],
|
||||
"SmsNotificationsEnabled": false,
|
||||
"SmsApiKey": "",
|
||||
@@ -69,7 +61,7 @@
|
||||
"Sms": {
|
||||
"Enabled": true,
|
||||
"Provider": "Kavenegar",
|
||||
"KavenegarApiKey": "43676A4E786A6C50452F2F6252507939346A5145764B33566B456454374E657A614468706658656D6534593D",
|
||||
"KavenegarApiKey": "497263626F32626A48685A6137524C4F78575A766E4C74694A556B79317648424964655030682B554545413D",
|
||||
"Sender": "1000001110100"
|
||||
},
|
||||
"DayaPayment": {
|
||||
@@ -89,7 +81,7 @@
|
||||
},
|
||||
"BackgroundJobs": {
|
||||
"WeeklyCommissionCalculation": {
|
||||
"Enabled": true,
|
||||
"Enabled": false,
|
||||
"CronExpression": "5 0 * * 0"
|
||||
}
|
||||
},
|
||||
@@ -102,12 +94,23 @@
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Kestrel": {
|
||||
"EndpointDefaults": {
|
||||
"Protocols": "Http2"
|
||||
}
|
||||
},
|
||||
"Authentication": {
|
||||
"Authority": "https://ids.domain.com/",
|
||||
"Audience": "domain_api"
|
||||
},
|
||||
"Seq": {
|
||||
"ServerUrl": "https://seq.afrino.co",
|
||||
"ServerUrl": "http://seq-svc:5341",
|
||||
"ApiKey": "oxpvpUzU1pZxMS4s3Fqq"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user