|
|
@@ -97,126 +97,80 @@ export class MessageRecordService {
|
|
|
public async getOverallStatistics() {
|
|
|
try {
|
|
|
const result = await MessageRecord.aggregate([
|
|
|
- // 1. 根据状态对消息进行分类
|
|
|
+ // 1. 按状态和inforeground字段分组,计算每个类别的记录数
|
|
|
{
|
|
|
$group: {
|
|
|
- _id: "$status",
|
|
|
+ _id: { status: "$status", inforeground: "$inforeground" },
|
|
|
count: { $sum: 1 },
|
|
|
},
|
|
|
},
|
|
|
- // 2. 将结果转换成一个更容易处理的格式
|
|
|
+ // 2. 将数据重组,计算总数、发送成功数、送达数、打开数、展示数
|
|
|
{
|
|
|
$group: {
|
|
|
_id: null,
|
|
|
- total_records: { $sum: "$count" },
|
|
|
- status_counts: {
|
|
|
- $push: {
|
|
|
- k: { $toString: "$_id" },
|
|
|
- v: "$count",
|
|
|
+ totalRecords: { $sum: "$count" },
|
|
|
+ // 发送成功数:status >= 1
|
|
|
+ sent: {
|
|
|
+ $sum: { $cond: [{ $gte: ["$_id.status", 1] }, "$count", 0] },
|
|
|
+ },
|
|
|
+ // 送达数:status >= 2
|
|
|
+ delivered: {
|
|
|
+ $sum: { $cond: [{ $gte: ["$_id.status", 2] }, "$count", 0] },
|
|
|
+ },
|
|
|
+ // 打开数:status === 3
|
|
|
+ opened: {
|
|
|
+ $sum: { $cond: [{ $eq: ["$_id.status", 3] }, "$count", 0] },
|
|
|
+ },
|
|
|
+ failed: {
|
|
|
+ $sum: { $cond: [{ $eq: ["$_id.status", -1] }, "$count", 0] },
|
|
|
+ },
|
|
|
+ // 展示数:status >= 2 并且 inforeground = false
|
|
|
+ displayCount: {
|
|
|
+ $sum: {
|
|
|
+ $cond: [
|
|
|
+ {
|
|
|
+ $and: [{ $gte: ["$_id.status", 2] }, { $eq: ["$_id.inforeground", false] }],
|
|
|
+ },
|
|
|
+ "$count",
|
|
|
+ 0,
|
|
|
+ ],
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
- // 3. 重新格式化输出,计算所有比率
|
|
|
+ // 3. 计算比率并格式化输出
|
|
|
{
|
|
|
$project: {
|
|
|
_id: 0,
|
|
|
- totalRecords: "$total_records",
|
|
|
- sent: {
|
|
|
- $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "1"] }] }, 0],
|
|
|
+ totalRecords: "$totalRecords",
|
|
|
+ sent: "$sent",
|
|
|
+ delivered: "$delivered",
|
|
|
+ opened: "$opened",
|
|
|
+ failed: "$failed",
|
|
|
+ displayCount: "$displayCount",
|
|
|
+ // 发送成功率 = sent / totalRecords
|
|
|
+ sentSuccessRate: {
|
|
|
+ $cond: [{ $eq: ["$totalRecords", 0] }, 0, { $divide: ["$sent", "$totalRecords"] }],
|
|
|
},
|
|
|
- delivered: {
|
|
|
- $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "2"] }] }, 0],
|
|
|
+ // 送达率 = delivered / sent
|
|
|
+ deliveredRate: {
|
|
|
+ $cond: [{ $eq: ["$sent", 0] }, 0, { $divide: ["$delivered", "$sent"] }],
|
|
|
},
|
|
|
- opened: {
|
|
|
- $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "3"] }] }, 0],
|
|
|
+ // 展示率 = displayCount / delivered
|
|
|
+ displayRate: {
|
|
|
+ $cond: [{ $eq: ["$delivered", 0] }, 0, { $divide: ["$displayCount", "$delivered"] }],
|
|
|
},
|
|
|
- failed: {
|
|
|
- $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "-1"] }] }, 0],
|
|
|
+ // 点击率 = opened / displayCount
|
|
|
+ clickThroughRate: {
|
|
|
+ $cond: [{ $eq: ["$displayCount", 0] }, 0, { $divide: ["$opened", "$displayCount"] }],
|
|
|
},
|
|
|
- // 新增:发送成功率(发送成功数 + 送达数 + 打开数)/ 总数
|
|
|
- sentSuccessRate: {
|
|
|
- $cond: [
|
|
|
- { $eq: ["$total_records", 0] },
|
|
|
- 0,
|
|
|
- {
|
|
|
- $divide: [
|
|
|
- {
|
|
|
- $sum: [
|
|
|
- { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "1"] }] }, 0] },
|
|
|
- { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "2"] }] }, 0] },
|
|
|
- { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "3"] }] }, 0] },
|
|
|
- ],
|
|
|
- },
|
|
|
- "$total_records",
|
|
|
- ],
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
- // 新增:送达率 = 送达数 / (发送成功数 + 送达数)
|
|
|
- deliveredRate: {
|
|
|
- $cond: [
|
|
|
- {
|
|
|
- $eq: [
|
|
|
- {
|
|
|
- $sum: [
|
|
|
- { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "1"] }] }, 0] },
|
|
|
- { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "2"] }] }, 0] },
|
|
|
- ],
|
|
|
- },
|
|
|
- 0,
|
|
|
- ],
|
|
|
- },
|
|
|
- 0,
|
|
|
- {
|
|
|
- $divide: [
|
|
|
- { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "2"] }] }, 0] },
|
|
|
- {
|
|
|
- $sum: [
|
|
|
- { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "1"] }] }, 0] },
|
|
|
- { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "2"] }] }, 0] },
|
|
|
- ],
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
- // 新增:打开率 = 打开数 / (送达数 + 打开数)
|
|
|
- openedRate: {
|
|
|
- $cond: [
|
|
|
- {
|
|
|
- $eq: [
|
|
|
- {
|
|
|
- $sum: [
|
|
|
- { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "2"] }] }, 0] },
|
|
|
- { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "3"] }] }, 0] },
|
|
|
- ],
|
|
|
- },
|
|
|
- 0,
|
|
|
- ],
|
|
|
- },
|
|
|
- 0,
|
|
|
- {
|
|
|
- $divide: [
|
|
|
- { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "3"] }] }, 0] },
|
|
|
- {
|
|
|
- $sum: [
|
|
|
- { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "2"] }] }, 0] },
|
|
|
- { $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "3"] }] }, 0] },
|
|
|
- ],
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
- ],
|
|
|
- },
|
|
|
- // Token 失效率
|
|
|
+ // Token 失效率 = failed / totalRecords
|
|
|
tokenInvalidationRate: {
|
|
|
- $cond: [{ $eq: ["$total_records", 0] }, 0, { $divide: [{ $ifNull: [{ $arrayElemAt: ["$status_counts.v", { $indexOfArray: ["$status_counts.k", "-1"] }] }, 0] }, "$total_records"] }],
|
|
|
+ $cond: [{ $eq: ["$totalRecords", 0] }, 0, { $divide: ["$failed", "$totalRecords"] }],
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
]);
|
|
|
-
|
|
|
return result[0];
|
|
|
} catch (error) {
|
|
|
console.error("Error fetching overall statistics:", error);
|
|
|
@@ -230,29 +184,51 @@ export class MessageRecordService {
|
|
|
public async getStatisticsByActivity() {
|
|
|
try {
|
|
|
const results = await MessageRecord.aggregate([
|
|
|
- // 1. 根据 activityId 和 status 进行分组
|
|
|
+ // 1. 根据 activityId, status, 和 inforeground 进行分组
|
|
|
{
|
|
|
$group: {
|
|
|
- _id: { activityId: "$activityId", activityName: "$activityName", status: "$status" },
|
|
|
+ _id: {
|
|
|
+ activityId: "$activityId",
|
|
|
+ activityName: "$activityName",
|
|
|
+ status: "$status",
|
|
|
+ inforeground: "$inforeground",
|
|
|
+ },
|
|
|
count: { $sum: 1 },
|
|
|
},
|
|
|
},
|
|
|
// 2. 将数据重组,以便按 activityId 汇总
|
|
|
{
|
|
|
$group: {
|
|
|
- _id: "$_id.activityId", // Group by the ID
|
|
|
- activityName: { $first: "$_id.activityName" }, // Keep the name
|
|
|
- total_sent: { $sum: "$count" },
|
|
|
- sent_count: { $sum: { $cond: [{ $eq: ["$_id.status", 1] }, "$count", 0] } },
|
|
|
- delivered_count: {
|
|
|
- $sum: { $cond: [{ $eq: ["$_id.status", 2] }, "$count", 0] },
|
|
|
- },
|
|
|
- opened_count: {
|
|
|
+ _id: "$_id.activityId",
|
|
|
+ activityName: { $first: "$_id.activityName" },
|
|
|
+ totalRecords: { $sum: "$count" },
|
|
|
+ // 发送成功数:status >= 1
|
|
|
+ sent: {
|
|
|
+ $sum: { $cond: [{ $gte: ["$_id.status", 1] }, "$count", 0] },
|
|
|
+ },
|
|
|
+ // 送达数:status >= 2
|
|
|
+ delivered: {
|
|
|
+ $sum: { $cond: [{ $gte: ["$_id.status", 2] }, "$count", 0] },
|
|
|
+ },
|
|
|
+ // 打开数:status === 3
|
|
|
+ opened: {
|
|
|
$sum: { $cond: [{ $eq: ["$_id.status", 3] }, "$count", 0] },
|
|
|
},
|
|
|
- failed_count: {
|
|
|
+ failed: {
|
|
|
$sum: { $cond: [{ $eq: ["$_id.status", -1] }, "$count", 0] },
|
|
|
},
|
|
|
+ // 展示数:status >= 2 并且 inforeground = false
|
|
|
+ displayCount: {
|
|
|
+ $sum: {
|
|
|
+ $cond: [
|
|
|
+ {
|
|
|
+ $and: [{ $gte: ["$_id.status", 2] }, { $eq: ["$_id.inforeground", false] }],
|
|
|
+ },
|
|
|
+ "$count",
|
|
|
+ 0,
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ },
|
|
|
},
|
|
|
},
|
|
|
// 3. 计算比率并格式化输出
|
|
|
@@ -261,22 +237,31 @@ export class MessageRecordService {
|
|
|
_id: 0,
|
|
|
activityId: "$_id",
|
|
|
activityName: "$activityName",
|
|
|
- totalSent: "$total_sent",
|
|
|
- sent: "$sent_count",
|
|
|
- delivered: "$delivered_count",
|
|
|
- opened: "$opened_count",
|
|
|
- failed: "$failed_count",
|
|
|
+ totalRecords: "$totalRecords",
|
|
|
+ sent: "$sent",
|
|
|
+ delivered: "$delivered",
|
|
|
+ opened: "$opened",
|
|
|
+ failed: "$failed",
|
|
|
+ displayCount: "$displayCount",
|
|
|
+ // 发送成功率 = sent / totalRecords
|
|
|
sentSuccessRate: {
|
|
|
- $cond: [{ $eq: ["$total_sent", 0] }, 0, { $divide: [{ $sum: ["$sent_count", "$delivered_count", "$opened_count"] }, "$total_sent"] }],
|
|
|
- },
|
|
|
- tokenInvalidationRate: {
|
|
|
- $cond: [{ $eq: ["$total_sent", 0] }, 0, { $divide: ["$failed_count", "$total_sent"] }],
|
|
|
+ $cond: [{ $eq: ["$totalRecords", 0] }, 0, { $divide: ["$sent", "$totalRecords"] }],
|
|
|
},
|
|
|
+ // 送达率 = delivered / sent
|
|
|
deliveredRate: {
|
|
|
- $cond: [{ $eq: [{ $sum: ["$sent_count", "$delivered_count"] }, 0] }, 0, { $divide: ["$delivered_count", { $sum: ["$sent_count", "$delivered_count"] }] }],
|
|
|
+ $cond: [{ $eq: ["$sent", 0] }, 0, { $divide: ["$delivered", "$sent"] }],
|
|
|
+ },
|
|
|
+ // 展示率 = displayCount / delivered
|
|
|
+ displayRate: {
|
|
|
+ $cond: [{ $eq: ["$delivered", 0] }, 0, { $divide: ["$displayCount", "$delivered"] }],
|
|
|
},
|
|
|
- openedRate: {
|
|
|
- $cond: [{ $eq: [{ $sum: ["$delivered_count", "$opened_count"] }, 0] }, 0, { $divide: ["$opened_count", { $sum: ["$delivered_count", "$opened_count"] }] }],
|
|
|
+ // 点击率 = opened / displayCount
|
|
|
+ clickThroughRate: {
|
|
|
+ $cond: [{ $eq: ["$displayCount", 0] }, 0, { $divide: ["$opened", "$displayCount"] }],
|
|
|
+ },
|
|
|
+ // Token 失效率 = failed / totalRecords
|
|
|
+ tokenInvalidationRate: {
|
|
|
+ $cond: [{ $eq: ["$totalRecords", 0] }, 0, { $divide: ["$failed", "$totalRecords"] }],
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
@@ -299,10 +284,15 @@ export class MessageRecordService {
|
|
|
public async getStatisticsByStrategy() {
|
|
|
try {
|
|
|
const results = await MessageRecord.aggregate([
|
|
|
- // 1. 根据 strategyId 和 status 进行分组
|
|
|
+ // 1. 根据 strategyId, status, 和 inforeground 进行分组
|
|
|
{
|
|
|
$group: {
|
|
|
- _id: { strategyId: "$strategyId", strategyName: "$strategyName", status: "$status" },
|
|
|
+ _id: {
|
|
|
+ strategyId: "$strategyId",
|
|
|
+ strategyName: "$strategyName",
|
|
|
+ status: "$status",
|
|
|
+ inforeground: "$inforeground",
|
|
|
+ },
|
|
|
count: { $sum: 1 },
|
|
|
},
|
|
|
},
|
|
|
@@ -311,17 +301,34 @@ export class MessageRecordService {
|
|
|
$group: {
|
|
|
_id: "$_id.strategyId",
|
|
|
strategyName: { $first: "$_id.strategyName" },
|
|
|
- total_sent: { $sum: "$count" },
|
|
|
- sent_count: { $sum: { $cond: [{ $eq: ["$_id.status", 1] }, "$count", 0] } },
|
|
|
- delivered_count: {
|
|
|
- $sum: { $cond: [{ $eq: ["$_id.status", 2] }, "$count", 0] },
|
|
|
+ totalRecords: { $sum: "$count" },
|
|
|
+ // 发送成功数:status >= 1
|
|
|
+ sent: {
|
|
|
+ $sum: { $cond: [{ $gte: ["$_id.status", 1] }, "$count", 0] },
|
|
|
+ },
|
|
|
+ // 送达数:status >= 2
|
|
|
+ delivered: {
|
|
|
+ $sum: { $cond: [{ $gte: ["$_id.status", 2] }, "$count", 0] },
|
|
|
},
|
|
|
- opened_count: {
|
|
|
+ // 打开数:status === 3
|
|
|
+ opened: {
|
|
|
$sum: { $cond: [{ $eq: ["$_id.status", 3] }, "$count", 0] },
|
|
|
},
|
|
|
- failed_count: {
|
|
|
+ failed: {
|
|
|
$sum: { $cond: [{ $eq: ["$_id.status", -1] }, "$count", 0] },
|
|
|
},
|
|
|
+ // 展示数:status >= 2 并且 inforeground = false
|
|
|
+ displayCount: {
|
|
|
+ $sum: {
|
|
|
+ $cond: [
|
|
|
+ {
|
|
|
+ $and: [{ $gte: ["$_id.status", 2] }, { $eq: ["$_id.inforeground", false] }],
|
|
|
+ },
|
|
|
+ "$count",
|
|
|
+ 0,
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ },
|
|
|
},
|
|
|
},
|
|
|
// 3. 计算比率并格式化输出
|
|
|
@@ -330,22 +337,31 @@ export class MessageRecordService {
|
|
|
_id: 0,
|
|
|
strategyId: "$_id",
|
|
|
strategyName: "$strategyName",
|
|
|
- totalSent: "$total_sent",
|
|
|
- sent: "$sent_count",
|
|
|
- delivered: "$delivered_count",
|
|
|
- opened: "$opened_count",
|
|
|
- failed: "$failed_count",
|
|
|
+ totalRecords: "$totalRecords",
|
|
|
+ sent: "$sent",
|
|
|
+ delivered: "$delivered",
|
|
|
+ opened: "$opened",
|
|
|
+ failed: "$failed",
|
|
|
+ displayCount: "$displayCount",
|
|
|
+ // 发送成功率 = sent / totalRecords
|
|
|
sentSuccessRate: {
|
|
|
- $cond: [{ $eq: ["$total_sent", 0] }, 0, { $divide: [{ $sum: ["$sent_count", "$delivered_count", "$opened_count"] }, "$total_sent"] }],
|
|
|
- },
|
|
|
- tokenInvalidationRate: {
|
|
|
- $cond: [{ $eq: ["$total_sent", 0] }, 0, { $divide: ["$failed_count", "$total_sent"] }],
|
|
|
+ $cond: [{ $eq: ["$totalRecords", 0] }, 0, { $divide: ["$sent", "$totalRecords"] }],
|
|
|
},
|
|
|
+ // 送达率 = delivered / sent
|
|
|
deliveredRate: {
|
|
|
- $cond: [{ $eq: [{ $sum: ["$sent_count", "$delivered_count"] }, 0] }, 0, { $divide: ["$delivered_count", { $sum: ["$sent_count", "$delivered_count"] }] }],
|
|
|
+ $cond: [{ $eq: ["$sent", 0] }, 0, { $divide: ["$delivered", "$sent"] }],
|
|
|
+ },
|
|
|
+ // 展示率 = displayCount / delivered
|
|
|
+ displayRate: {
|
|
|
+ $cond: [{ $eq: ["$delivered", 0] }, 0, { $divide: ["$displayCount", "$delivered"] }],
|
|
|
},
|
|
|
- openedRate: {
|
|
|
- $cond: [{ $eq: [{ $sum: ["$delivered_count", "$opened_count"] }, 0] }, 0, { $divide: ["$opened_count", { $sum: ["$delivered_count", "$opened_count"] }] }],
|
|
|
+ // 点击率 = opened / displayCount
|
|
|
+ clickThroughRate: {
|
|
|
+ $cond: [{ $eq: ["$displayCount", 0] }, 0, { $divide: ["$opened", "$displayCount"] }],
|
|
|
+ },
|
|
|
+ // Token 失效率 = failed / totalRecords
|
|
|
+ tokenInvalidationRate: {
|
|
|
+ $cond: [{ $eq: ["$totalRecords", 0] }, 0, { $divide: ["$failed", "$totalRecords"] }],
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
@@ -354,7 +370,6 @@ export class MessageRecordService {
|
|
|
$sort: { deliveredRate: -1 },
|
|
|
},
|
|
|
]);
|
|
|
-
|
|
|
return results;
|
|
|
} catch (error) {
|
|
|
console.error("Error fetching statistics by strategy:", error);
|
|
|
@@ -368,10 +383,15 @@ export class MessageRecordService {
|
|
|
public async getStatisticsByTemplate() {
|
|
|
try {
|
|
|
const results = await MessageRecord.aggregate([
|
|
|
- // 1. 根据 templateId 和 status 进行分组
|
|
|
+ // 1. 根据 templateId, status, 和 inforeground 进行分组
|
|
|
{
|
|
|
$group: {
|
|
|
- _id: { templateId: "$templateId", templateName: "$templateName", status: "$status" },
|
|
|
+ _id: {
|
|
|
+ templateId: "$templateId",
|
|
|
+ templateName: "$templateName",
|
|
|
+ status: "$status",
|
|
|
+ inforeground: "$inforeground",
|
|
|
+ },
|
|
|
count: { $sum: 1 },
|
|
|
},
|
|
|
},
|
|
|
@@ -380,17 +400,34 @@ export class MessageRecordService {
|
|
|
$group: {
|
|
|
_id: "$_id.templateId",
|
|
|
templateName: { $first: "$_id.templateName" },
|
|
|
- total_sent: { $sum: "$count" },
|
|
|
- sent_count: { $sum: { $cond: [{ $eq: ["$_id.status", 1] }, "$count", 0] } },
|
|
|
- delivered_count: {
|
|
|
- $sum: { $cond: [{ $eq: ["$_id.status", 2] }, "$count", 0] },
|
|
|
+ totalRecords: { $sum: "$count" },
|
|
|
+ // 发送成功数:status >= 1
|
|
|
+ sent: {
|
|
|
+ $sum: { $cond: [{ $gte: ["$_id.status", 1] }, "$count", 0] },
|
|
|
+ },
|
|
|
+ // 送达数:status >= 2
|
|
|
+ delivered: {
|
|
|
+ $sum: { $cond: [{ $gte: ["$_id.status", 2] }, "$count", 0] },
|
|
|
},
|
|
|
- opened_count: {
|
|
|
+ // 打开数:status === 3
|
|
|
+ opened: {
|
|
|
$sum: { $cond: [{ $eq: ["$_id.status", 3] }, "$count", 0] },
|
|
|
},
|
|
|
- failed_count: {
|
|
|
+ failed: {
|
|
|
$sum: { $cond: [{ $eq: ["$_id.status", -1] }, "$count", 0] },
|
|
|
},
|
|
|
+ // 展示数:status >= 2 并且 inforeground = false
|
|
|
+ displayCount: {
|
|
|
+ $sum: {
|
|
|
+ $cond: [
|
|
|
+ {
|
|
|
+ $and: [{ $gte: ["$_id.status", 2] }, { $eq: ["$_id.inforeground", false] }],
|
|
|
+ },
|
|
|
+ "$count",
|
|
|
+ 0,
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ },
|
|
|
},
|
|
|
},
|
|
|
// 3. 计算比率并格式化输出
|
|
|
@@ -399,22 +436,31 @@ export class MessageRecordService {
|
|
|
_id: 0,
|
|
|
templateId: "$_id",
|
|
|
templateName: "$templateName",
|
|
|
- totalSent: "$total_sent",
|
|
|
- sent: "$sent_count",
|
|
|
- delivered: "$delivered_count",
|
|
|
- opened: "$opened_count",
|
|
|
- failed: "$failed_count",
|
|
|
+ totalRecords: "$totalRecords",
|
|
|
+ sent: "$sent",
|
|
|
+ delivered: "$delivered",
|
|
|
+ opened: "$opened",
|
|
|
+ failed: "$failed",
|
|
|
+ displayCount: "$displayCount",
|
|
|
+ // 发送成功率 = sent / totalRecords
|
|
|
sentSuccessRate: {
|
|
|
- $cond: [{ $eq: ["$total_sent", 0] }, 0, { $divide: [{ $sum: ["$sent_count", "$delivered_count", "$opened_count"] }, "$total_sent"] }],
|
|
|
- },
|
|
|
- tokenInvalidationRate: {
|
|
|
- $cond: [{ $eq: ["$total_sent", 0] }, 0, { $divide: ["$failed_count", "$total_sent"] }],
|
|
|
+ $cond: [{ $eq: ["$totalRecords", 0] }, 0, { $divide: ["$sent", "$totalRecords"] }],
|
|
|
},
|
|
|
+ // 送达率 = delivered / sent
|
|
|
deliveredRate: {
|
|
|
- $cond: [{ $eq: [{ $sum: ["$sent_count", "$delivered_count"] }, 0] }, 0, { $divide: ["$delivered_count", { $sum: ["$sent_count", "$delivered_count"] }] }],
|
|
|
+ $cond: [{ $eq: ["$sent", 0] }, 0, { $divide: ["$delivered", "$sent"] }],
|
|
|
+ },
|
|
|
+ // 展示率 = displayCount / delivered
|
|
|
+ displayRate: {
|
|
|
+ $cond: [{ $eq: ["$delivered", 0] }, 0, { $divide: ["$displayCount", "$delivered"] }],
|
|
|
},
|
|
|
- openedRate: {
|
|
|
- $cond: [{ $eq: [{ $sum: ["$delivered_count", "$opened_count"] }, 0] }, 0, { $divide: ["$opened_count", { $sum: ["$delivered_count", "$opened_count"] }] }],
|
|
|
+ // 点击率 = opened / displayCount
|
|
|
+ clickThroughRate: {
|
|
|
+ $cond: [{ $eq: ["$displayCount", 0] }, 0, { $divide: ["$opened", "$displayCount"] }],
|
|
|
+ },
|
|
|
+ // Token 失效率 = failed / totalRecords
|
|
|
+ tokenInvalidationRate: {
|
|
|
+ $cond: [{ $eq: ["$totalRecords", 0] }, 0, { $divide: ["$failed", "$totalRecords"] }],
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
@@ -423,7 +469,6 @@ export class MessageRecordService {
|
|
|
$sort: { deliveredRate: -1 },
|
|
|
},
|
|
|
]);
|
|
|
-
|
|
|
return results;
|
|
|
} catch (error) {
|
|
|
console.error("Error fetching statistics by template:", error);
|
|
|
@@ -443,12 +488,13 @@ export class MessageRecordService {
|
|
|
$dateTrunc: { date: "$actualSendAt", unit: "day", timezone: "America/Los_Angeles" },
|
|
|
},
|
|
|
status: "$status",
|
|
|
+ inforeground: "$inforeground",
|
|
|
},
|
|
|
},
|
|
|
- // 2. 根据日期和状态进行分组
|
|
|
+ // 2. 根据日期、状态和 inforeground 进行分组
|
|
|
{
|
|
|
$group: {
|
|
|
- _id: { date: "$date", status: "$status" },
|
|
|
+ _id: { date: "$date", status: "$status", inforeground: "$inforeground" },
|
|
|
count: { $sum: 1 },
|
|
|
},
|
|
|
},
|
|
|
@@ -456,11 +502,34 @@ export class MessageRecordService {
|
|
|
{
|
|
|
$group: {
|
|
|
_id: "$_id.date",
|
|
|
- totalSent: { $sum: "$count" },
|
|
|
- sent: { $sum: { $cond: [{ $eq: ["$_id.status", 1] }, "$count", 0] } },
|
|
|
- delivered: { $sum: { $cond: [{ $eq: ["$_id.status", 2] }, "$count", 0] } },
|
|
|
- opened: { $sum: { $cond: [{ $eq: ["$_id.status", 3] }, "$count", 0] } },
|
|
|
- failed: { $sum: { $cond: [{ $eq: ["$_id.status", -1] }, "$count", 0] } },
|
|
|
+ totalRecords: { $sum: "$count" },
|
|
|
+ // 发送成功数:status >= 1
|
|
|
+ sent: {
|
|
|
+ $sum: { $cond: [{ $gte: ["$_id.status", 1] }, "$count", 0] },
|
|
|
+ },
|
|
|
+ // 送达数:status >= 2
|
|
|
+ delivered: {
|
|
|
+ $sum: { $cond: [{ $gte: ["$_id.status", 2] }, "$count", 0] },
|
|
|
+ },
|
|
|
+ // 打开数:status === 3
|
|
|
+ opened: {
|
|
|
+ $sum: { $cond: [{ $eq: ["$_id.status", 3] }, "$count", 0] },
|
|
|
+ },
|
|
|
+ failed: {
|
|
|
+ $sum: { $cond: [{ $eq: ["$_id.status", -1] }, "$count", 0] },
|
|
|
+ },
|
|
|
+ // 展示数:status >= 2 并且 inforeground = false
|
|
|
+ displayCount: {
|
|
|
+ $sum: {
|
|
|
+ $cond: [
|
|
|
+ {
|
|
|
+ $and: [{ $gte: ["$_id.status", 2] }, { $eq: ["$_id.inforeground", false] }],
|
|
|
+ },
|
|
|
+ "$count",
|
|
|
+ 0,
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ },
|
|
|
},
|
|
|
},
|
|
|
// 4. 计算比率并格式化输出
|
|
|
@@ -468,26 +537,31 @@ export class MessageRecordService {
|
|
|
$project: {
|
|
|
_id: 0,
|
|
|
date: "$_id",
|
|
|
- totalSent: "$totalSent",
|
|
|
+ totalRecords: "$totalRecords",
|
|
|
sent: "$sent",
|
|
|
delivered: "$delivered",
|
|
|
opened: "$opened",
|
|
|
failed: "$failed",
|
|
|
- // Sent success rate = (sent + delivered + opened) / total
|
|
|
+ displayCount: "$displayCount",
|
|
|
+ // 发送成功率 = sent / totalRecords
|
|
|
sentSuccessRate: {
|
|
|
- $cond: [{ $eq: ["$totalSent", 0] }, 0, { $divide: [{ $sum: ["$sent", "$delivered", "$opened"] }, "$totalSent"] }],
|
|
|
+ $cond: [{ $eq: ["$totalRecords", 0] }, 0, { $divide: ["$sent", "$totalRecords"] }],
|
|
|
},
|
|
|
- // Delivered rate = delivered / (sent + delivered)
|
|
|
+ // 送达率 = delivered / sent
|
|
|
deliveredRate: {
|
|
|
- $cond: [{ $eq: [{ $sum: ["$sent", "$delivered"] }, 0] }, 0, { $divide: ["$delivered", { $sum: ["$sent", "$delivered"] }] }],
|
|
|
+ $cond: [{ $eq: ["$sent", 0] }, 0, { $divide: ["$delivered", "$sent"] }],
|
|
|
},
|
|
|
- // Opened rate = opened / (delivered + opened)
|
|
|
- openedRate: {
|
|
|
- $cond: [{ $eq: [{ $sum: ["$delivered", "$opened"] }, 0] }, 0, { $divide: ["$opened", { $sum: ["$delivered", "$opened"] }] }],
|
|
|
+ // 展示率 = displayCount / delivered
|
|
|
+ displayRate: {
|
|
|
+ $cond: [{ $eq: ["$delivered", 0] }, 0, { $divide: ["$displayCount", "$delivered"] }],
|
|
|
+ },
|
|
|
+ // 点击率 = opened / displayCount
|
|
|
+ clickThroughRate: {
|
|
|
+ $cond: [{ $eq: ["$displayCount", 0] }, 0, { $divide: ["$opened", "$displayCount"] }],
|
|
|
},
|
|
|
// Token invalidation rate
|
|
|
tokenInvalidationRate: {
|
|
|
- $cond: [{ $eq: ["$totalSent", 0] }, 0, { $divide: ["$failed", "$totalSent"] }],
|
|
|
+ $cond: [{ $eq: ["$totalRecords", 0] }, 0, { $divide: ["$failed", "$totalRecords"] }],
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
@@ -496,7 +570,6 @@ export class MessageRecordService {
|
|
|
$sort: { date: 1 },
|
|
|
},
|
|
|
]);
|
|
|
-
|
|
|
return results;
|
|
|
} catch (error) {
|
|
|
console.error("Error fetching daily sent trends:", error);
|
|
|
@@ -504,14 +577,14 @@ export class MessageRecordService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 消息生命周期分析, 利用 plannedSendAt、actualSendAt、deliveredAt 和 openedAt 字段,可以分析消息从计划到被用户打开的整个生命周期。
|
|
|
+ // 消息生命周期分析
|
|
|
public async getAverageDeliveryTime() {
|
|
|
try {
|
|
|
const result = await MessageRecord.aggregate([
|
|
|
- // 1. 只筛选出已送达的消息(status = 2 或 3)
|
|
|
+ // 1. 只筛选出已送达的消息(status >= 2)
|
|
|
{
|
|
|
$match: {
|
|
|
- status: { $in: [2, 3] },
|
|
|
+ status: { $gte: 2 },
|
|
|
actualSendAt: { $exists: true, $ne: null },
|
|
|
deliveredAt: { $exists: true, $ne: null },
|
|
|
},
|