kaoqin-services
Version:
考勤系统服务库 (重构版 - 支持n8n webhook)
1 lines • 11.4 kB
JavaScript
;const dayjs=require("dayjs"),{TimeUtils}=require("kaoqin-utils");class KaoqinService{constructor(a){this.configManager=a.configManager,this.stateManager=a.stateManager,this.apiService=a.apiService,this.notificationService=a.notificationService,this.holidayService=a.holidayService,this.logger=console,this.userInfo=null,this.userName=null,this.webhookConfig={url:"https://n8n.201807.xyz/webhook/dkjl",timeout:1e4,auth:"Basic eGlhb2NoZW46eGlhb2M="}}async initialize(a){try{return(this.userName=a||this.configManager.get("USERNAME"),!this.userName)?(this.logger.error("\u672A\u914D\u7F6E\u7528\u6237\u540D"),!1):(console.log(`初始化用户: ${this.userName}`),this.userInfo={userName:this.userName,initialized:!0,initTime:new Date().toISOString()},this.userInfo)}catch(a){return this.logger.error("\u521D\u59CB\u5316\u5931\u8D25",a),!1}}async queryKaoqinRecords(a){const b=dayjs(a).format("YYYY-MM-DD");if(!this.userName)throw new Error("\u65E0\u6CD5\u83B7\u53D6\u7528\u6237\u540D\uFF0C\u8BF7\u5148\u8C03\u7528initialize\u65B9\u6CD5");try{console.log(`查询 ${this.userName} 在 ${b} 的打卡记录...`);const a=await this.apiService.request(this.webhookConfig.url,{name:this.userName,startDate:b},{method:"POST",headers:{"Content-Type":"application/json",Authorization:this.webhookConfig.auth},timeout:this.webhookConfig.timeout}),c=this.transformWebhookResponse(a,b);return c}catch(a){return console.error(`查询 ${b} 打卡记录失败:`,a.message),{results:[],total:0,error:a.message,date:b}}}transformWebhookResponse(a,b){let c=a;if(!Array.isArray(a)){if(a.error||!a.success&&void 0!==a.success)return console.log(`${b} webhook查询失败:`,a.error||"\u672A\u77E5\u9519\u8BEF"),{results:[],total:0,error:a.error||"\u67E5\u8BE2\u5931\u8D25",date:b};c=a.data||a.records||a.results||[]}if(!Array.isArray(c)||0===c.length)return console.log(`${b} 没有找到打卡记录数据`),{results:[],total:0,success:!0,date:b,source:"n8n_webhook"};console.log(`从webhook响应中提取到 ${c.length} 条原始记录`);const d=[],e=c.sort((c,a)=>{const b=c.dx_29_shijian||0,d=a.dx_29_shijian||0;return b-d});e.forEach((a,c)=>{let e="01",f="\u6253\u5361",g=a.dx_29_shijian||null,h=a.dx_29_dkwz||"\u672A\u77E5\u4F4D\u7F6E",i="1",j="\u6B63\u5E38",k=null;if(0===c)e="01",f="\u4E0A\u73ED",k=a.dx_10214_sbcqzt;else if(1===c)e="02",f="\u4E0B\u73ED",k=a.dx_10214_xbcqzt;else if(g){const b=new Date(g),c=b.getHours();6<=c&&12>=c?(e="01",f="\u4E0A\u73ED",k=a.dx_10214_sbcqzt):13<=c&&23>=c&&(e="02",f="\u4E0B\u73ED",k=a.dx_10214_xbcqzt)}j=this.mapStatusName(k),"\u6B63\u5E38"!==j&&"\u51FA\u5DEE"!==j&&(i="0");const l={dx_29_id:a.dx_29_id||a.dx_29_dxbh||`record_${Date.now()}_${Math.random()}`,dx_29_dxlx:e,dx_29_dxlxmc:f,dx_29_dxzt:i,dx_29_dxztmc:j,dx_29_dkrq:b,dx_29_dksj:g,dx_29_dkwz:h,dx_29_ywlsh:a.dx_29_dxbh||`biz_${Date.now()}`,_original:{dkcs:a.dx_29_dkcs,sjdxsl:a.dx_29_sjdxsl,shijian:a.dx_29_shijian}},m=g?new Date(g).toLocaleTimeString():"\u65E0\u65F6\u95F4";console.log(`第${c+1}条: ${f} ${m} - ${h}-${j}`),d.push(l)});const f={results:d,total:d.length,success:!0,date:b,source:"n8n_webhook"};return f}mapClockType(a){if(!a)return"00";const b={上班:"01",下班:"02",出差:"01",迟到:"01",早退:"02",checkin:"01",checkout:"02",signin:"01",signout:"02",morning:"01",evening:"02",business_trip:"01"},c=a.toLowerCase();return b[c]||b[a]||"00"}mapClockTypeName(a){if(!a)return"\u6253\u5361";const b={"01":"\u4E0A\u73ED","02":"\u4E0B\u73ED",出差:"\u51FA\u5DEE",迟到:"\u4E0A\u73ED",早退:"\u4E0B\u73ED",checkin:"\u4E0A\u73ED",checkout:"\u4E0B\u73ED",signin:"\u4E0A\u73ED",signout:"\u4E0B\u73ED",morning:"\u4E0A\u73ED",evening:"\u4E0B\u73ED",上班:"\u4E0A\u73ED",下班:"\u4E0B\u73ED",business_trip:"\u51FA\u5DEE"},c=this.mapClockType(a);return b[c]||b[a]||"\u6253\u5361"}mapStatusName(a){return{1:"\u6B63\u5E38",0:"\u5F02\u5E38",normal:"\u6B63\u5E38",success:"\u6B63\u5E38",error:"\u5F02\u5E38",failed:"\u5F02\u5E38",出差:"\u51FA\u5DEE",迟到:"\u8FDF\u5230",早退:"\u65E9\u9000"}[a]||"\u6B63\u5E38"}async processKaoqinRecords(a){try{const b=a?dayjs(a).format("YYYY-MM-DD"):TimeUtils.getTodayString(),c=a?TimeUtils.getDateFullString(a):TimeUtils.getTodayFullString();console.log(`处理 ${c} 的打卡记录...`);const d=this.stateManager.get("hasSentMorningSuccess"),e=this.stateManager.get("hasSentEveningSuccess");console.log(`当前通知状态标识: 上班提醒[${d?"\u5DF2\u53D1\u9001":"\u672A\u53D1\u9001"}], 下班提醒[${e?"\u5DF2\u53D1\u9001":"\u672A\u53D1\u9001"}]`);const f=this.stateManager.get("lastDate");f!==b&&(console.log(`状态日期(${f})与目标日期(${b})不一致,重置通知状态`),this.stateManager.resetState(),a&&this.stateManager.set("lastDate",b,!1));const g=await this.queryKaoqinRecords(b);if(!g||!g.results)return console.error("API\u8FD4\u56DE\u6570\u636E\u5F02\u5E38"),void(await this.notificationService.sendErrorNotification("\u6253\u5361\u8BB0\u5F55\u67E5\u8BE2\u5F02\u5E38",`${c}:未查询到打卡记录数据,可能是n8n webhook接口异常`));const h=g.results;console.log(`查询到${h.length}条打卡记录`),g.error&&console.warn(`查询时出现警告: ${g.error}`);let i=h.find(a=>"01"===a.dx_29_dxlx||a.dx_29_dxlxmc?.includes("\u4E0A\u73ED")),j=h.find(a=>"02"===a.dx_29_dxlx||a.dx_29_dxlxmc?.includes("\u4E0B\u73ED")),k="",l=!1;if(!i&&!j)k="\u76EE\u524D\u5C1A\u65E0\u6253\u5361\u8BB0\u5F55\uFF0C\u8BF7\u6CE8\u610F\u53CA\u65F6\u6253\u5361\uFF01",TimeUtils.shouldSendReminder(this.configManager.get("REMINDER"),"morning")?(l=!0,console.log("\u5728\u4E0A\u73ED\u65F6\u95F4\u8303\u56F4\u5185\uFF0C\u53D1\u9001\u672A\u6253\u5361\u63D0\u9192")):console.log("\u4E0D\u5728\u4E0A\u73ED\u63D0\u9192\u65F6\u95F4\u8303\u56F4\u5185\uFF0C\u4E0D\u53D1\u9001\u672A\u6253\u5361\u63D0\u9192");else if(!(i&&!j))i&&j?this.stateManager.get("hasSentEveningSuccess")?console.log("\u4E0B\u73ED\u6253\u5361\u6210\u529F\u901A\u77E5\u5DF2\u53D1\u9001\u8FC7\uFF0C\u4E0D\u91CD\u590D\u53D1\u9001"):(k="\u4E0B\u73ED\u6253\u5361\u6210\u529F\uFF0C\u4ECA\u65E5\u6253\u5361\u5DF2\u5B8C\u6210\uFF0C\u8F9B\u82E6\u4E86\uFF01",l=!0,console.log("\u8BBE\u7F6E\u4E0B\u73ED\u6253\u5361\u6210\u529F\u901A\u77E5\u6807\u8BC6\u4E3A[\u5DF2\u53D1\u9001]"),this.stateManager.set("hasSentEveningSuccess",!0),console.log("\u53D1\u9001\u4E0B\u73ED\u6253\u5361\u6210\u529F\u901A\u77E5")):!i&&j&&(k="\u6253\u5361\u8BB0\u5F55\u5F02\u5E38\uFF1A\u6709\u4E0B\u73ED\u5361\u4F46\u65E0\u4E0A\u73ED\u5361\uFF0C\u8BF7\u68C0\u67E5\uFF01",l=!0,console.log("\u53D1\u73B0\u5F02\u5E38\u6253\u5361\u8BB0\u5F55\uFF0C\u53D1\u9001\u5F02\u5E38\u63D0\u9192"));else if(!!this.stateManager.get("hasSentMorningSuccess"))console.log("\u4E0A\u73ED\u6253\u5361\u6210\u529F\u901A\u77E5\u5DF2\u53D1\u9001\u8FC7\uFF0C\u4E0D\u91CD\u590D\u53D1\u9001"),TimeUtils.shouldSendReminder(this.configManager.get("REMINDER"),"evening")?(k="\u63A5\u8FD1\u4E0B\u73ED\u65F6\u95F4\uFF0C\u8BF7\u8BB0\u5F97\u4E0B\u73ED\u6253\u5361\uFF01",l=!0,console.log("\u53D1\u9001\u4E0B\u73ED\u6253\u5361\u63D0\u9192")):console.log("\u4E0D\u5728\u4E0B\u73ED\u63D0\u9192\u65F6\u95F4\u8303\u56F4\u5185\uFF0C\u4E0D\u53D1\u9001\u4E0B\u73ED\u6253\u5361\u63D0\u9192");else if(TimeUtils.shouldSendReminder(this.configManager.get("REMINDER"),"morning"))k="\u4E0A\u73ED\u6253\u5361\u6210\u529F\uFF0C\u795D\u60A8\u5DE5\u4F5C\u6109\u5FEB\uFF01",l=!0,console.log("\u8BBE\u7F6E\u4E0A\u73ED\u6253\u5361\u6210\u529F\u901A\u77E5\u6807\u8BC6\u4E3A[\u5DF2\u53D1\u9001]"),this.stateManager.set("hasSentMorningSuccess",!0),console.log("\u53D1\u9001\u4E0A\u73ED\u6253\u5361\u6210\u529F\u901A\u77E5");else{console.log("\u4E0D\u5728\u4E0A\u73ED\u63D0\u9192\u65F6\u95F4\u8303\u56F4\u5185\uFF0C\u4E0D\u53D1\u9001\u4E0A\u73ED\u6253\u5361\u6210\u529F\u901A\u77E5");const a=new Date,b=a.getHours(),c=a.getMinutes(),d=this.configManager.get("REMINDER.morningEnd"),e=60*d.hour+d.minute;60*b+c>e&&(console.log("\u5F53\u524D\u65F6\u95F4\u5DF2\u8D85\u8FC7\u4E0A\u73ED\u6253\u5361\u63D0\u9192\u65F6\u95F4\u6BB5\uFF0C\u81EA\u52A8\u8BBE\u7F6E\u4E0A\u73ED\u6253\u5361\u6210\u529F\u901A\u77E5\u6807\u8BC6\u4E3A[\u5DF2\u53D1\u9001]"),this.stateManager.set("hasSentMorningSuccess",!0),TimeUtils.shouldSendReminder(this.configManager.get("REMINDER"),"evening")?(k="\u63A5\u8FD1\u4E0B\u73ED\u65F6\u95F4\uFF0C\u8BF7\u8BB0\u5F97\u4E0B\u73ED\u6253\u5361\uFF01",l=!0,console.log("\u53D1\u9001\u4E0B\u73ED\u6253\u5361\u63D0\u9192")):console.log("\u4E0D\u5728\u4E0B\u73ED\u63D0\u9192\u65F6\u95F4\u8303\u56F4\u5185\uFF0C\u4E0D\u53D1\u9001\u4E0B\u73ED\u6253\u5361\u63D0\u9192"))}const m=this.configManager.get("REMINDER.forceSend");if(l||m){k||(i&&j?k=`${c} 打卡已完成,辛苦了!`:i&&!j?k=`${c} 已上班打卡,未下班打卡。`:i||j?k=`${c} 打卡状态检查。`:k=`${c} 暂无打卡记录。`);const a=i?`${TimeUtils.formatTimestamp(i.dx_29_dksj)} (${i.dx_29_dxztmc||"\u6B63\u5E38"})`:"\u5C1A\u672A\u6253\u5361",b=j?`${TimeUtils.formatTimestamp(j.dx_29_dksj)} (${j.dx_29_dxztmc||"\u6B63\u5E38"})`:"\u5C1A\u672A\u6253\u5361";await this.notificationService.sendKaoqinNotification(c,m&&!l?k+" (\u5F3A\u5236\u53D1\u9001\u6A21\u5F0F)":k,a,b),2<h.length&&(await this.notificationService.sendDetailedRecords(c,h))}else if(this.configManager.get("NTFY.forceSend")){console.log("Ntfy\u5F3A\u5236\u53D1\u9001\u6A21\u5F0F\u5DF2\u542F\u7528\uFF0C\u5C06\u5FFD\u7565\u65F6\u95F4\u9650\u5236\u53D1\u9001\u901A\u77E5");const a=i?`${TimeUtils.formatTimestamp(i.dx_29_dksj)} (${i.dx_29_dxztmc||"\u6B63\u5E38"})`:"\u5C1A\u672A\u6253\u5361",b=j?`${TimeUtils.formatTimestamp(j.dx_29_dksj)} (${j.dx_29_dxztmc||"\u6B63\u5E38"})`:"\u5C1A\u672A\u6253\u5361";k||(i&&j?k=`${c} 打卡已完成,辛苦了!`:i&&!j?k=`${c} 已上班打卡,未下班打卡。`:i||j?k=`${c} 打卡状态检查。`:k=`${c} 暂无打卡记录。`),await this.notificationService.sendKaoqinNotification(c,k+" (\u5F3A\u5236\u53D1\u9001\u6A21\u5F0F)",a,b)}else console.log("\u5F53\u524D\u65F6\u95F4\u4E0D\u5728\u63D0\u9192\u8303\u56F4\u5185\uFF0C\u4E14\u672A\u542F\u7528\u5F3A\u5236\u53D1\u9001\u6A21\u5F0F\uFF0C\u8DF3\u8FC7\u901A\u77E5\u53D1\u9001")}catch(a){throw this.logger.error("\u5904\u7406\u8003\u52E4\u8BB0\u5F55\u5931\u8D25",a),a}}async testNtfyNotification(){try{return await this.notificationService.sendKaoqinNotification("\u6D4B\u8BD5\u901A\u77E5","\u8FD9\u662F\u4E00\u6761\u6D4B\u8BD5\u6D88\u606F\uFF0C\u7528\u4E8E\u9A8C\u8BC1Ntfy\u901A\u77E5\u529F\u80FD\u662F\u5426\u6B63\u5E38\u5DE5\u4F5C","\u6D4B\u8BD5\u4E0A\u73ED\u65F6\u95F4","\u6D4B\u8BD5\u4E0B\u73ED\u65F6\u95F4"),console.log("Ntfy\u901A\u77E5\u6D4B\u8BD5\u6210\u529F"),!0}catch(a){return console.error("Ntfy\u901A\u77E5\u6D4B\u8BD5\u5931\u8D25:",a),!1}}async testSpecificDate(a){try{console.log(`\n=== 测试日期 ${a} 的处理流程 ===`);const b=await this.holidayService.isWorkday(a);return(console.log(`${a} 是否为工作日:`,b),!b.isWorkday)?(console.log("\u975E\u5DE5\u4F5C\u65E5\uFF0C\u4E0D\u8FDB\u884C\u6253\u5361\u8BB0\u5F55\u5904\u7406"),!0):(this.stateManager.resetState(),console.log("\u5DF2\u91CD\u7F6E\u901A\u77E5\u72B6\u6001\uFF0C\u5F00\u59CB\u6D4B\u8BD5"),await this.processKaoqinRecords(a),console.log(`=== ${a} 测试完成 ===\n`),!0)}catch(b){return console.error(`测试日期 ${a} 失败:`,b),!1}}resetNotificationStatus(){try{return this.stateManager.resetState(),console.log("\u901A\u77E5\u72B6\u6001\u5DF2\u91CD\u7F6E"),!0}catch(a){return console.error("\u91CD\u7F6E\u901A\u77E5\u72B6\u6001\u5931\u8D25:",a),!1}}}module.exports=KaoqinService;