messageRecordController.ts 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. // oms/src/controllers/messageRecordController.ts
  2. import { Request, Response } from "express";
  3. import { isObjectIdOrHexString } from "mongoose";
  4. import { MessageRecord, IMessageRecord } from "../models/messageRecordModel";
  5. import { MessageRecordService } from "../services/messageRecordService";
  6. class MessageRecordController {
  7. private messageRecordService: MessageRecordService;
  8. constructor() {
  9. this.messageRecordService = new MessageRecordService();
  10. }
  11. /**
  12. * @route POST /api/message-record
  13. * @desc Creates a new message record
  14. * @access Private
  15. */
  16. public createRecord = async (req: Request, res: Response): Promise<Response> => {
  17. try {
  18. const newRecord = new MessageRecord(req.body);
  19. await newRecord.save();
  20. return res.status(201).json({ success: true, data: newRecord });
  21. } catch (error: any) {
  22. console.error("Error creating message record:", error);
  23. return res.status(500).json({ success: false, message: "Server error", error: error.message });
  24. }
  25. };
  26. /**
  27. * @route GET /api/message-records
  28. * @desc Retrieves all message records with pagination and optional filters
  29. * @access Private
  30. */
  31. public getPaginatedRecords = async (req: Request, res: Response): Promise<Response> => {
  32. const { page = 1, limit = 30, uid, activityName, templateName, strategyName, status, startDate, endDate } = req.query;
  33. const pageNum = parseInt(page as string, 10);
  34. const limitNum = parseInt(limit as string, 10);
  35. // 动态构建查询过滤器
  36. const filters: any = {};
  37. if (uid) {
  38. filters.uid = uid;
  39. }
  40. if (status) {
  41. const statusNum = parseInt(status as string, 10);
  42. if (!isNaN(statusNum)) {
  43. filters.status = statusNum;
  44. }
  45. }
  46. if (activityName) {
  47. filters.activityName = activityName;
  48. }
  49. if (strategyName) {
  50. filters.strategyName = strategyName;
  51. }
  52. if (templateName) {
  53. filters.templateName = templateName;
  54. }
  55. // 定义所有可查询的日期字段
  56. const dateQueryKeys: (keyof IMessageRecord)[] = ["plannedSendAt", "actualSendAt", "deliveredAt", "openedAt", "createdAt", "updatedAt"];
  57. // 遍历所有日期字段,处理单个日期或日期范围
  58. dateQueryKeys.forEach((key) => {
  59. const queryValue = req.query[key as string] as string;
  60. if (queryValue) {
  61. const dates = queryValue.split(",");
  62. if (dates.length === 2) {
  63. const startDate = new Date(dates[0]);
  64. const endDate = new Date(dates[1]);
  65. // 确保日期有效
  66. if (!isNaN(startDate.getTime()) && !isNaN(endDate.getTime())) {
  67. filters[key] = {
  68. $gte: startDate,
  69. $lte: endDate,
  70. };
  71. } else {
  72. console.warn(`[API] Invalid date range format for ${key}: ${queryValue}. Skipping.`);
  73. }
  74. } else {
  75. // 如果不是范围,则按单个日期精确匹配
  76. const singleDate = new Date(queryValue);
  77. if (!isNaN(singleDate.getTime())) {
  78. filters[key] = singleDate;
  79. }
  80. }
  81. }
  82. });
  83. // 保留对原有startDate/endDate参数的兼容性,并将其应用于createdAt
  84. if (startDate || endDate) {
  85. // 确保 createdAt 过滤器不存在冲突,如果已通过范围查询设置,则跳过
  86. if (!filters.createdAt) {
  87. filters.createdAt = {};
  88. }
  89. if (startDate) {
  90. filters.createdAt.$gte = new Date(startDate as string);
  91. }
  92. if (endDate) {
  93. filters.createdAt.$lte = new Date(endDate as string);
  94. }
  95. }
  96. try {
  97. const records = await MessageRecord.find(filters)
  98. .sort({ createdAt: -1 })
  99. .skip((pageNum - 1) * limitNum)
  100. .limit(limitNum);
  101. const total = await MessageRecord.countDocuments(filters);
  102. return res.status(200).json({
  103. success: true,
  104. data: records,
  105. pagination: {
  106. total,
  107. page: pageNum,
  108. limit: limitNum,
  109. totalPages: Math.ceil(total / limitNum),
  110. },
  111. });
  112. } catch (error: any) {
  113. console.error("Error fetching paginated records:", error);
  114. return res.status(500).json({ success: false, message: "Server error", error: error.message });
  115. }
  116. };
  117. /**
  118. * @route GET /api/message-records/user/:uid
  119. * @desc Retrieves message records by user UID
  120. * @access Private
  121. */
  122. public getRecordsByUid = async (req: Request, res: Response): Promise<Response> => {
  123. try {
  124. const records = await MessageRecord.find({ uid: req.params.uid }).sort({ createdAt: -1 });
  125. if (!records || records.length === 0) {
  126. return res.status(404).json({ success: false, message: "No records found for this user" });
  127. }
  128. return res.status(200).json({ success: true, data: records });
  129. } catch (error: any) {
  130. console.error("Error fetching records by user UID:", error);
  131. return res.status(500).json({ success: false, message: "Server error", error: error.message });
  132. }
  133. };
  134. /**
  135. * @route GET /api/message-record/:id
  136. * @desc Retrieves a single message record by ID
  137. * @access Private
  138. */
  139. public getRecordById = async (req: Request, res: Response): Promise<Response> => {
  140. try {
  141. // 检查 id 是否是有效的 ObjectId 格式
  142. if (!isObjectIdOrHexString(req.params.id)) {
  143. return res.status(400).json({ success: false, message: "Invalid record ID" });
  144. }
  145. const record = await MessageRecord.findById(req.params.id);
  146. if (!record) {
  147. return res.status(404).json({ success: false, message: "Message record not found" });
  148. }
  149. return res.status(200).json({ success: true, data: record });
  150. } catch (error: any) {
  151. console.error("Error fetching message record by ID:", error);
  152. return res.status(500).json({ success: false, message: "Server error", error: error.message });
  153. }
  154. };
  155. /**
  156. * @route PUT /api/message-record/:id
  157. * @desc Updates the status of a message record
  158. * @access Private
  159. */
  160. public updateRecord = async (req: Request, res: Response): Promise<Response> => {
  161. try {
  162. // 检查 id 是否是有效的 ObjectId 格式
  163. if (!isObjectIdOrHexString(req.params.id)) {
  164. return res.status(400).json({ success: false, message: "Invalid record ID" });
  165. }
  166. const updatedRecord = await MessageRecord.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true });
  167. if (!updatedRecord) {
  168. return res.status(404).json({ success: false, message: "Message record not found" });
  169. }
  170. return res.status(200).json({ success: true, data: updatedRecord });
  171. } catch (error: any) {
  172. console.error("Error updating message record:", error);
  173. return res.status(500).json({ success: false, message: "Server error", error: error.message });
  174. }
  175. };
  176. /**
  177. * @route GET /api/message/statistics/overall
  178. * @desc Retrieves overall message push statistics
  179. * @access Private
  180. */
  181. public getOverallStatistics = async (req: Request, res: Response): Promise<Response> => {
  182. try {
  183. const stats = await this.messageRecordService.getOverallStatistics();
  184. return res.status(200).json({ success: true, data: stats });
  185. } catch (error: any) {
  186. console.error("Error fetching overall statistics:", error);
  187. return res.status(500).json({ success: false, message: "Server error", error: error.message });
  188. }
  189. };
  190. /**
  191. * @route GET /api/message/statistics/by-activity
  192. * @desc Retrieves message push statistics grouped by activity
  193. * @access Private
  194. */
  195. public getStatisticsByActivity = async (req: Request, res: Response): Promise<Response> => {
  196. try {
  197. const stats = await this.messageRecordService.getStatisticsByActivity();
  198. return res.status(200).json({ success: true, data: stats });
  199. } catch (error: any) {
  200. console.error("Error fetching statistics by activity:", error);
  201. return res.status(500).json({ success: false, message: "Server error", error: error.message });
  202. }
  203. };
  204. /**
  205. * @route GET /api/message/statistics/by-strategy
  206. * @desc Retrieves message push statistics grouped by strategy
  207. * @access Private
  208. */
  209. public getStatisticsByStrategy = async (req: Request, res: Response): Promise<Response> => {
  210. try {
  211. const stats = await this.messageRecordService.getStatisticsByStrategy();
  212. return res.status(200).json({ success: true, data: stats });
  213. } catch (error: any) {
  214. console.error("Error fetching statistics by strategy:", error);
  215. return res.status(500).json({ success: false, message: "Server error", error: error.message });
  216. }
  217. };
  218. /**
  219. * @route GET /api/message/statistics/by-template
  220. * @desc Retrieves message push statistics grouped by template
  221. * @access Private
  222. */
  223. public getStatisticsByTemplate = async (req: Request, res: Response): Promise<Response> => {
  224. try {
  225. const stats = await this.messageRecordService.getStatisticsByTemplate();
  226. return res.status(200).json({ success: true, data: stats });
  227. } catch (error: any) {
  228. console.error("Error fetching statistics by strategy:", error);
  229. return res.status(500).json({ success: false, message: "Server error", error: error.message });
  230. }
  231. };
  232. /**
  233. * @route GET /api/message/statistics/daily-trends
  234. * @desc Retrieves daily sent trend statistics
  235. * @access Private
  236. */
  237. public getDailySentTrends = async (req: Request, res: Response): Promise<Response> => {
  238. try {
  239. const stats = await this.messageRecordService.getDailySentTrends();
  240. return res.status(200).json({ success: true, data: stats });
  241. } catch (error: any) {
  242. console.error("Error fetching daily sent trends:", error);
  243. return res.status(500).json({ success: false, message: "Server error", error: error.message });
  244. }
  245. };
  246. }
  247. export default new MessageRecordController();