@koishi-ce/plugin-analytics 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{icons as e,store as t}from"@koishi-ce/client";import{Fragment as n,computed as r,createBlock as i,createCommentVNode as a,createElementBlock as o,createElementVNode as s,createSlots as ee,createTextVNode as c,createVNode as l,defineAsyncComponent as u,defineComponent as d,h as f,openBlock as p,provide as m,ref as h,renderSlot as g,resolveComponent as _,toDisplayString as v,unref as y,withCtx as b}from"vue";var x=u(()=>import(`./echarts-8St0gdH6.js`)),S=h(`send`);function C({title:e,fields:n=[],showTab:r,options:i}){return d({render:()=>{if(!n.every(e=>t[e]))return null;let a=i(t,S.value);if(a)return f(_(`k-card`),{class:`frameless analytic-chart`},{header:()=>[f(`span`,{class:`left`},[e]),...r?[f(`span`,{class:`right`},[f(`span`,{class:`tab-item`+(S.value===`send`?` active`:``),onClick:()=>S.value=`send`},[`发送`]),f(`span`,{class:`tab-item`+(S.value===`receive`?` active`:``),onClick:()=>S.value=`receive`},[`接收`])])]:[]],default:()=>f(x,{option:a,autoresize:!0})})}})}var w={item(e){return{trigger:`item`,formatter:e}},axis(e){return{trigger:`axis`,axisPointer:{type:`cross`},formatter:e}}},T=e=>{e.slot({type:`analytic-chart`,component:C({title:`各平台消息占比`,fields:[`analytics`],showTab:!0,options({analytics:e},t){if(!e)return;let n=Object.entries(e.messageByBot).map(([e,n])=>({name:e,children:Object.entries(n).map(([e,n])=>({name:n.name||e,value:n[t]}))}));if(n.reduce((e,{children:t})=>e+t.reduce((e,{value:t})=>e+t,0),0))return{tooltip:w.item(({data:e})=>`${e.children?`平台`:`昵称`}:${e.name}<br>日均消息数量:${+e.value.toFixed(1)}`),series:[{type:`sunburst`,data:n,radius:[`0`,`65%`],nodeClick:!1,emphasis:{focus:`ancestor`},levels:[{},{label:{rotate:`tangential`}},{label:{position:`outside`,padding:3,silent:!1}}]}]}}})})},E=e=>{e.slot({type:`analytic-chart`,component:C({title:`指令调用频率`,fields:[`analytics`],options({analytics:e}){if(!e)return;let t=Object.entries(e.commandRate).sort((e,t)=>t[1]-e[1]).map(([e,t])=>({name:e,value:t}));if(t.length)return{tooltip:w.item(({data:e})=>{let t=[e.name];return t.push(`日均调用:${+e.value.toFixed(1)}`),t.join(`<br>`)}),series:[{type:`pie`,data:t,radius:[`35%`,`65%`],minShowLabelAngle:3}]}}})})},D=`日一二三四五六`,O=e=>{e.slot({type:`analytic-chart`,component:C({title:`历史消息数量`,fields:[`analytics`],showTab:!0,options({analytics:e},t){if(!e||!e.messageByDate.length)return;let n=e.messageByDate.slice(1);return{tooltip:w.axis(([e])=>{if(!e)return``;let{name:t,value:n}=e;return`${t} 星期${D[new Date(t).getDay()]}<br>消息数量:${n}`}),xAxis:{type:`category`,data:n.map((e,t)=>new Date(Date.now()-(t+1)*864e5).toLocaleDateString(`zh-CN`)).reverse()},yAxis:{type:`value`},series:{type:`line`,smooth:!0,data:n.map(e=>e[t]).reverse()}}}})})},k=e=>`${(e-.5).toFixed()}:00-${(e+.5).toFixed()}:00`,A=e=>{e.slot({type:`analytic-chart`,component:C({title:`每小时消息数量`,fields:[`analytics`],showTab:!0,options({analytics:e},t){if(e&&!e.messageByHour.every(e=>!e[t]))return{tooltip:w.axis(([n])=>{if(!n)return``;let[r=0]=n.data,{dataIndex:i}=n,a=e.messageByHour[i];return[`${k(r)}`,`日均消息数量:${+(a?.[t]||0).toFixed(1)}`].join(`<br>`)}),xAxis:{type:`value`,min:0,max:24,minInterval:1,maxInterval:4,axisLabel:{formatter:e=>`${e}:00`},axisPointer:{label:{formatter:({value:e})=>k(e)}}},yAxis:{type:`value`},series:[{data:e.messageByHour.map((e,n)=>[n+.5,e[t]||0]),type:`bar`,stack:`1`}]}}})})},j=e=>{e.plugin(O),e.plugin(A),e.plugin(T),e.plugin(E)},M={class:`content`},N={class:`title`},P={class:`value`},F={class:`left`},I={class:`right`},L=d({__name:`numeric`,props:{title:{},icon:{}},setup(e){return(t,n)=>{let r=_(`k-icon`),a=_(`k-card`);return p(),i(a,{class:`k-analytic-number`},ee({default:b(()=>[l(r,{name:e.icon},null,8,[`name`]),s(`div`,M,[s(`p`,N,v(e.title),1),s(`p`,P,[g(t.$slots,`default`,{},()=>[n[0]||=c(`-`,-1)])])])]),_:2},[t.$slots[`footer-left`]?{name:`footer`,fn:b(()=>[s(`span`,F,[g(t.$slots,`footer-left`)]),s(`span`,I,[g(t.$slots,`footer-right`,{},()=>[n[1]||=c(`-`,-1)])])]),key:`0`}:void 0]),1024)}}}),R={key:0,class:`card-grid numeric-grid`},z=d({__name:`index`,setup(e){m(`component:analytic-number`,L);let n=r(()=>{let e=t.analytics.dauHistory.slice(1),n=t.analytics.dauHistory.length-1;return n?e.reduce((e,t)=>e+t,0)/Math.min(e.length,n):0});return(e,r)=>{let i=_(`k-slot-item`),s=_(`k-slot`);return y(t).analytics?(p(),o(`div`,R,[l(s,{name:`analytic-number`},{default:b(()=>[l(i,null,{default:b(()=>[l(L,{icon:`analytic:user`,title:`用户数量`},{default:b(()=>[c(v(y(t).analytics.userCount),1)]),"footer-left":b(()=>[...r[0]||=[c(`昨日新增用户`,-1)]]),"footer-right":b(()=>[c(v(y(t).analytics.userIncrement),1)]),_:1}),l(L,{icon:`analytic:guild`,title:`群组数量`},{default:b(()=>[c(v(y(t).analytics.guildCount),1)]),"footer-left":b(()=>[...r[1]||=[c(`昨日新增群组`,-1)]]),"footer-right":b(()=>[c(v(y(t).analytics.guildIncrement),1)]),_:1}),l(L,{icon:`analytic:heart`,title:`今日 DAU`},{default:b(()=>[c(v(y(t).analytics.dauHistory[0]),1)]),"footer-left":b(()=>[...r[2]||=[c(`近期 DAU`,-1)]]),"footer-right":b(()=>[c(v(+n.value.toFixed(1)),1)]),_:1})]),_:1})]),_:1})])):a(`v-if`,!0)}}}),B=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},V=B(z,[[`__scopeId`,`data-v-c5f44e22`]]),H={class:`card-grid chart-grid`},U=d({__name:`home`,setup(e){return(e,t)=>{let r=_(`k-slot`);return p(),o(n,null,[l(V),s(`div`,H,[l(r,{name:`analytic-chart`})])],64)}}}),W={},G={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 640 512`};function K(e,t){return p(),o(`svg`,G,[...t[0]||=[s(`path`,{fill:`currentColor`,d:`M274.7 304H173.3c-95.73 0-173.3 77.6-173.3 173.3C0 496.5 15.52 512 34.66 512H413.3C432.5 512 448 496.5 448 477.3C448 381.6 370.4 304 274.7 304zM413.3 480H34.66C33.2 480 32 478.8 32 477.3C32 399.4 95.4 336 173.3 336H274.7C352.6 336 416 399.4 416 477.3C416 478.8 414.8 480 413.3 480zM224 256c70.7 0 128-57.31 128-128S294.7 0 224 0C153.3 0 96 57.31 96 128S153.3 256 224 256zM224 32c52.94 0 96 43.06 96 96c0 52.93-43.06 96-96 96S128 180.9 128 128C128 75.06 171.1 32 224 32zM375.1 241C392.9 250.8 412.3 256 432 256C493.8 256 544 205.8 544 144S493.8 32 432 32c-12.83 0-25.39 2.156-37.34 6.391c-8.328 2.953-12.69 12.09-9.734 20.42c2.953 8.344 12.12 12.66 20.42 9.734C413.9 65.53 422.8 64 432 64C476.1 64 512 99.89 512 144S476.1 224 432 224c-14.08 0-27.91-3.703-39.98-10.69c-7.656-4.453-17.44-1.828-21.86 5.828C365.7 226.8 368.3 236.6 375.1 241zM490.7 320H448c-8.844 0-16 7.156-16 16S439.2 352 448 352h42.67C555.4 352 608 404.6 608 469.3C608 475.2 603.2 480 597.3 480H496c-8.844 0-16 7.156-16 16s7.156 16 16 16h101.3C620.9 512 640 492.9 640 469.3C640 386.1 573 320 490.7 320z`},null,-1)]])}var q=B(W,[[`render`,K]]),J={},Y={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 512 512`};function X(e,t){return p(),o(`svg`,Y,[...t[0]||=[s(`path`,{fill:`currentColor`,d:`M255.1 96L267.1 84.02C300.6 51.37 347 36.51 392.6 44.1C461.5 55.58 512 115.2 512 185.1V190.9C512 232.4 494.8 272.1 464.4 300.4L283.7 469.1C276.2 476.1 266.3 480 256 480C245.7 480 235.8 476.1 228.3 469.1L47.59 300.4C17.23 272.1 0 232.4 0 190.9V185.1C0 115.2 50.52 55.58 119.4 44.1C164.1 36.51 211.4 51.37 244 84.02L255.1 96zM255.1 141.3L221.4 106.6C196.1 81.31 160 69.77 124.7 75.66C71.21 84.58 31.1 130.9 31.1 185.1V190.9C31.1 223.6 45.55 254.7 69.42 277L250.1 445.7C251.7 447.2 253.8 448 255.1 448C258.2 448 260.3 447.2 261.9 445.7L442.6 277C466.4 254.7 480 223.6 480 190.9V185.1C480 130.9 440.8 84.58 387.3 75.66C351.1 69.77 315.9 81.31 290.6 106.6L255.1 141.3z`},null,-1)]])}var Z=B(J,[[`render`,X]]),Q={},$={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 512 512`};function te(e,t){return p(),o(`svg`,$,[...t[0]||=[s(`path`,{fill:`currentColor`,d:`M256 0C397.4 0 512 114.6 512 256C512 397.4 397.4 512 256 512C172.2 512 97.87 471.8 51.19 409.6C45.88 402.5 47.31 392.5 54.37 387.2C61.44 381.9 71.47 383.3 76.78 390.4C117.7 444.8 182.7 480 256 480C379.7 480 480 379.7 480 256C480 132.3 379.7 32 256 32C166.7 32 89.51 84.3 53.55 160H144C152.8 160 160 167.2 160 176C160 184.8 152.8 192 144 192H16C7.164 192 0 184.8 0 176V48C0 39.16 7.164 32 16 32C24.84 32 32 39.16 32 48V131.1C75.66 53.29 159.6 0 256 0zM256 128C264.8 128 272 135.2 272 144V249.4L347.3 324.7C353.6 330.9 353.6 341.1 347.3 347.3C341.1 353.6 330.9 353.6 324.7 347.3L244.7 267.3C241.7 264.3 239.1 260.2 239.1 256V144C239.1 135.2 247.2 128 255.1 128H256z`},null,-1)]])}var ne=B(Q,[[`render`,te]]),re={},ie={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 448 512`};function ae(e,t){return p(),o(`svg`,ie,[...t[0]||=[s(`path`,{fill:`currentColor`,d:`M224 256c70.7 0 128-57.31 128-128s-57.3-128-128-128C153.3 0 96 57.31 96 128S153.3 256 224 256zM224 32c52.94 0 96 43.06 96 96c0 52.93-43.06 96-96 96S128 180.9 128 128C128 75.06 171.1 32 224 32zM274.7 304H173.3C77.61 304 0 381.6 0 477.3c0 19.14 15.52 34.67 34.66 34.67h378.7C432.5 512 448 496.5 448 477.3C448 381.6 370.4 304 274.7 304zM413.3 480H34.66C33.2 480 32 478.8 32 477.3C32 399.4 95.4 336 173.3 336h101.3C352.6 336 416 399.4 416 477.3C416 478.8 414.8 480 413.3 480z`},null,-1)]])}var oe=B(re,[[`render`,ae]]);e.register(`analytic:guild`,q),e.register(`analytic:heart`,Z),e.register(`analytic:history`,ne),e.register(`analytic:user`,oe);var se=e=>{e.plugin(j),e.slot({type:`home`,component:U,order:0})};export{se as default};
1
+ import{icons as e,store as t}from"@koishi-ce/client";import{Fragment as n,computed as r,createBlock as i,createCommentVNode as a,createElementBlock as o,createElementVNode as s,createSlots as ee,createTextVNode as c,createVNode as l,defineAsyncComponent as u,defineComponent as d,h as f,openBlock as p,provide as m,ref as h,renderSlot as g,resolveComponent as _,toDisplayString as v,unref as y,withCtx as b}from"vue";var x=u(()=>import(`./echarts-D6RpzsTY.js`)),S=h(`send`);function C({title:e,fields:n=[],showTab:r,options:i}){return d({render:()=>{if(!n.every(e=>t[e]))return null;let a=i(t,S.value);if(a)return f(_(`k-card`),{class:`frameless analytic-chart`},{header:()=>[f(`span`,{class:`left`},[e]),...r?[f(`span`,{class:`right`},[f(`span`,{class:`tab-item`+(S.value===`send`?` active`:``),onClick:()=>S.value=`send`},[`发送`]),f(`span`,{class:`tab-item`+(S.value===`receive`?` active`:``),onClick:()=>S.value=`receive`},[`接收`])])]:[]],default:()=>f(x,{option:a,autoresize:!0})})}})}var w={item(e){return{trigger:`item`,formatter:e}},axis(e){return{trigger:`axis`,axisPointer:{type:`cross`},formatter:e}}},T=e=>{e.slot({type:`analytic-chart`,component:C({title:`各平台消息占比`,fields:[`analytics`],showTab:!0,options({analytics:e},t){if(!e)return;let n=Object.entries(e.messageByBot).map(([e,n])=>({name:e,children:Object.entries(n).map(([e,n])=>({name:n.name||e,value:n[t]}))}));if(n.reduce((e,{children:t})=>e+t.reduce((e,{value:t})=>e+t,0),0))return{tooltip:w.item(({data:e})=>`${e.children?`平台`:`昵称`}:${e.name}<br>日均消息数量:${+e.value.toFixed(1)}`),series:[{type:`sunburst`,data:n,radius:[`0`,`65%`],nodeClick:!1,emphasis:{focus:`ancestor`},levels:[{},{label:{rotate:`tangential`}},{label:{position:`outside`,padding:3,silent:!1}}]}]}}})})},E=e=>{e.slot({type:`analytic-chart`,component:C({title:`指令调用频率`,fields:[`analytics`],options({analytics:e}){if(!e)return;let t=Object.entries(e.commandRate).sort((e,t)=>t[1]-e[1]).map(([e,t])=>({name:e,value:t}));if(t.length)return{tooltip:w.item(({data:e})=>{let t=[e.name];return t.push(`日均调用:${+e.value.toFixed(1)}`),t.join(`<br>`)}),series:[{type:`pie`,data:t,radius:[`35%`,`65%`],minShowLabelAngle:3}]}}})})},D=`日一二三四五六`,O=e=>{e.slot({type:`analytic-chart`,component:C({title:`历史消息数量`,fields:[`analytics`],showTab:!0,options({analytics:e},t){if(!e||!e.messageByDate.length)return;let n=e.messageByDate.slice(1);return{tooltip:w.axis(([e])=>{if(!e)return``;let{name:t,value:n}=e;return`${t} 星期${D[new Date(t).getDay()]}<br>消息数量:${n}`}),xAxis:{type:`category`,data:n.map((e,t)=>new Date(Date.now()-(t+1)*864e5).toLocaleDateString(`zh-CN`)).reverse()},yAxis:{type:`value`},series:{type:`line`,smooth:!0,data:n.map(e=>e[t]).reverse()}}}})})},k=e=>`${(e-.5).toFixed()}:00-${(e+.5).toFixed()}:00`,A=e=>{e.slot({type:`analytic-chart`,component:C({title:`每小时消息数量`,fields:[`analytics`],showTab:!0,options({analytics:e},t){if(e&&!e.messageByHour.every(e=>!e[t]))return{tooltip:w.axis(([n])=>{if(!n)return``;let[r=0]=n.data,{dataIndex:i}=n,a=e.messageByHour[i];return[`${k(r)}`,`日均消息数量:${+(a?.[t]||0).toFixed(1)}`].join(`<br>`)}),xAxis:{type:`value`,min:0,max:24,minInterval:1,maxInterval:4,axisLabel:{formatter:e=>`${e}:00`},axisPointer:{label:{formatter:({value:e})=>k(e)}}},yAxis:{type:`value`},series:[{data:e.messageByHour.map((e,n)=>[n+.5,e[t]||0]),type:`bar`,stack:`1`}]}}})})},j=e=>{e.plugin(O),e.plugin(A),e.plugin(T),e.plugin(E)},M={class:`content`},N={class:`title`},P={class:`value`},F={class:`left`},I={class:`right`},L=d({__name:`numeric`,props:{title:{},icon:{}},setup(e){return(t,n)=>{let r=_(`k-icon`),a=_(`k-card`);return p(),i(a,{class:`k-analytic-number`},ee({default:b(()=>[l(r,{name:e.icon},null,8,[`name`]),s(`div`,M,[s(`p`,N,v(e.title),1),s(`p`,P,[g(t.$slots,`default`,{},()=>[n[0]||=c(`-`,-1)])])])]),_:2},[t.$slots[`footer-left`]?{name:`footer`,fn:b(()=>[s(`span`,F,[g(t.$slots,`footer-left`)]),s(`span`,I,[g(t.$slots,`footer-right`,{},()=>[n[1]||=c(`-`,-1)])])]),key:`0`}:void 0]),1024)}}}),R={key:0,class:`card-grid numeric-grid`},z=d({__name:`index`,setup(e){m(`component:analytic-number`,L);let n=r(()=>{let e=t.analytics.dauHistory.slice(1),n=t.analytics.dauHistory.length-1;return n?e.reduce((e,t)=>e+t,0)/Math.min(e.length,n):0});return(e,r)=>{let i=_(`k-slot-item`),s=_(`k-slot`);return y(t).analytics?(p(),o(`div`,R,[l(s,{name:`analytic-number`},{default:b(()=>[l(i,null,{default:b(()=>[l(L,{icon:`analytic:user`,title:`用户数量`},{default:b(()=>[c(v(y(t).analytics.userCount),1)]),"footer-left":b(()=>[...r[0]||=[c(`昨日新增用户`,-1)]]),"footer-right":b(()=>[c(v(y(t).analytics.userIncrement),1)]),_:1}),l(L,{icon:`analytic:guild`,title:`群组数量`},{default:b(()=>[c(v(y(t).analytics.guildCount),1)]),"footer-left":b(()=>[...r[1]||=[c(`昨日新增群组`,-1)]]),"footer-right":b(()=>[c(v(y(t).analytics.guildIncrement),1)]),_:1}),l(L,{icon:`analytic:heart`,title:`今日 DAU`},{default:b(()=>[c(v(y(t).analytics.dauHistory[0]),1)]),"footer-left":b(()=>[...r[2]||=[c(`近期 DAU`,-1)]]),"footer-right":b(()=>[c(v(+n.value.toFixed(1)),1)]),_:1})]),_:1})]),_:1})])):a(`v-if`,!0)}}}),B=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},V=B(z,[[`__scopeId`,`data-v-41af15ac`]]),H={class:`card-grid chart-grid`},U=d({__name:`home`,setup(e){return(e,t)=>{let r=_(`k-slot`);return p(),o(n,null,[l(V),s(`div`,H,[l(r,{name:`analytic-chart`})])],64)}}}),W={},G={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 640 512`};function K(e,t){return p(),o(`svg`,G,[...t[0]||=[s(`path`,{fill:`currentColor`,d:`M274.7 304H173.3c-95.73 0-173.3 77.6-173.3 173.3C0 496.5 15.52 512 34.66 512H413.3C432.5 512 448 496.5 448 477.3C448 381.6 370.4 304 274.7 304zM413.3 480H34.66C33.2 480 32 478.8 32 477.3C32 399.4 95.4 336 173.3 336H274.7C352.6 336 416 399.4 416 477.3C416 478.8 414.8 480 413.3 480zM224 256c70.7 0 128-57.31 128-128S294.7 0 224 0C153.3 0 96 57.31 96 128S153.3 256 224 256zM224 32c52.94 0 96 43.06 96 96c0 52.93-43.06 96-96 96S128 180.9 128 128C128 75.06 171.1 32 224 32zM375.1 241C392.9 250.8 412.3 256 432 256C493.8 256 544 205.8 544 144S493.8 32 432 32c-12.83 0-25.39 2.156-37.34 6.391c-8.328 2.953-12.69 12.09-9.734 20.42c2.953 8.344 12.12 12.66 20.42 9.734C413.9 65.53 422.8 64 432 64C476.1 64 512 99.89 512 144S476.1 224 432 224c-14.08 0-27.91-3.703-39.98-10.69c-7.656-4.453-17.44-1.828-21.86 5.828C365.7 226.8 368.3 236.6 375.1 241zM490.7 320H448c-8.844 0-16 7.156-16 16S439.2 352 448 352h42.67C555.4 352 608 404.6 608 469.3C608 475.2 603.2 480 597.3 480H496c-8.844 0-16 7.156-16 16s7.156 16 16 16h101.3C620.9 512 640 492.9 640 469.3C640 386.1 573 320 490.7 320z`},null,-1)]])}var q=B(W,[[`render`,K]]),J={},Y={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 512 512`};function X(e,t){return p(),o(`svg`,Y,[...t[0]||=[s(`path`,{fill:`currentColor`,d:`M255.1 96L267.1 84.02C300.6 51.37 347 36.51 392.6 44.1C461.5 55.58 512 115.2 512 185.1V190.9C512 232.4 494.8 272.1 464.4 300.4L283.7 469.1C276.2 476.1 266.3 480 256 480C245.7 480 235.8 476.1 228.3 469.1L47.59 300.4C17.23 272.1 0 232.4 0 190.9V185.1C0 115.2 50.52 55.58 119.4 44.1C164.1 36.51 211.4 51.37 244 84.02L255.1 96zM255.1 141.3L221.4 106.6C196.1 81.31 160 69.77 124.7 75.66C71.21 84.58 31.1 130.9 31.1 185.1V190.9C31.1 223.6 45.55 254.7 69.42 277L250.1 445.7C251.7 447.2 253.8 448 255.1 448C258.2 448 260.3 447.2 261.9 445.7L442.6 277C466.4 254.7 480 223.6 480 190.9V185.1C480 130.9 440.8 84.58 387.3 75.66C351.1 69.77 315.9 81.31 290.6 106.6L255.1 141.3z`},null,-1)]])}var Z=B(J,[[`render`,X]]),Q={},$={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 512 512`};function te(e,t){return p(),o(`svg`,$,[...t[0]||=[s(`path`,{fill:`currentColor`,d:`M256 0C397.4 0 512 114.6 512 256C512 397.4 397.4 512 256 512C172.2 512 97.87 471.8 51.19 409.6C45.88 402.5 47.31 392.5 54.37 387.2C61.44 381.9 71.47 383.3 76.78 390.4C117.7 444.8 182.7 480 256 480C379.7 480 480 379.7 480 256C480 132.3 379.7 32 256 32C166.7 32 89.51 84.3 53.55 160H144C152.8 160 160 167.2 160 176C160 184.8 152.8 192 144 192H16C7.164 192 0 184.8 0 176V48C0 39.16 7.164 32 16 32C24.84 32 32 39.16 32 48V131.1C75.66 53.29 159.6 0 256 0zM256 128C264.8 128 272 135.2 272 144V249.4L347.3 324.7C353.6 330.9 353.6 341.1 347.3 347.3C341.1 353.6 330.9 353.6 324.7 347.3L244.7 267.3C241.7 264.3 239.1 260.2 239.1 256V144C239.1 135.2 247.2 128 255.1 128H256z`},null,-1)]])}var ne=B(Q,[[`render`,te]]),re={},ie={class:`k-icon`,xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 448 512`};function ae(e,t){return p(),o(`svg`,ie,[...t[0]||=[s(`path`,{fill:`currentColor`,d:`M224 256c70.7 0 128-57.31 128-128s-57.3-128-128-128C153.3 0 96 57.31 96 128S153.3 256 224 256zM224 32c52.94 0 96 43.06 96 96c0 52.93-43.06 96-96 96S128 180.9 128 128C128 75.06 171.1 32 224 32zM274.7 304H173.3C77.61 304 0 381.6 0 477.3c0 19.14 15.52 34.67 34.66 34.67h378.7C432.5 512 448 496.5 448 477.3C448 381.6 370.4 304 274.7 304zM413.3 480H34.66C33.2 480 32 478.8 32 477.3C32 399.4 95.4 336 173.3 336h101.3C352.6 336 416 399.4 416 477.3C416 478.8 414.8 480 413.3 480z`},null,-1)]])}var oe=B(re,[[`render`,ae]]);e.register(`analytic:guild`,q),e.register(`analytic:heart`,Z),e.register(`analytic:history`,ne),e.register(`analytic:user`,oe);var se=e=>{e.plugin(j),e.slot({type:`home`,component:U,order:0})};export{se as default};
@@ -1,2 +1,2 @@
1
- .analytic-chart header{justify-content:space-between;display:flex}.analytic-chart header .right{gap:1rem;font-size:1rem;font-weight:400;display:flex}.analytic-chart header .tab-item{cursor:pointer;padding:0 4px}.analytic-chart header .tab-item.active{color:var(--primary);border-bottom:2px solid var(--primary)}.k-analytic-number .k-card-body{justify-content:space-between;align-items:center;margin:1rem 0;display:flex}.k-analytic-number .k-card-body .k-icon{width:3rem;height:2.5rem;color:var(--k-text-normal);transition:color .3s}@media screen and (width<=768px){.k-analytic-number .k-card-body .k-icon{height:2.25rem}}.k-analytic-number .k-card-body .content{float:right;text-align:right;flex-direction:column;justify-content:space-around;height:3rem;display:flex}.k-analytic-number .k-card-body p{margin:0;font-size:.9375rem}.k-analytic-number .k-card-body .value{font-size:1rem}@media (width<=768px){.k-analytic-number .k-card-body{margin:.5rem 0;padding:0 1rem}}.k-analytic-number footer{border-top:1px solid var(--k-card-border);justify-content:space-between;margin-bottom:.75rem;padding-top:.75rem;font-size:.9em;display:flex}@media (width<=768px){.k-analytic-number footer{margin:.5rem 0;padding:.5rem 1rem 0}}.numeric-grid[data-v-c5f44e22]{grid-template-columns:repeat(4,1fr)}@media screen and (width<=1280px){.numeric-grid[data-v-c5f44e22]{grid-template-columns:repeat(2,1fr)}}.card-grid{margin:var(--card-margin);grid-gap:var(--card-margin);display:grid}.chart-grid .echarts{max-width:100%;margin:0 auto}@media (width>=1280px){.chart-grid{grid-template-columns:repeat(2,1fr)}}@media (width>=1280px) and (width>=1600px){.chart-grid .echarts{width:600px;max-width:100%;height:400px;margin:0 auto}}@media (width>=1280px) and (width<=1600px){.chart-grid .echarts{width:480px;height:360px}}@media (width<=1280px){.chart-grid{grid-template-columns:1fr}}@media (width<=1280px) and (width>=768px){.chart-grid .echarts{width:800px;height:400px}}@media (width<=1280px) and (width<=768px) and (width>=480px){.chart-grid .echarts{width:600px;height:400px}}@media (width<=1280px) and (width<=480px){.chart-grid .echarts{width:100%;height:300px}}.grid{display:grid}.flex{display:flex}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter,backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease{transition-timing-function:cubic-bezier(.4,0,.2,1)}
1
+ .analytic-chart header{justify-content:space-between;display:flex}.analytic-chart header .right{gap:1rem;font-size:1rem;font-weight:400;display:flex}.analytic-chart header .tab-item{cursor:pointer;padding:0 4px}.analytic-chart header .tab-item.active{color:var(--primary);border-bottom:2px solid var(--primary)}.k-analytic-number .k-card-body{justify-content:space-between;align-items:center;margin:1rem 0;display:flex}.k-analytic-number .k-card-body .k-icon{width:3rem;height:2.5rem;color:var(--k-text-normal);transition:color .3s}@media screen and (width<=768px){.k-analytic-number .k-card-body .k-icon{height:2.25rem}}.k-analytic-number .k-card-body .content{float:right;text-align:right;flex-direction:column;justify-content:space-around;height:3rem;display:flex}.k-analytic-number .k-card-body p{margin:0;font-size:.9375rem}.k-analytic-number .k-card-body .value{font-size:1rem}@media (width<=768px){.k-analytic-number .k-card-body{margin:.5rem 0;padding:0 1rem}}.k-analytic-number footer{border-top:1px solid var(--k-card-border);justify-content:space-between;margin-bottom:.75rem;padding-top:.75rem;font-size:.9em;display:flex}@media (width<=768px){.k-analytic-number footer{margin:.5rem 0;padding:.5rem 1rem 0}}.numeric-grid[data-v-41af15ac]{grid-template-columns:repeat(4,1fr)}@media screen and (width<=1280px){.numeric-grid[data-v-41af15ac]{grid-template-columns:repeat(2,1fr)}}.card-grid{margin:var(--card-margin);grid-gap:var(--card-margin);display:grid}.chart-grid .echarts{max-width:100%;margin:0 auto}@media (width>=1280px){.chart-grid{grid-template-columns:repeat(2,1fr)}}@media (width>=1280px) and (width>=1600px){.chart-grid .echarts{width:600px;max-width:100%;height:400px;margin:0 auto}}@media (width>=1280px) and (width<=1600px){.chart-grid .echarts{width:480px;height:360px}}@media (width<=1280px){.chart-grid{grid-template-columns:1fr}}@media (width<=1280px) and (width>=768px){.chart-grid .echarts{width:800px;height:400px}}@media (width<=1280px) and (width<=768px) and (width>=480px){.chart-grid .echarts{width:600px;height:400px}}@media (width<=1280px) and (width<=480px){.chart-grid .echarts{width:100%;height:300px}}.grid{display:grid}.flex{display:flex}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter,backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease{transition-timing-function:cubic-bezier(.4,0,.2,1)}
2
2
  /*$vite$:1*/
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@koishi-ce/plugin-analytics",
3
3
  "description": "Show statistics for Koishi application",
