@@ -230,7 +269,11 @@ export default {
chat_workdays_only: 1,
auto_active: 0,
active_interval: 60,
- active_actions_json: ''
+ active_actions_json: '',
+ // 授权相关字段
+ authorization_date: null,
+ authorization_days: 0,
+ remaining_days: 0
},
// 排序优先级列表
priorityList: [
@@ -284,6 +327,16 @@ export default {
},
// 处理 JSON 配置字段
processJsonFields(row) {
+ // 处理授权信息
+ if (row.authorization_date) {
+ this.formData.authorization_date = new Date(row.authorization_date)
+ } else {
+ // 如果没有授权日期,默认使用当天
+ this.formData.authorization_date = new Date()
+ }
+ this.formData.authorization_days = row.authorization_days || 0
+ this.formData.remaining_days = row.remaining_days || 0
+
// is_salary_priority 配置
if (row.is_salary_priority) {
const priorityConfig = typeof row.is_salary_priority === 'string'
@@ -400,6 +453,56 @@ export default {
}
})
},
+ // 获取过期时间
+ getExpireDate(formData) {
+ if (!formData.authorization_date || !formData.authorization_days) {
+ return '未授权'
+ }
+ const authDate = new Date(formData.authorization_date)
+ const expireDate = new Date(authDate.getTime() + formData.authorization_days * 24 * 60 * 60 * 1000)
+ const year = expireDate.getFullYear()
+ const month = String(expireDate.getMonth() + 1).padStart(2, '0')
+ const day = String(expireDate.getDate()).padStart(2, '0')
+ return `${year}-${month}-${day}`
+ },
+ // 获取过期时间颜色
+ getExpireDateColor(formData) {
+ const remainingDays = formData.remaining_days || 0
+ if (remainingDays <= 0) {
+ return '#ed4014'
+ } else if (remainingDays <= 7) {
+ return '#ff9900'
+ } else {
+ return '#515a6e'
+ }
+ },
+ // 获取剩余天数颜色
+ getRemainingDaysColor(remainingDays) {
+ if (!remainingDays || remainingDays <= 0) {
+ return '#ed4014'
+ } else if (remainingDays <= 7) {
+ return '#ff9900'
+ } else {
+ return '#19be6b'
+ }
+ },
+ // 设置授权天数(快捷按钮)
+ setAuthorizationDays(days) {
+ this.formData.authorization_days = days
+ // 如果没有设置授权日期,自动设置为当前日期
+ if (!this.formData.authorization_date) {
+ this.formData.authorization_date = new Date()
+ }
+ // 计算剩余天数(如果已有授权日期)
+ if (this.formData.authorization_date && days > 0) {
+ const authDate = new Date(this.formData.authorization_date)
+ const expireDate = new Date(authDate.getTime() + days * 24 * 60 * 60 * 1000)
+ const now = new Date()
+ const remaining = Math.ceil((expireDate.getTime() - now.getTime()) / (24 * 60 * 60 * 1000))
+ this.formData.remaining_days = Math.max(0, remaining)
+ }
+ this.$Message.success(`已设置授权天数为 ${days} 天`)
+ },
// 返回
handleBack() {
this.$refs.floatPanel.hide()
diff --git a/api/controller_admin/pla_account.js b/api/controller_admin/pla_account.js
index 94d2eec..371f13d 100644
--- a/api/controller_admin/pla_account.js
+++ b/api/controller_admin/pla_account.js
@@ -509,6 +509,158 @@ module.exports = {
const { ids } = body;
const result = await plaAccountService.batchParseLocation(ids);
return ctx.success(result);
+ },
+
+ /**
+ * @swagger
+ * /admin_api/account/check-authorization:
+ * post:
+ * summary: 检查账号授权状态
+ * description: 根据账号ID或SN码检查账号的授权状态(剩余天数、是否过期等)
+ * tags: [后台-账号管理]
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * id:
+ * type: integer
+ * description: 账号ID(可选,如果提供id则不需要sn_code)
+ * sn_code:
+ * type: string
+ * description: 设备SN码(可选,如果提供sn_code则不需要id)
+ * responses:
+ * 200:
+ * description: 检查成功
+ */
+ 'POST /account/check-authorization': async (ctx) => {
+ const { id, sn_code } = ctx.getBody();
+ const models = await Framework.getModels();
+ const { pla_account } = models;
+ const dayjs = require('dayjs');
+
+ if (!id && !sn_code) {
+ return ctx.fail('请提供账号ID或SN码');
+ }
+
+ const where = id ? { id } : { sn_code };
+ const account = await pla_account.findOne({ where });
+
+ if (!account) {
+ return ctx.fail('账号不存在');
+ }
+
+ const accountData = account.toJSON();
+ const authDate = accountData.authorization_date;
+ const authDays = accountData.authorization_days || 0;
+
+ // 计算剩余天数
+ let remaining_days = 0;
+ let is_expired = false;
+ let end_date = null;
+
+ if (authDate && authDays > 0) {
+ const startDate = dayjs(authDate);
+ end_date = startDate.add(authDays, 'day').toDate();
+ const now = dayjs();
+ const remaining = dayjs(end_date).diff(now, 'day', true);
+ remaining_days = Math.max(0, Math.ceil(remaining));
+ is_expired = remaining_days <= 0;
+ }
+
+ return ctx.success({
+ id: accountData.id,
+ sn_code: accountData.sn_code,
+ name: accountData.name,
+ authorization_date: authDate,
+ authorization_days: authDays,
+ remaining_days: remaining_days,
+ end_date: end_date,
+ is_expired: is_expired,
+ is_authorized: !is_expired && remaining_days > 0
+ });
+ },
+
+ /**
+ * @swagger
+ * /admin_api/account/update-authorization:
+ * post:
+ * summary: 更新账号授权信息
+ * description: 更新账号的授权日期和授权天数
+ * tags: [后台-账号管理]
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * required:
+ * - id
+ * properties:
+ * id:
+ * type: integer
+ * description: 账号ID
+ * authorization_date:
+ * type: string
+ * format: date-time
+ * description: 授权日期(可选,不提供则使用当前时间)
+ * authorization_days:
+ * type: integer
+ * description: 授权天数(必填)
+ * responses:
+ * 200:
+ * description: 更新成功
+ */
+ 'POST /account/update-authorization': async (ctx) => {
+ const { id, authorization_date, authorization_days } = ctx.getBody();
+ const models = await Framework.getModels();
+ const { pla_account } = models;
+ const dayjs = require('dayjs');
+
+ if (!id) {
+ return ctx.fail('账号ID不能为空');
+ }
+
+ if (authorization_days === undefined || authorization_days === null) {
+ return ctx.fail('授权天数不能为空');
+ }
+
+ if (authorization_days < 0) {
+ return ctx.fail('授权天数不能为负数');
+ }
+
+ const account = await pla_account.findOne({ where: { id } });
+
+ if (!account) {
+ return ctx.fail('账号不存在');
+ }
+
+ // 如果提供了授权日期,使用提供的日期;否则使用当前时间
+ const authDate = authorization_date ? new Date(authorization_date) : new Date();
+
+ await account.update({
+ authorization_date: authDate,
+ authorization_days: authorization_days
+ });
+
+ // 计算更新后的剩余天数
+ const end_date = dayjs(authDate).add(authorization_days, 'day').toDate();
+ const now = dayjs();
+ const remaining = dayjs(end_date).diff(now, 'day', true);
+ const remaining_days = Math.max(0, Math.ceil(remaining));
+
+ return ctx.success({
+ message: '授权信息更新成功',
+ data: {
+ id: account.id,
+ authorization_date: authDate,
+ authorization_days: authorization_days,
+ remaining_days: remaining_days,
+ end_date: end_date
+ }
+ });
}
};
diff --git a/api/controller_front/user.js b/api/controller_front/user.js
index d0ea90d..b34e890 100644
--- a/api/controller_front/user.js
+++ b/api/controller_front/user.js
@@ -69,6 +69,7 @@ module.exports = {
"POST /user/login": async (ctx) => {
const { sn_code, device_id } = ctx.getBody();
+ const dayjs = require('dayjs');
const { pla_account,device_status} = await Framework.getModels();
@@ -79,6 +80,26 @@ module.exports = {
return ctx.fail('用户不存在');
}
+ // 检查授权状态
+ const userData = user.toJSON();
+ const authDate = userData.authorization_date;
+ const authDays = userData.authorization_days || 0;
+
+ if (authDate && authDays > 0) {
+ const startDate = dayjs(authDate);
+ const endDate = startDate.add(authDays, 'day');
+ const now = dayjs();
+ const remaining = endDate.diff(now, 'day', true);
+ const remaining_days = Math.max(0, Math.ceil(remaining));
+
+ if (remaining_days <= 0) {
+ return ctx.fail('账号授权已过期,请联系管理员续费');
+ }
+ } else {
+ // 如果没有授权信息,检查是否允许登录(可以根据业务需求决定是否允许)
+ // 这里暂时允许登录,但可以添加配置项控制
+ }
+
// 更新设备状态
const device = await device_status.findOne({ where: { sn_code } });
@@ -99,7 +120,20 @@ module.exports = {
device_id: user.device_id
});
- return ctx.success({ token, user: user.toJSON() });
+ // 计算剩余天数并返回
+ let remaining_days = 0;
+ if (authDate && authDays > 0) {
+ const startDate = dayjs(authDate);
+ const endDate = startDate.add(authDays, 'day');
+ const now = dayjs();
+ const remaining = endDate.diff(now, 'day', true);
+ remaining_days = Math.max(0, Math.ceil(remaining));
+ }
+
+ const userInfo = user.toJSON();
+ userInfo.remaining_days = remaining_days;
+
+ return ctx.success({ token, user: userInfo });
}
}
\ No newline at end of file
diff --git a/api/middleware/mqtt/mqttDispatcher.js b/api/middleware/mqtt/mqttDispatcher.js
index 0c93229..02ef788 100644
--- a/api/middleware/mqtt/mqttDispatcher.js
+++ b/api/middleware/mqtt/mqttDispatcher.js
@@ -97,6 +97,18 @@ class MqttDispatcher {
});
}
+ // 检查授权状态(对于需要授权的操作)
+ const authorizationService = require('../../services/authorization_service');
+ const authCheck = await authorizationService.checkAuthorization(deviceCode, 'sn_code');
+ if (!authCheck.is_authorized) {
+ console.log(`[MQTT分发器] 设备 ${deviceCode} 授权检查失败: ${authCheck.message}`);
+ return this.sendMqttResponse(mqttClient, uuid, {
+ code: 403,
+ message: authCheck.message,
+ data: null
+ });
+ }
+
// 获取对应的处理器
const handler = this.actionHandlers.get(action);
if (!handler) {
diff --git a/api/middleware/schedule/command.js b/api/middleware/schedule/command.js
index 4ce2f58..49b9b4d 100644
--- a/api/middleware/schedule/command.js
+++ b/api/middleware/schedule/command.js
@@ -3,6 +3,7 @@ const db = require('../dbProxy');
const jobManager = require('../job/index');
const ScheduleUtils = require('./utils');
const ScheduleConfig = require('./config');
+const authorizationService = require('../../services/authorization_service');
/**
@@ -91,6 +92,12 @@ class CommandManager {
throw new Error(`任务不存在: ${task_id}`);
}
+ // 1.5 检查账号授权状态
+ const authCheck = await authorizationService.checkAuthorization(task.sn_code, 'sn_code');
+ if (!authCheck.is_authorized) {
+ throw new Error(`授权检查失败: ${authCheck.message}`);
+ }
+
// 2. 获取指令信息
const command_name = command.command_name || command.name || '未知指令';
const command_type = command.command_type || command.type;
diff --git a/api/middleware/schedule/taskHandlers.js b/api/middleware/schedule/taskHandlers.js
index 7d74df7..69eca86 100644
--- a/api/middleware/schedule/taskHandlers.js
+++ b/api/middleware/schedule/taskHandlers.js
@@ -48,6 +48,18 @@ class TaskHandlers {
console.log(`[任务处理器] 自动投递任务 - 设备: ${sn_code}, 关键词: ${keyword}`);
+ // 检查授权状态
+ const authorizationService = require('../../services/authorization_service');
+ const authCheck = await authorizationService.checkAuthorization(sn_code, 'sn_code');
+ if (!authCheck.is_authorized) {
+ console.log(`[任务处理器] 自动投递任务 - 设备: ${sn_code} 授权检查失败: ${authCheck.message}`);
+ return {
+ success: false,
+ deliveredCount: 0,
+ message: authCheck.message
+ };
+ }
+
deviceManager.recordTaskStart(sn_code, task);
const startTime = Date.now();
@@ -543,6 +555,18 @@ class TaskHandlers {
const { sn_code, taskParams } = task;
console.log(`[任务处理器] 自动沟通任务 - 设备: ${sn_code}`);
+ // 检查授权状态
+ const authorizationService = require('../../services/authorization_service');
+ const authCheck = await authorizationService.checkAuthorization(sn_code, 'sn_code');
+ if (!authCheck.is_authorized) {
+ console.log(`[任务处理器] 自动沟通任务 - 设备: ${sn_code} 授权检查失败: ${authCheck.message}`);
+ return {
+ success: false,
+ chatCount: 0,
+ message: authCheck.message
+ };
+ }
+
deviceManager.recordTaskStart(sn_code, task);
const startTime = Date.now();
diff --git a/api/model/pla_account.js b/api/model/pla_account.js
index 9aaa4ba..f9d907f 100644
--- a/api/model/pla_account.js
+++ b/api/model/pla_account.js
@@ -264,6 +264,39 @@ module.exports = (db) => {
})
},
+ // 授权相关字段
+ authorization_date: {
+ comment: '授权日期(授权开始时间)',
+ type: Sequelize.DATE,
+ allowNull: true,
+ defaultValue: null
+ },
+ authorization_days: {
+ comment: '授权天数',
+ type: Sequelize.INTEGER,
+ allowNull: false,
+ defaultValue: 0
+ },
+ remaining_days: {
+ comment: '剩余天数(虚拟字段,通过计算得出)',
+ type: Sequelize.VIRTUAL,
+ get: function () {
+ const authDate = this.getDataValue('authorization_date');
+ const authDays = this.getDataValue('authorization_days') || 0;
+
+ if (!authDate || authDays <= 0) {
+ return 0;
+ }
+
+ const startDate = dayjs(authDate);
+ const endDate = startDate.add(authDays, 'day');
+ const now = dayjs();
+
+ // 计算剩余天数
+ const remaining = endDate.diff(now, 'day', true);
+ return Math.max(0, Math.ceil(remaining));
+ }
+ }
});
diff --git a/api/services/authorization_service.js b/api/services/authorization_service.js
new file mode 100644
index 0000000..96c7384
--- /dev/null
+++ b/api/services/authorization_service.js
@@ -0,0 +1,80 @@
+/**
+ * 授权服务
+ * 提供账号授权检查功能
+ */
+
+const db = require('../middleware/dbProxy');
+const dayjs = require('dayjs');
+
+class AuthorizationService {
+ /**
+ * 检查账号授权状态
+ * @param {string|number} identifier - 账号标识(sn_code 或 id)
+ * @param {string} identifierType - 标识类型:'sn_code' 或 'id'
+ * @returns {Promise