| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- const API = require('../../lib/API')
- const db = require('../../plugin/DataBase/db')
- const AccessControl = require('../../lib/AccessControl')
- const { BaseStdResponse } = require('../../BaseStdResponse')
- const { checkQuota } = require('../../lib/AIChat/QuotaService')
- const { getUserVipInfo } = require('../../lib/VipService')
- const WeixinBindingService = require('../../lib/AIChat/WeixinBindingService')
- function getSemesterRange() {
- const now = new Date()
- const year = now.getFullYear()
- const feb1 = new Date(year, 1, 1, 0, 0, 0, 0)
- const aug31 = new Date(year, 7, 31, 0, 0, 0, 0)
- const start = now >= feb1 && now < aug31
- ? feb1
- : new Date(now < feb1 ? year - 1 : year, 7, 31, 0, 0, 0, 0)
- const end = new Date(now.getTime() + 24 * 60 * 60 * 1000)
- return { start: start.getTime(), end: end.getTime() }
- }
- function compactQuota(quota) {
- const configured = Boolean(quota?.configured)
- const limit = Number(quota?.limit || 0)
- const used = Number(quota?.used || 0)
- const remaining = Number(quota?.remaining || Math.max(0, limit - used))
- return {
- configured,
- limit,
- used,
- remaining,
- percent: configured && limit > 0 ? Math.max(0, Math.min(100, Math.round((remaining / limit) * 100))) : 0
- }
- }
- function dateKey(date) {
- const month = String(date.getMonth() + 1).padStart(2, '0')
- const day = String(date.getDate()).padStart(2, '0')
- return `${month}-${day}`
- }
- function fillTrend(rows) {
- const rowMap = new Map((rows || []).map(row => [row.day_label, row]))
- const today = new Date()
- const list = []
- for (let i = 13; i >= 0; i--) {
- const date = new Date(today.getFullYear(), today.getMonth(), today.getDate() - i)
- const key = dateKey(date)
- const row = rowMap.get(key) || {}
- list.push({
- date: key,
- delta: Number(row.delta || 0),
- income: Number(row.income || 0),
- expense: Math.abs(Number(row.expense || 0))
- })
- }
- return list
- }
- class AssistantDashboard extends API {
- constructor() {
- super()
- this.setPath('/AIChat/Assistant/Dashboard')
- this.setMethod('GET')
- }
- async onRequest(req, res) {
- const { uuid, session } = req.query
- if ([uuid, session].some(value => value === '' || value === null || value === undefined)) {
- return res.json({ ...BaseStdResponse.MISSING_PARAMETER })
- }
- if (!await AccessControl.checkSession(uuid, session)) {
- return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
- }
- const semester = getSemesterRange()
- const [userRows, accountRows, ledgerRows, webQuota, wechatQuota, vipInfo, binding] = await Promise.all([
- db.query('SELECT ic_count, lepao_count, vip, vip_expire_time FROM users WHERE uuid = ? LIMIT 1', [uuid]),
- db.query(
- `SELECT
- COUNT(*) AS total,
- COALESCE(SUM(CASE WHEN state = 1 THEN 1 ELSE 0 END), 0) AS normal,
- COALESCE(SUM(CASE WHEN state = 1 THEN 0 ELSE 1 END), 0) AS need_login,
- COALESCE(SUM(CASE WHEN auto_run = 1 THEN 1 ELSE 0 END), 0) AS auto_run
- FROM lepao_account
- WHERE create_user = ?
- AND ((update_time >= ? AND update_time < ?) OR (create_time >= ? AND create_time < ?))`,
- [uuid, semester.start, semester.end, semester.start, semester.end]
- ),
- db.query(
- `SELECT
- DATE_FORMAT(created_at, '%m-%d') AS day_label,
- COALESCE(SUM(delta), 0) AS delta,
- COALESCE(SUM(CASE WHEN delta > 0 THEN delta ELSE 0 END), 0) AS income,
- COALESCE(SUM(CASE WHEN delta < 0 THEN delta ELSE 0 END), 0) AS expense
- FROM lepao_count_ledger
- WHERE user_uuid = ? AND created_at >= DATE_SUB(CURDATE(), INTERVAL 13 DAY)
- GROUP BY DATE_FORMAT(created_at, '%m-%d')
- ORDER BY MIN(created_at) ASC`,
- [uuid]
- ),
- checkQuota({ uuid, channel: 'web' }),
- checkQuota({ uuid, channel: 'wechat' }),
- getUserVipInfo(uuid),
- WeixinBindingService.getBindingByUser(uuid)
- ])
- if (!userRows || !accountRows || !ledgerRows) {
- return res.json({ ...BaseStdResponse.DATABASE_ERR })
- }
- const user = userRows[0] || {}
- const account = accountRows[0] || {}
- const total = Number(account.total || 0)
- const normal = Number(account.normal || 0)
- const needLogin = Number(account.need_login || 0)
- const autoRun = Number(account.auto_run || 0)
- const compactBinding = binding ? {
- state: Number(binding.state),
- bound: Number(binding.state) === 1,
- last_user_message_time: Number(binding.last_user_message_time || 0),
- bind_time: Number(binding.bind_time || 0),
- last_error: binding.last_error || ''
- } : null
- return res.json({
- ...BaseStdResponse.OK,
- data: {
- user: {
- lepao_count: Number(user.lepao_count || 0),
- ic_count: Number(user.ic_count || 0),
- vip: Boolean(vipInfo.vip),
- vip_expire_time: Number(vipInfo.vip_expire_time || 0)
- },
- semester: {
- start: semester.start,
- end: semester.end
- },
- account: {
- total,
- normal,
- need_login: needLogin,
- auto_run: autoRun,
- manual: Math.max(0, total - autoRun),
- normal_percent: total > 0 ? Math.round((normal / total) * 100) : 0,
- auto_percent: total > 0 ? Math.round((autoRun / total) * 100) : 0
- },
- quota: {
- vip: Boolean(vipInfo.vip),
- vip_expire_time: Number(vipInfo.vip_expire_time || 0),
- web: compactQuota(webQuota),
- wechat: compactQuota(wechatQuota)
- },
- weixin: compactBinding,
- ledger_trend: fillTrend(ledgerRows)
- }
- })
- }
- }
- module.exports.AssistantDashboard = AssistantDashboard
|