4
- "version": "1.0.0",
4
+ "version": "1.0.1",
5
5
  "type": "module",
6
6
  "main": "lib/index.mjs",
7
7
  "typings": "lib/index.d.ts",
@@ -50,14 +50,14 @@
50
50
  }
51
51
  },
52
52
  "peerDependencies": {
53
- "@koishijs/plugin-console": "^5.30.11",
54
- "koishi": "^4.18.11"
53
+ "@koishi-ce/plugin-console": "^1.0.0",
54
+ "@koishi-ce/koishi": "^1.0.0"
55
55
  },
56
56
  "devDependencies": {
57
- "@koishijs/assets": "^1.1.2",
58
- "@koishi-ce/client": "^1.0.0",
57
+ "@koishi-ce/assets": "^1.0.1",
58
+ "@koishi-ce/client": "^1.0.2",
59
59
  "echarts": "^6.1.0",
60
- "@koishi-ce/koishi": "^1.0.0",
60
+ "@koishi-ce/koishi": "^1.0.3",
61
61
  "vue-echarts": "^8.1.0"
62
62
  },
63
63
  "dependencies": {
@@ -0,0 +1,182 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ // Copyright (c) 2026-present Koishi-CE contributors.
4
+
5
+ import { afterAll, beforeAll, describe, expect, it } from "bun:test";
6
+ import { Console, type Entry } from "@koishi-ce/console";
7
+ import { App, type Plugin, Time } from "@koishi-ce/koishi";
8
+ import mock from "@koishi-ce/plugin-mock";
9
+ import memory from "@koishijs/plugin-database-memory";
10
+ import analytics from "./index.ts";
11
+
12
+ // Analytics 类仅默认导出,实例类型经构造器派生(namespace 侧的 Payload 等随实例可用)
13
+ type Analytics = InstanceType<typeof analytics>;
14
+
15
+ /** 控制台服务桩:仅实现入口登记所需的最小面。 */
16
+ class FakeConsole extends Console {
17
+ protected resolveEntry(_files: Entry.Files, _key: string): string[] {
18
+ return [];
19
+ }
20
+ }
21
+
22
+ const app = new App();
23
+
24
+ // 同 admin:CJS 实现配 ESM 声明,nodenext 互操作视图多包一层 default,类型层穿透取真实类
25
+ app.plugin(memory as unknown as typeof memory.default);
26
+ // Console 基类的 static inject 是 cordis 3 旧形态,与 Plugin.Constructor 期待类型不兼容,仅做类型层转型
27
+ app.plugin(FakeConsole as unknown as Plugin.Constructor<App>);
28
+ // statsInternal 归零:每条消息事件后立即落库,便于断言
29
+ app.plugin(analytics, { statsInternal: 0, recentDayCount: 7 });
30
+ app.plugin(mock);
31
+
32
+ // 注册一个空指令,用于驱动 command/execute 计数;带回复以驱动 send 事件
33
+ app.command("analytics-probe").action(() => "probe-done");
34
+
35
+ const service = () => app.get("console.services.analytics") as Analytics;
36
+
37
+ const client = app.mock.client("123", "321");
38
+
39
+ beforeAll(async () => {
40
+ await app.start();
41
+ await app.mock.initUser("123", 1, { id: 1 });
42
+ await app.mock.initChannel("321");
43
+ // 预置昨日消息历史:一收一发(memory 驱动下数组 create 对联合主键报缺键,逐条写入)
44
+ const yesterday = Time.getDateNumber() - 1;
45
+ await app.database.create("analytics.message", {
46
+ date: yesterday,
47
+ hour: 10,
48
+ type: "receive",
49
+ selfId: "514",
50
+ platform: "mock",
51
+ count: 2,
52
+ });
53
+ await app.database.create("analytics.message", {
54
+ date: yesterday,
55
+ hour: 10,
56
+ type: "send",
57
+ selfId: "514",
58
+ platform: "mock",
59
+ count: 3,
60
+ });
61
+ // 预置昨日指令记录:一个用户两次调用
62
+ await app.database.create("analytics.command", {
63
+ date: yesterday,
64
+ hour: 11,
65
+ name: "analytics-probe",
66
+ selfId: "514",
67
+ userId: 1,
68
+ channelId: "321",
69
+ platform: "mock",
70
+ count: 2,
71
+ });
72
+ });
73
+
74
+ afterAll(async () => {
75
+ await app.stop();
76
+ });
77
+
78
+ describe("analytics 统计服务", () => {
79
+ it("消息收发与指令执行被计数落库", async () => {
80
+ await client.receive("analytics-probe");
81
+ // 强制上传,避免与构造期上传落在同一毫秒被跳过
82
+ await service().upload(true);
83
+
84
+ const today = Time.getDateNumber();
85
+ const rows = await app.database.get("analytics.message", {
86
+ date: today,
87
+ });
88
+ // mock 会话:用户消息计入 receive(mock 编码器不派发 send 会话,发出侧见下一用例)
89
+ expect(rows.length).toBeGreaterThan(0);
90
+ const receiveRow = rows.find((row) => row.type === "receive");
91
+ expect(receiveRow?.count).toBe(1);
92
+ expect(receiveRow?.platform).toBe("mock");
93
+ expect(receiveRow?.selfId).toBe("514");
94
+
95
+ const commands = await app.database.get("analytics.command", {
96
+ date: today,
97
+ });
98
+ expect(commands).toHaveLength(1);
99
+ expect(commands[0]).toMatchObject({
100
+ name: "analytics-probe",
101
+ userId: 1,
102
+ channelId: "321",
103
+ });
104
+ });
105
+
106
+ it("send 事件计入发出消息数", async () => {
107
+ // mock 编码器不派发 send 会话,此处直接派发最小会话驱动计数
108
+ app.emit("send", { selfId: "514", platform: "mock" } as never);
109
+ await service().upload(true);
110
+ const today = Time.getDateNumber();
111
+ const rows = await app.database.get("analytics.message", {
112
+ date: today,
113
+ type: "send",
114
+ });
115
+ expect(rows[0]?.count).toBe(1);
116
+ });
117
+
118
+ it("非强制上传在间隔超时后触发(statsInternal=0)", async () => {
119
+ // statsInternal 为 0:任意时间流逝都满足间隔条件,走 forced=false 路径
120
+ await new Promise((resolve) => setTimeout(resolve, 5));
121
+ const before = +service().lastUpdate;
122
+ await service().upload();
123
+ // 触发后 lastUpdate 刷新为更新的时间
124
+ expect(+service().lastUpdate).toBeGreaterThanOrEqual(before);
125
+ });
126
+
127
+ it("非强制上传在跨小时边界触发", async () => {
128
+ // 将记录的小时号置为不可能值:dateHour !== updateHour 分支命中
129
+ service().updateHour = -1;
130
+ await service().upload();
131
+ expect(service().updateHour).toBe(new Date().getHours());
132
+ });
133
+
134
+ it("download 聚合数值指标与各图表数据", async () => {
135
+ const payload = await service().download();
136
+
137
+ expect(payload.userCount).toBeGreaterThanOrEqual(1);
138
+ // 昨日没有新用户与新群组
139
+ expect(payload.userIncrement).toBe(0);
140
+ expect(payload.guildCount).toBe(0);
141
+ expect(payload.guildIncrement).toBe(0);
142
+
143
+ // 指令日均调用:预置 2 次 / 有效天数 1
144
+ expect(payload.commandRate["analytics-probe"]).toBe(2);
145
+
146
+ // DAU 历史:下标 1(昨天)为 1 个去重用户,长度为 8(7+1);
147
+ // 今天的指令执行(上一用例)也计入下标 0
148
+ expect(payload.dauHistory).toHaveLength(8);
149
+ expect(payload.dauHistory[1]).toBe(1);
150
+ expect(payload.dauHistory[0]).toBe(1);
151
+
152
+ // 各机器人消息:昨天的收发日均
153
+ const bot = payload.messageByBot["mock"]?.["514"];
154
+ expect(bot?.receive).toBe(2);
155
+ expect(bot?.send).toBe(3);
156
+
157
+ // 按日历史:昨天收 2 发 3,今天恒为 0 占位
158
+ expect(payload.messageByDate[1]).toEqual({ receive: 2, send: 3 });
159
+ expect(payload.messageByDate[0]).toEqual({ receive: 0, send: 0 });
160
+
161
+ // 按小时分布:24 个时段,10 点的日均收发
162
+ expect(payload.messageByHour).toHaveLength(24);
163
+ expect(payload.messageByHour[10]).toEqual({ receive: 2, send: 3 });
164
+ expect(payload.messageByHour[23]).toEqual({ receive: 0, send: 0 });
165
+ });
166
+
167
+ it("get 按自然日缓存聚合结果", async () => {
168
+ const svc = service();
169
+ let downloads = 0;
170
+ const original = svc.download.bind(svc);
171
+ svc.download = async () => {
172
+ downloads += 1;
173
+ return original();
174
+ };
175
+ const first = await svc.get();
176
+ const second = await svc.get();
177
+ expect(second).toEqual(first);
178
+ // 同一天内重复拉取不触发重新聚合
179
+ expect(downloads).toBe(1);
180
+ svc.download = original;
181
+ });
182
+ });
package/src/index.ts CHANGED
@@ -1,3 +1,7 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (c) 2019-present Shigma and Koishijs contributors.
3
+ // Copyright (c) 2026-present Koishi-CE contributors.
4
+
1
5
  /**
2
6
  * analytics 插件(node 侧):消息与指令的统计分析服务。
3
7
  *