Commit 5454e9a9 by chenjinjing

二期内容提交测试准备

parent 33f487e6
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
"moment": "^2.24.0", "moment": "^2.24.0",
"mongoose": "^5.4.0", "mongoose": "^5.4.0",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"node-schedule": "^2.1.1",
"node-xlsx": "^0.16.1", "node-xlsx": "^0.16.1",
"nodemailer": "^6.1.1", "nodemailer": "^6.1.1",
"officegen": "^0.6.5", "officegen": "^0.6.5",
...@@ -28,7 +29,6 @@ ...@@ -28,7 +29,6 @@
"ws": "^5.2.2", "ws": "^5.2.2",
"xml2js": "^0.4.23" "xml2js": "^0.4.23"
}, },
"devDependencies": {},
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
......
<config> <config>
<port>9099</port> <port>9099</port>
<sign>xxx90909082fsdahfjosadjfpoiwausjorip2hjklrhn1ioud0u124rx0qwejfokasjfolksaujfoas</sign> <sign>xxx90909082fsdahfjosadjfpoiwausjorip2hjklrhn1ioud0u124rx0qwejfokasjfolksaujfoas</sign>
<dbServer>http://192.168.0.71:40012</dbServer> <dbServer>http://192.168.0.105:40012</dbServer>
</config> </config>
\ No newline at end of file
/**
* 企业消息通知
*/
import moment = require("moment");
import { OPERATIONALDATATYPE, TABLEID, TABLENAME } from "../config/enum/dbEnum";
import { operationalData, selectData } from "../data/operationalData";
import { getMySqlMs, randomId } from "../tools/system";
import { BizError } from "../util/bizError";
import { ERRORENUM } from "../config/enum/errorEnum";
import { MSGTYPE } from "../config/enum/enum";
/**
* 获取企业通知列表
*/
export async function getNotificationList(eId: string) {
// 查询消息表,获取所有给该企业或所有企业的消息
const messageFields = ["msgId", "msgType", "msgTitle", "createTime", "effectiveTime", "msgContent", "eId", "isPop"];
// 查询条件:eId包含该企业或eId为空数组(发给所有企业)
const selectParam = {
"%or%": [
{ eId: { "%like%": eId } }, // 包含该企业ID
{ eId: '[]' } // 发给所有企业
]
};
let messageList = await selectData(
OPERATIONALDATATYPE.查询多个,
TABLENAME.企业消息通知表,
selectParam,
messageFields
);
// 过滤过期消息
const currentTime = getMySqlMs();
messageList = messageList.filter(message =>
!message.effectiveTime || message.effectiveTime > currentTime
);
// 按创建时间排序
messageList.sort((a, b) =>
new Date(b.createTime).valueOf() - new Date(a.createTime).valueOf()
);
// 获取已读状态
const readStatusFields = ["readId", "msgId", "eId", "readTime", "isRead"];
const readStatusList = await selectData(
OPERATIONALDATATYPE.查询多个,
TABLENAME.企业消息已读状态表,
{ eId: eId },
readStatusFields
);
// 创建已读状态映射
const readStatusMap = new Map();
readStatusList.forEach(status => {
readStatusMap.set(status.msgId, status);
});
// 存储需要创建的未读记录
const unreadRecordsToCreate = [];
// 构建返回数据
const dataList = messageList.map(message => {
// 解析企业ID数组
let eIdArray: string[] = [];
if (message.eId) {
try {
eIdArray = JSON.parse(message.eId);
} catch (error) {
console.error('解析 eId 失败:', error);
}
}
// 获取已读状态
const readStatus = readStatusMap.get(message.msgId);
let isRead = false;
let readTime = null;
let readId = null;
if (readStatus) {
// 如果已有记录,使用现有状态
isRead = readStatus.isRead === 1;
readTime = readStatus.readTime;
readId = readStatus.readId;
} else {
// 如果没有记录,标记为未读,并准备创建记录
isRead = false;
readTime = null;
readId = null; // 尚未创建,没有readId
// 收集需要创建的未读记录
unreadRecordsToCreate.push({
msgId: message.msgId,
readId: randomId(TABLEID.企业消息已读状态表),
eId: eId,
readTime: null,
isRead: 0, // 0表示未读
createTime: getMySqlMs() // 添加创建时间
});
}
return {
msgId: message.msgId,
readId: readId,
msgType: message.msgType,
msgTitle: message.msgTitle,
msgContent: message.msgContent,
eId: eIdArray,
isPop: message.isPop === 1,
isRead: isRead,
effectiveTime: message.effectiveTime ? moment(message.effectiveTime).format("YYYY-MM-DD HH:mm:ss") : null,
readTime: readTime ? moment(readTime).format("YYYY-MM-DD HH:mm:ss") : null,
createTime: moment(message.createTime).format("YYYY-MM-DD HH:mm:ss")
};
});
// 批量创建未读记录(如果有需要创建的)
if (unreadRecordsToCreate.length > 0) {
try {
// 这里需要实现批量插入数据的函数
await createUnreadRecords(unreadRecordsToCreate);
} catch (error) {
console.error('创建未读记录失败:', error);
// 这里可以根据需要决定是否抛出错误
}
}
return { dataList };
}
/**
* 批量创建未读记录
*/
async function createUnreadRecords(records: any[]) {
// 批量插入未读记录
for (const record of records) {
await operationalData(
OPERATIONALDATATYPE.增加,
TABLENAME.企业消息已读状态表,
record,
{}
);
}
}
/**
* 标记通知为已读
*/
export async function markNotificationAsRead(eId: string, msgId: string, readId:string) {
// 先检查消息是否存在且对该企业可见
const message = await selectData(
OPERATIONALDATATYPE.查询单个,
TABLENAME.企业消息通知表,
{
msgId: msgId,
readId: readId,
"%or%": [
{ eId: { "%like%": `%"${eId}"%` } },
{ eId: '[]' }
]
},
["msgId"]
);
if (!message) {
throw new BizError(ERRORENUM.数据不存在);
}
// 检查是否已存在已读记录
const existingRead = await selectData(
OPERATIONALDATATYPE.查询单个,
TABLENAME.企业消息已读状态表,
{
msgId: msgId,
readId: readId,
eId: eId
},
["readId"]
);
if (Object.keys(existingRead).length) {
// 更新已存在记录
const updateData = {
isRead: 1,
// isRead: 0,
readTime: getMySqlMs()
};
await operationalData(
OPERATIONALDATATYPE.修改,
TABLENAME.企业消息已读状态表,
updateData,
{ readId: existingRead.readId }
);
} else {
// 创建新的已读记录
const readData = {
readId: randomId(TABLEID.企业消息已读状态表),
msgId: msgId,
eId: eId,
isRead: 1,
// isRead: 0,
readTime: getMySqlMs()
};
await operationalData(
OPERATIONALDATATYPE.增加,
TABLENAME.企业消息已读状态表,
readData,
{}
);
}
return { isSuccess: true };
}
/**
* 批量标记通知为已读
*/
export async function markNotificationsAsRead(eId: string, msgIds: string[], readId:string) {
for (const msgId of msgIds) {
await markNotificationAsRead(eId, msgId, readId);
}
return { isSuccess: true };
}
/**
* 获取弹窗消息
*/
export async function getPopupNotifications(eId: string) {
// 先获取所有消息
const result = await getNotificationList(eId);
// 过滤出弹窗消息
const popupMessages = result.dataList.filter(message => message.isPop);
// 特殊处理季度填报提醒消息
const processedMessages = await Promise.all(
popupMessages.map(async (message) => {
// 如果是季度填报提醒类型
if (message.msgType === MSGTYPE.季度填报提醒) {
// 获取上季度的时间
const lastQuarter = getLastQuarter();
// 替换模板变量
const year = lastQuarter.year;
const quarter = lastQuarter.quarter;
// const quarterNames = ["第一季度", "第二季度", "第三季度", "第四季度"];
const quarterNames = ["一", "二", "三", "四"];
// 替换标题和内容中的模板变量
let processedTitle = message.msgTitle;
let processedContent = message.msgContent;
if (processedTitle.includes("${year}") || processedTitle.includes("${quarter}")) {
processedTitle = processedTitle
.replace(/\${year}/g, year.toString())
.replace(/\${quarter}/g, quarter.toString());
}
if (processedContent.includes("${year}") || processedContent.includes("${quarter}")) {
processedContent = processedContent
.replace(/\${year}/g, year.toString())
.replace(/\${quarter}/g, quarterNames[quarter-1]);
}
// 检查是否是每季度1号
const today = moment();
const isFirstDayOfMonth = today.date() === 1;
if (isFirstDayOfMonth) {
// 重置该消息对该企业的已读状态为未读
await resetNotificationReadStatus(eId, message.msgId);
// 返回处理后的消息,标记为未读
return {
...message,
msgTitle: processedTitle,
msgContent: processedContent,
isRead: false // 强制设置为未读
};
}
// 如果不是1号,返回处理后的消息(保持原有已读状态)
return {
...message,
msgTitle: processedTitle,
msgContent: processedContent
};
}
// 其他类型的消息保持不变
return message;
})
);
// 过滤出未读的消息(包括被重置为未读的季度填报提醒)
const unreadPopupMessages = processedMessages.filter(message => !message.isRead);
return { dataList: unreadPopupMessages };
}
/**
* 获取上一个季度
*/
function getLastQuarter(): { year: number; quarter: number } {
const currentTime = moment();
const currentYear = currentTime.year();
const currentMonth = currentTime.month() + 1;
let lastQuarter = 0;
let lastQuarterYear = currentYear;
if (currentMonth >= 1 && currentMonth <= 3) {
lastQuarter = 4;
lastQuarterYear = currentYear - 1;
} else if (currentMonth >= 4 && currentMonth <= 6) {
lastQuarter = 1;
} else if (currentMonth >= 7 && currentMonth <= 9) {
lastQuarter = 2;
} else {
lastQuarter = 3;
}
return { year: lastQuarterYear, quarter: lastQuarter };
}
/**
* 重置消息的已读状态为未读
*/
async function resetNotificationReadStatus(eId: string, msgId: string) {
// 检查是否已存在已读记录
const existingRead = await selectData(
OPERATIONALDATATYPE.查询单个,
TABLENAME.企业消息已读状态表,
{
msgId: msgId,
eId: eId
},
["readId"]
);
if (existingRead) {
// 更新已存在记录为未读
const updateData = {
isRead: 0,
readTime: null
};
await operationalData(
OPERATIONALDATATYPE.修改,
TABLENAME.企业消息已读状态表,
updateData,
{ readId: existingRead.readId }
);
} else {
// 如果没有已读记录,创建一条未读记录(可选)
// 通常不需要,因为默认就是未读状态
}
return { isSuccess: true };
}
/** /**
* 企业消息通知 * 企业消息通知 - 季度消息管理
*/ */
import moment = require("moment"); import moment = require("moment");
...@@ -12,9 +12,522 @@ import { MSGTYPE } from "../config/enum/enum"; ...@@ -12,9 +12,522 @@ import { MSGTYPE } from "../config/enum/enum";
/** /**
* 初始化季度消息定时任务(使用setTimeout轮询)
*/
export function initQuarterlyNotificationTask() {
console.log('季度消息定时任务已启动');
// 立即检查并执行一次
checkAndExecuteQuarterlyTasks();
// 设置每天检查一次的定时器
setInterval(checkAndExecuteQuarterlyTasks, 24 * 60 * 60 * 1000); // 24小时检查一次
}
/**
* 检查并执行季度任务
*/
async function checkAndExecuteQuarterlyTasks() {
const now = new Date();
console.log(`检查季度任务执行时间: ${now.toLocaleString()}`);
try {
// 1. 检查是否需要重置填报截止提醒已读状态(季度第一个月1号凌晨3点)
if (shouldResetDeadlineReminder(now)) {
console.log('开始重置填报截止提醒已读状态...');
await resetDeadlineReminderReadStatusForAllEnterprises();
console.log('填报截止提醒已读状态重置完成');
}
// 2. 检查是否需要重置季度填报提醒已读状态(季度最后一个月最后一周的第一天)
if (shouldResetQuarterlyReminder(now)) {
console.log('开始重置季度填报提醒已读状态...');
await resetQuarterlyReminderReadStatusForAllEnterprises();
console.log('季度填报提醒已读状态重置完成');
}
} catch (error) {
console.error('季度任务执行失败:', error);
}
}
/**
* 判断是否需要重置填报截止提醒
*/
function shouldResetDeadlineReminder(now: Date): boolean {
const momentNow = moment(now);
// 检查是否是季度第一个月的一号
const month = momentNow.month(); // 0-11
const date = momentNow.date();
const hour = momentNow.hour();
// 季度第一个月:1月(0), 4月(3), 7月(6), 10月(9)
const isQuarterFirstMonth = [0, 3, 6, 9].includes(month);
// const isFirstDay = date === 1;
// const isTargetHour = hour === 3; // 凌晨3点
return isQuarterFirstMonth;
}
/**
* 判断是否需要重置季度填报提醒
*/
function shouldResetQuarterlyReminder(now: Date): boolean {
const momentNow = moment(now);
// 获取当前季度的最后一个月
const currentQuarterLastMonth = getCurrentQuarterLastMonth(momentNow);
// 检查是否是当前季度的最后一个月
if (momentNow.month() !== currentQuarterLastMonth) {
return false;
}
// 判断是否是最后一周的周一
const daysInMonth = momentNow.daysInMonth();
const isInLastWeek = momentNow.date() > daysInMonth - 7;
// const isMonday = momentNow.day() === 1; // 周一是1
return isInLastWeek;
}
/**
* 为所有企业重置填报截止提醒已读状态
*/
// async function resetDeadlineReminderReadStatusForAllEnterprises() {
// // 获取目标季度信息
// const targetQuarter = getCurrentQuarter();
// const quarterNames = ["一", "二", "三", "四"];
// const quarterName = quarterNames[targetQuarter.quarter - 1];
// // 查找填报截止提醒消息(msgType = 2)
// const deadlineMessages = await selectData(
// OPERATIONALDATATYPE.查询多个,
// TABLENAME.企业消息通知表,
// {
// msgType: MSGTYPE.填报截止提醒,
// // msgTitle: { "%like%": `${targetQuarter.year}年第${quarterName}季度` }
// },
// ["msgId", "eId"]
// );
// // 获取所有企业
// const allEnterprises = await getAllEnterprises();
// // 为每个企业重置已读状态
// for (const enterprise of allEnterprises) {
// // 查找该企业对应的消息
// const enterpriseMessage = deadlineMessages.find(msg => {
// try {
// const eIdArray = JSON.parse(msg.eId || '[]');
// return eIdArray.includes(enterprise.eId) || eIdArray.length === 0; // 包含该企业或发给所有企业
// } catch {
// return false;
// }
// });
// if (enterpriseMessage) {
// await resetMessageReadStatus(enterprise.eId, enterpriseMessage.msgId);
// }
// }
// }
/**
* 为所有企业重置填报截止提醒已读状态
*/
async function resetDeadlineReminderReadStatusForAllEnterprises() {
// 获取目标季度信息
const targetQuarter = getCurrentQuarter();
// 使用新的查询方法
const deadlineMessages = await getMessagesByQuarter(
MSGTYPE.填报截止提醒,
targetQuarter.year,
targetQuarter.quarter
);
// 获取所有企业
const allEnterprises = await getAllEnterprises();
// 为每个企业重置已读状态
for (const enterprise of allEnterprises) {
// 查找该企业对应的消息
const enterpriseMessage = deadlineMessages.find(msg => {
try {
const eIdArray = JSON.parse(msg.eId || '[]');
return eIdArray.includes(enterprise.eId) || eIdArray.length === 0;
} catch {
return false;
}
});
if (enterpriseMessage) {
await resetMessageReadStatus(enterprise.eId, enterpriseMessage.msgId);
}
}
}
/**
* 为所有企业重置季度填报提醒已读状态
*/
// async function resetQuarterlyReminderReadStatusForAllEnterprises() {
// // 获取下一季度信息
// const nextQuarter = getNextQuarter();
// const quarterNames = ["一", "二", "三", "四"];
// const quarterName = quarterNames[nextQuarter.quarter - 1];
// // 查找季度填报提醒消息(msgType = 1)
// const quarterlyMessages = await selectData(
// OPERATIONALDATATYPE.查询多个,
// TABLENAME.企业消息通知表,
// {
// msgType: MSGTYPE.季度填报提醒,
// // msgTitle: { "%like%": `${nextQuarter.year}年第${quarterName}季度` }
// },
// ["msgId", "eId"]
// );
// // 获取所有企业
// const allEnterprises = await getAllEnterprises();
// // 为每个企业重置已读状态
// for (const enterprise of allEnterprises) {
// // 查找该企业对应的消息
// const enterpriseMessage = quarterlyMessages.find(msg => {
// try {
// const eIdArray = JSON.parse(msg.eId || '[]');
// return eIdArray.includes(enterprise.eId) || eIdArray.length === 0;
// } catch {
// return false;
// }
// });
// if (enterpriseMessage) {
// await resetMessageReadStatus(enterprise.eId, enterpriseMessage.msgId);
// }
// }
// }
/**
* 为所有企业重置季度填报提醒已读状态
*/
async function resetQuarterlyReminderReadStatusForAllEnterprises() {
// 获取下一季度信息
const nextQuarter = getNextQuarter();
// 使用新的查询方法
const quarterlyMessages = await getMessagesByQuarter(
MSGTYPE.季度填报提醒,
nextQuarter.year,
nextQuarter.quarter
);
// 获取所有企业
const allEnterprises = await getAllEnterprises();
// 为每个企业重置已读状态
for (const enterprise of allEnterprises) {
// 查找该企业对应的消息
const enterpriseMessage = quarterlyMessages.find(msg => {
try {
const eIdArray = JSON.parse(msg.eId || '[]');
return eIdArray.includes(enterprise.eId) || eIdArray.length === 0;
} catch {
return false;
}
});
if (enterpriseMessage) {
await resetMessageReadStatus(enterprise.eId, enterpriseMessage.msgId);
}
}
}
/**
* 重置消息的已读状态(创建新的未读记录)
*/
async function resetMessageReadStatus(eId: string, msgId: string) {
// 先检查是否已有已读记录
const existingReads = await selectData(
OPERATIONALDATATYPE.查询多个,
TABLENAME.企业消息已读状态表,
{
msgId: msgId,
eId: eId
},
["readId"]
);
// 删除旧的已读记录
if (existingReads && existingReads.length > 0) {
for (const readRecord of existingReads) {
await operationalData(
OPERATIONALDATATYPE.删除,
TABLENAME.企业消息已读状态表,
{},
{ readId: readRecord.readId }
);
}
}
// 创建新的未读记录
const readId = randomId(TABLEID.企业消息已读状态表);
const readData = {
readId: readId,
msgId: msgId,
eId: eId,
readTime: null,
isRead: 0, // 未读
createTime: getMySqlMs()
};
try {
await operationalData(
OPERATIONALDATATYPE.增加,
TABLENAME.企业消息已读状态表,
readData,
{}
);
console.log(`为企业 ${eId} 重置消息 ${msgId} 已读状态成功`);
return true;
} catch (error) {
console.error(`为企业 ${eId} 重置消息 ${msgId} 已读状态失败:`, error);
return false;
}
}
/**
* 获取所有企业ID
*/
async function getAllEnterprises(): Promise<{ eId: string }[]> {
try {
const enterprises = await selectData(
OPERATIONALDATATYPE.查询多个,
TABLENAME.企业基础信息表,
{},
["eId"]
);
return enterprises || [];
} catch (error) {
console.error('获取企业列表失败:', error);
return [];
}
}
/**
* 获取当前季度信息
*/
function getCurrentQuarter(): { year: number; quarter: number; firstMonth: number } {
const now = moment();
const currentYear = now.year();
const currentMonth = now.month(); // 0-11
let quarter = 1;
let firstMonth = 0; // 1月
if (currentMonth >= 0 && currentMonth <= 2) {
quarter = 1;
firstMonth = 0;
} else if (currentMonth >= 3 && currentMonth <= 5) {
quarter = 2;
firstMonth = 3;
} else if (currentMonth >= 6 && currentMonth <= 8) {
quarter = 3;
firstMonth = 6;
} else {
quarter = 4;
firstMonth = 9;
}
return { year: currentYear, quarter, firstMonth };
}
/**
* 获取下一季度信息
*/
function getNextQuarter(): { year: number; quarter: number } {
const currentQuarter = getCurrentQuarter();
let nextYear = currentQuarter.year;
let nextQuarter = currentQuarter.quarter + 1;
if (nextQuarter > 4) {
nextQuarter = 1;
nextYear = currentQuarter.year + 1;
}
return { year: nextYear, quarter: nextQuarter };
}
/**
* 获取当前季度的最后一个月
*/
function getCurrentQuarterLastMonth(momentDate: moment.Moment): number {
const month = momentDate.month(); // 0-11
if (month >= 0 && month <= 2) return 2; // Q1: 3月
if (month >= 3 && month <= 5) return 5; // Q2: 6月
if (month >= 6 && month <= 8) return 8; // Q3: 9月
return 11; // Q4: 12月
}
/**
* 修改后的获取弹窗消息方法
*/
export async function getPopupNotifications(eId: string) {
// 先获取所有消息
const result = await getNotificationList(eId);
// 过滤出弹窗消息
const popupMessages = result.dataList.filter(message => message.isPop);
// 处理模板变量替换
const processedMessages = processNotificationTemplates(popupMessages);
// 过滤出未读的消息
const unreadPopupMessages = processedMessages.filter(message => !message.isRead);
return { dataList: unreadPopupMessages };
}
/**
* 获取企业通知列表
*/
// export async function getNotificationList(eId: string) {
// // 查询消息表,获取所有给该企业或所有企业的消息
// const messageFields = ["msgId", "msgType", "msgTitle", "createTime", "effectiveTime", "msgContent", "eId", "isPop"];
// // 查询条件:eId包含该企业或eId为空数组(发给所有企业)
// const selectParam = {
// "%or%": [
// { eId: { "%like%": eId } }, // 包含该企业ID
// { eId: '[]' } // 发给所有企业
// ]
// };
// let messageList = await selectData(
// OPERATIONALDATATYPE.查询多个,
// TABLENAME.企业消息通知表,
// selectParam,
// messageFields
// );
// // 过滤过期消息
// const currentTime = getMySqlMs();
// messageList = messageList.filter(message =>
// !message.effectiveTime || message.effectiveTime > currentTime
// );
// // 按创建时间排序
// messageList.sort((a, b) =>
// new Date(b.createTime).valueOf() - new Date(a.createTime).valueOf()
// );
// // 获取已读状态
// const readStatusFields = ["readId", "msgId", "eId", "readTime", "isRead"];
// const readStatusList = await selectData(
// OPERATIONALDATATYPE.查询多个,
// TABLENAME.企业消息已读状态表,
// { eId: eId },
// readStatusFields
// );
// // 创建已读状态映射
// const readStatusMap = new Map();
// readStatusList.forEach(status => {
// readStatusMap.set(status.msgId, status);
// });
// // 存储需要创建的未读记录
// const unreadRecordsToCreate = [];
// // 构建返回数据
// const dataList = messageList.map(message => {
// // 解析企业ID数组
// let eIdArray: string[] = [];
// if (message.eId) {
// try {
// eIdArray = JSON.parse(message.eId);
// } catch (error) {
// console.error('解析 eId 失败:', error);
// }
// }
// // 获取已读状态
// const readStatus = readStatusMap.get(message.msgId);
// let isRead = false;
// let readTime = null;
// let readId = null;
// if (readStatus) {
// // 如果已有记录,使用现有状态
// isRead = readStatus.isRead === 1;
// readTime = readStatus.readTime;
// readId = readStatus.readId;
// } else {
// // 如果没有记录,标记为未读,并准备创建记录
// isRead = false;
// readTime = null;
// readId = randomId(TABLEID.企业消息已读状态表); // 尚未创建,没有readId
// // 收集需要创建的未读记录
// unreadRecordsToCreate.push({
// msgId: message.msgId,
// readId,
// eId: eId,
// readTime: null,
// isRead: 0, // 0表示未读
// createTime: getMySqlMs() // 添加创建时间
// });
// }
// return {
// msgId: message.msgId,
// readId,
// msgType: message.msgType,
// msgTitle: message.msgTitle,
// msgContent: message.msgContent,
// eId: eIdArray,
// isPop: message.isPop === 1,
// isRead: isRead,
// effectiveTime: message.effectiveTime ? moment(message.effectiveTime).format("YYYY-MM-DD HH:mm:ss") : null,
// readTime: readTime ? moment(readTime).format("YYYY-MM-DD HH:mm:ss") : null,
// createTime: moment(message.createTime).format("YYYY-MM-DD HH:mm:ss")
// };
// });
// // 批量创建未读记录(如果有需要创建的)
// if (unreadRecordsToCreate.length > 0) {
// try {
// // 这里需要实现批量插入数据的函数
// await createUnreadRecords(unreadRecordsToCreate);
// } catch (error) {
// console.error('创建未读记录失败:', error);
// // 这里可以根据需要决定是否抛出错误
// }
// }
// return { dataList };
// }
/**
* 获取企业通知列表 * 获取企业通知列表
*/ */
export async function getNotificationList(eId: string) { export async function getNotificationList(eId: string) {
// 查询消息表,获取所有给该企业或所有企业的消息 // 查询消息表,获取所有给该企业或所有企业的消息
const messageFields = ["msgId", "msgType", "msgTitle", "createTime", "effectiveTime", "msgContent", "eId", "isPop"]; const messageFields = ["msgId", "msgType", "msgTitle", "createTime", "effectiveTime", "msgContent", "eId", "isPop"];
...@@ -44,6 +557,25 @@ import { MSGTYPE } from "../config/enum/enum"; ...@@ -44,6 +557,25 @@ import { MSGTYPE } from "../config/enum/enum";
new Date(b.createTime).valueOf() - new Date(a.createTime).valueOf() new Date(b.createTime).valueOf() - new Date(a.createTime).valueOf()
); );
// 添加季度任务判断逻辑
const now = new Date();
messageList = messageList.filter(message => {
// 检查是否是填报截止提醒 (msgType = 2)
if (message.msgType === MSGTYPE.填报截止提醒) {
// 只有在季度第一个月1号才返回
return shouldResetDeadlineReminder(now);
}
// 检查是否是季度填报提醒 (msgType = 1)
if (message.msgType === MSGTYPE.季度填报提醒) {
// 只有在季度最后一个月最后一周的周一才返回
return shouldResetQuarterlyReminder(now);
}
// 其他类型的消息直接返回
return true;
});
// 获取已读状态 // 获取已读状态
const readStatusFields = ["readId", "msgId", "eId", "readTime", "isRead"]; const readStatusFields = ["readId", "msgId", "eId", "readTime", "isRead"];
const readStatusList = await selectData( const readStatusList = await selectData(
...@@ -89,12 +621,12 @@ import { MSGTYPE } from "../config/enum/enum"; ...@@ -89,12 +621,12 @@ import { MSGTYPE } from "../config/enum/enum";
// 如果没有记录,标记为未读,并准备创建记录 // 如果没有记录,标记为未读,并准备创建记录
isRead = false; isRead = false;
readTime = null; readTime = null;
readId = null; // 尚未创建,没有readId readId = randomId(TABLEID.企业消息已读状态表); // 尚未创建,没有readId
// 收集需要创建的未读记录 // 收集需要创建的未读记录
unreadRecordsToCreate.push({ unreadRecordsToCreate.push({
msgId: message.msgId, msgId: message.msgId,
readId: randomId(TABLEID.企业消息已读状态表), readId,
eId: eId, eId: eId,
readTime: null, readTime: null,
isRead: 0, // 0表示未读 isRead: 0, // 0表示未读
...@@ -104,7 +636,7 @@ import { MSGTYPE } from "../config/enum/enum"; ...@@ -104,7 +636,7 @@ import { MSGTYPE } from "../config/enum/enum";
return { return {
msgId: message.msgId, msgId: message.msgId,
readId: readId, readId,
msgType: message.msgType, msgType: message.msgType,
msgTitle: message.msgTitle, msgTitle: message.msgTitle,
msgContent: message.msgContent, msgContent: message.msgContent,
...@@ -131,6 +663,7 @@ import { MSGTYPE } from "../config/enum/enum"; ...@@ -131,6 +663,7 @@ import { MSGTYPE } from "../config/enum/enum";
return { dataList }; return { dataList };
} }
/** /**
* 批量创建未读记录 * 批量创建未读记录
*/ */
...@@ -220,172 +753,374 @@ export async function markNotificationAsRead(eId: string, msgId: string, readId: ...@@ -220,172 +753,374 @@ export async function markNotificationAsRead(eId: string, msgId: string, readId:
/** /**
* 批量标记通知为已读 * 处理通知消息模板变量
*/ */
export async function markNotificationsAsRead(eId: string, msgIds: string[], readId:string) { function processNotificationTemplates(messages: any[]) {
for (const msgId of msgIds) { return messages.map(message => {
await markNotificationAsRead(eId, msgId, readId); let processedTitle = message.msgTitle;
let processedContent = message.msgContent;
// 根据消息类型处理不同的模板变量
if (message.msgType === MSGTYPE.季度填报提醒 || message.msgType === MSGTYPE.填报截止提醒) {
const quarterInfo = extractQuarterInfoFromMessage();
if (quarterInfo) {
const quarterNames = ["一", "二", "三", "四"];
// 添加开始时间、结束时间处理
if (message.msgType === MSGTYPE.填报截止提醒) {
processedTitle = processedTitle
.replace(/\${year}/g, quarterInfo.year.toString())
.replace(/\${quarter}/g, quarterNames[quarterInfo.quarter - 1]);
processedContent = processedContent
.replace(/\${year}/g, quarterInfo.year.toString())
.replace(/\${quarter}/g, quarterNames[quarterInfo.quarter - 1]);
const endTime = calculateQuarterEndTime(quarterInfo.year, quarterInfo.quarter);
processedContent = processedContent.replace(/\${endTime}/g, endTime);
} else if (message.msgType === MSGTYPE.季度填报提醒) {
const nextQuarter = getNextQuarter();
let targetYear = nextQuarter.year;
let targetQuarter = nextQuarter.quarter;
processedTitle = processedTitle
.replace(/\${year}/g, targetYear.toString())
.replace(/\${quarter}/g, quarterNames[targetQuarter - 1]);
processedContent = processedContent
.replace(/\${year}/g, targetYear.toString())
.replace(/\${quarter}/g, quarterNames[targetQuarter - 1]);
const startTime = calculateQuarterStartTime(targetYear, targetQuarter);
processedContent = processedContent.replace(/\${startTime}/g, startTime);
}
}
} }
return { isSuccess: true }; return {
...message,
msgTitle: processedTitle,
msgContent: processedContent
};
});
} }
/** /**
* 获取弹窗消息 * 从消息中提取季度信息
* 可以基于创建时间、消息内容等逻辑
*/ */
// export async function getPopupNotifications(eId: string) { function extractQuarterInfoFromMessage(): { year: number; quarter: number } | null {
// // 先获取所有消息 const dateTime = new Date();
// const result = await getNotificationList(eId); const year = dateTime.getFullYear();
const month = dateTime.getMonth() + 1; // 1-12
// // 过滤出弹窗消息且未读的 let quarter = 1;
// const popupMessages = result.dataList.filter(message => if (month >= 1 && month <= 3) quarter = 1;
// message.isPop && !message.isRead else if (month >= 4 && month <= 6) quarter = 2;
// ); else if (month >= 7 && month <= 9) quarter = 3;
else quarter = 4;
// return { dataList: popupMessages }; return { year, quarter };
// } }
/** /**
* 获取弹窗消息 * 计算季度截止时间(当前季度第一个月的最后一天)
*/ */
export async function getPopupNotifications(eId: string) { function calculateQuarterEndTime(year: number, quarter: number): string {
// 先获取所有消息 let endMonth = 0;
const result = await getNotificationList(eId); if (quarter === 1) endMonth = 1; // 3月
else if (quarter === 2) endMonth = 4; // 6月
else if (quarter === 3) endMonth = 7; // 9月
else endMonth = 10; // 12月
// 过滤出弹窗消息 return moment(`${year}-${quarter.toString().padStart(2, '0')}-01`)
const popupMessages = result.dataList.filter(message => message.isPop); .endOf('month')
.format('YYYY-MM-DD');
}
// 特殊处理季度填报提醒消息
const processedMessages = await Promise.all(
popupMessages.map(async (message) => {
// 如果是季度填报提醒类型
if (message.msgType === MSGTYPE.季度填报提醒) {
// 获取上季度的时间
const lastQuarter = getLastQuarter();
// 替换模板变量 /**
const year = lastQuarter.year; * 计算季度开始时间(下一季度的第一个月第一天)
const quarter = lastQuarter.quarter; */
// const quarterNames = ["第一季度", "第二季度", "第三季度", "第四季度"]; function calculateQuarterStartTime(year: number, quarter: number): string {
const quarterNames = ["一", "二", "三", "四"]; // 计算下一季度第一个月
let startMonth = 0;
if (quarter === 1) startMonth = 0; // 1月 (0-11)
else if (quarter === 2) startMonth = 3; // 4月
else if (quarter === 3) startMonth = 6; // 7月
else startMonth = 9; // 10月
// 替换标题和内容中的模板变量 // 下一季度第一天
let processedTitle = message.msgTitle; return moment(`${year}-${(startMonth + 1).toString().padStart(2, '0')}-01`)
let processedContent = message.msgContent; .startOf('day')
.format('YYYY-MM-DD');
}
if (processedTitle.includes("${year}") || processedTitle.includes("${quarter}")) {
processedTitle = processedTitle
.replace(/\${year}/g, year.toString())
.replace(/\${quarter}/g, quarter.toString());
}
if (processedContent.includes("${year}") || processedContent.includes("${quarter}")) { /**
processedContent = processedContent * 从消息标题中提取年份和季度
.replace(/\${year}/g, year.toString()) */
.replace(/\${quarter}/g, quarterNames[quarter-1]); // function extractYearAndQuarterFromTitle(title: string): { year: number; quarter: number } | null {
} // try {
// // 匹配模式:2024年第一季度填报提醒
// const pattern = /(\d{4})年.*?[第]?([一二三四1234])[季度]?/;
// const match = title.match(pattern);
// 检查是否是每季度1号 // if (match && match.length >= 3) {
const today = moment(); // const year = parseInt(match[1]);
const isFirstDayOfMonth = today.date() === 1; // let quarter = 0;
if (isFirstDayOfMonth) { // // 解析季度
// 重置该消息对该企业的已读状态为未读 // const quarterText = match[2];
await resetNotificationReadStatus(eId, message.msgId); // if (quarterText === '一' || quarterText === '1') quarter = 1;
// else if (quarterText === '二' || quarterText === '2') quarter = 2;
// else if (quarterText === '三' || quarterText === '3') quarter = 3;
// else if (quarterText === '四' || quarterText === '4') quarter = 4;
// 返回处理后的消息,标记为未读 // if (quarter > 0) {
return { // return { year, quarter };
...message, // }
msgTitle: processedTitle, // }
msgContent: processedContent,
isRead: false // 强制设置为未读 // return null;
}; // } catch (error) {
// console.error('从标题提取年份和季度失败:', error);
// return null;
// }
// }
/**
* 从消息标题中提取年份和季度
* 支持格式:${year}年第${quarter}季度填报提醒
*/
function extractYearAndQuarterFromTitle(title: string): { year: number; quarter: number } | null {
try {
console.log('正在解析标题:', title);
// 匹配模式1:模板变量格式 ${year}年第${quarter}季度...
const templatePattern = /\$\{year\}年.*?\$\{quarter\}季度/;
const templateMatch = title.match(templatePattern);
if (templateMatch) {
// 对于模板变量,我们需要从其他途径获取实际的年份和季度
// 通常这是从消息上下文或数据库中获取
console.log('检测到模板变量格式的标题');
return null; // 返回 null,让调用方处理
} }
// 如果不是1号,返回处理后的消息(保持原有已读状态) // 匹配模式2:已填充的具体值格式 ${2024}年第${1}季度...
return { const valuePattern = /\$\{(\d{4})\}年.*?\$\{(\d+)\}季度/;
...message, const valueMatch = title.match(valuePattern);
msgTitle: processedTitle,
msgContent: processedContent if (valueMatch && valueMatch.length >= 3) {
}; const year = parseInt(valueMatch[1]);
const quarter = parseInt(valueMatch[2]);
console.log('解析到具体值:', { year, quarter });
// 验证季度是否有效
if (quarter >= 1 && quarter <= 4) {
return { year, quarter };
}
} }
// 其他类型的消息保持不变 // 匹配模式3:已替换的格式 2024年第1季度...
return message; const replacedPattern = /(\d{4})年.*?\s*(\d+)\s*季度/;
}) const replacedMatch = title.match(replacedPattern);
);
// 过滤出未读的消息(包括被重置为未读的季度填报提醒) if (replacedMatch && replacedMatch.length >= 3) {
const unreadPopupMessages = processedMessages.filter(message => !message.isRead); const year = parseInt(replacedMatch[1]);
const quarter = parseInt(replacedMatch[2]);
return { dataList: unreadPopupMessages }; if (quarter >= 1 && quarter <= 4) {
return { year, quarter };
}
}
console.warn('无法从标题提取年份和季度,标题格式:', title);
return null;
} catch (error) {
console.error('从标题提取年份和季度失败:', error, '标题:', title);
return null;
}
} }
/** /**
* 获取上一个季度 * 根据季度信息查询消息
*/ */
function getLastQuarter(): { year: number; quarter: number } { // async function getMessagesByQuarter(msgType: number, year: number, quarter: number, isTemplate: boolean = true) {
const currentTime = moment(); // const quarterNames = ["一", "二", "三", "四"];
const currentYear = currentTime.year(); // const quarterName = quarterNames[quarter - 1];
const currentMonth = currentTime.month() + 1;
let lastQuarter = 0;
let lastQuarterYear = currentYear;
if (currentMonth >= 1 && currentMonth <= 3) {
lastQuarter = 4;
lastQuarterYear = currentYear - 1;
} else if (currentMonth >= 4 && currentMonth <= 6) {
lastQuarter = 1;
} else if (currentMonth >= 7 && currentMonth <= 9) {
lastQuarter = 2;
} else {
lastQuarter = 3;
}
return { year: lastQuarterYear, quarter: lastQuarter }; // let messages = [];
}
// if (isTemplate) {
// // 查询模板格式的消息
// const templateTitles = [
// `\${${year}}年第\${${quarter}}季度填报提醒`,
// `\${${year}}年第\${${quarter}}季度`,
// `\${${year}年第\${${quarter}}季度}`,
// `${year}年第${quarter}季度填报提醒`,
// `${year}年第${quarterName}季度填报提醒`
// ];
// for (const title of templateTitles) {
// const result = await selectData(
// OPERATIONALDATATYPE.查询多个,
// TABLENAME.企业消息通知表,
// {
// msgType: msgType,
// msgTitle: { "%like%": title }
// },
// ["msgId", "eId", "msgTitle"]
// );
// if (result && result.length > 0) {
// messages = [...messages, ...result];
// }
// }
// // 如果上面的精确查询没找到,尝试模糊查询
// if (messages.length === 0) {
// const fuzzyResult = await selectData(
// OPERATIONALDATATYPE.查询多个,
// TABLENAME.企业消息通知表,
// {
// msgType: msgType,
// "%or%": [
// { msgTitle: { "%like%": `${year}年%` } },
// { msgTitle: { "%like%": `\${${year}}年%` } }
// ]
// },
// ["msgId", "eId", "msgTitle"]
// );
// // 进一步过滤
// messages = fuzzyResult.filter(msg => {
// const result = extractYearAndQuarterFromTitle(msg.msgTitle);
// return result && result.year === year && result.quarter === quarter;
// });
// }
// } else {
// // 查询已替换格式的消息
// const result = await selectData(
// OPERATIONALDATATYPE.查询多个,
// TABLENAME.企业消息通知表,
// {
// msgType: msgType,
// msgTitle: { "%like%": `${year}年第${quarterName}季度%` }
// },
// ["msgId", "eId"]
// );
// messages = result;
// }
// return messages;
// }
/** /**
* 重置消息的已读状态为未读 * 根据季度信息查询消息
*/ */
async function resetNotificationReadStatus(eId: string, msgId: string) { async function getMessagesByQuarter(msgType: number, year: number, quarter: number, isTemplate: boolean = true) {
// 检查是否已存在已读记录 const quarterNames = ["一", "二", "三", "四"];
const existingRead = await selectData( const quarterName = quarterNames[quarter - 1];
OPERATIONALDATATYPE.查询单个,
TABLENAME.企业消息已读状态表, let messages = [];
if (isTemplate) {
// 查询模板格式的消息
const templateTitles = [
`\${${year}}年第\${${quarter}}季度填报提醒`,
`\${${year}}年第\${${quarter}}季度`,
`\${${year}年第\${${quarter}}季度}`,
`${year}年第${quarter}季度填报提醒`,
`${year}年第${quarterName}季度填报提醒`
];
for (const title of templateTitles) {
const result = await selectData(
OPERATIONALDATATYPE.查询多个,
TABLENAME.企业消息通知表,
{ {
msgId: msgId, msgType: msgType,
eId: eId msgTitle: { "%like%": title }
}, },
["readId"] ["msgId", "eId", "msgTitle"]
); );
if (existingRead) { if (result && result.length > 0) {
// 更新已存在记录为未读 messages = [...messages, ...result];
const updateData = { }
isRead: 0, }
readTime: null
};
await operationalData( // 如果上面的精确查询没找到,尝试模糊查询
OPERATIONALDATATYPE.修改, if (messages.length === 0) {
TABLENAME.企业消息已读状态表, const fuzzyResult = await selectData(
updateData, OPERATIONALDATATYPE.查询多个,
{ readId: existingRead.readId } TABLENAME.企业消息通知表,
{
msgType: msgType,
"%or%": [
{ msgTitle: { "%like%": `${year}年%` } },
{ msgTitle: { "%like%": `\${${year}}年%` } }
]
},
["msgId", "eId", "msgTitle"]
); );
// 进一步过滤
messages = fuzzyResult.filter(msg => {
const result = extractYearAndQuarterFromTitle(msg.msgTitle);
return result && result.year === year && result.quarter === quarter;
});
}
} else { } else {
// 如果没有已读记录,创建一条未读记录(可选) // 查询已替换格式的消息
// 通常不需要,因为默认就是未读状态 const result = await selectData(
OPERATIONALDATATYPE.查询多个,
TABLENAME.企业消息通知表,
{
msgType: msgType,
msgTitle: { "%like%": `${year}年第${quarterName}季度%` }
},
["msgId", "eId"]
);
messages = result;
} }
return { isSuccess: true }; return messages;
}
/**
* 创建季度消息的辅助函数
*/
function createQuarterlyMessageTemplate(year: number, quarter: number, msgType: number) {
const quarterNames = ["一", "二", "三", "四"];
const quarterName = quarterNames[quarter - 1];
// 建议统一使用模板格式
const templateTitle = `\${${year}}年第\${${quarter}}季度填报提醒`;
// 或者使用已替换格式
const replacedTitle = `${year}年第${quarterName}季度填报提醒`;
// 根据您的需求选择一种格式
return {
msgTitle: templateTitle, // 或者 replacedTitle
msgContent: `\${year}年第\${quarter}季度填报提醒内容...`, // 模板内容
msgType: msgType
};
} }
...@@ -278,7 +278,9 @@ export enum MSGTYPE { ...@@ -278,7 +278,9 @@ export enum MSGTYPE {
// 园区通知 // 园区通知
园区公告 = 9, 园区公告 = 9,
活动通知 = 10 活动通知 = 10,
其他通知 = 100
}; };
......
import { initQuarterlyNotificationTask } from "./biz/massageNotice";
import { initConfig, systemConfig } from "./config/serverConfig"; import { initConfig, systemConfig } from "./config/serverConfig";
import { httpServer } from "./net/http_server"; import { httpServer } from "./net/http_server";
...@@ -6,6 +7,9 @@ async function lanuch() { ...@@ -6,6 +7,9 @@ async function lanuch() {
await initConfig(); await initConfig();
httpServer.createServer(systemConfig.port); httpServer.createServer(systemConfig.port);
console.log('This indicates that the server is started successfully.'); console.log('This indicates that the server is started successfully.');
// 初始化季度消息定时任务
initQuarterlyNotificationTask();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment