Bladeren bron

feat(message): 添加系统消息模块- 实现了系统公告、用户通知、私信等功能
- 创建了消息设置和模板管理功能- 添加了前后端控制器和模型
- 创建了数据库迁移脚本
- 添加了模块配置和安装信息

runphp 7 maanden geleden
commit
9d69e91ffe

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+composer.phar
+/vendor/
+.idea/

+ 25 - 0
README.md

@@ -0,0 +1,25 @@
+# 系统消息扩展
+
+## 简介
+
+系统消息扩展为SixShop应用提供消息管理功能,包括消息的发送、接收、存储和查询等核心功能。
+
+## 安装
+
+通过 Composer 安装扩展:
+
+`composer require sixdec/message`
+
+## 功能特性
+
+- 消息发送与接收
+- 消息存储与查询
+- 支持多种消息类型(文本、图片、链接等)
+- 消息状态跟踪(已读、未读等)
+- 消息批量处理
+- 消息模板支持
+
+## 使用方法
+
+### 发送消息
+

+ 35 - 0
composer.json

@@ -0,0 +1,35 @@
+{
+  "name": "sixdec/message",
+  "description": "系统消息扩展,为SixShop应用提供消息管理功能,包括消息的发送、接收、存储和查询等核心功能。",
+  "type": "sixshop-extension",
+  "keywords": [
+    "sixshop",
+    "thinkphp",
+    "message",
+    "notification"
+  ],
+  "require": {
+    "php": ">=8.3",
+    "six-shop/core": ">=0.4 <1.0"
+  },
+  "authors": [
+    {
+      "name": "hui he",
+      "email": "runphp@qq.com"
+    }
+  ],
+  "license": "MIT",
+  "autoload": {
+    "psr-4": {
+      "SixDec\\Message\\": "src"
+    }
+  },
+  "extra": {
+    "sixshop": {
+      "id": "message",
+      "class": "SixDec\\Message\\Extension"
+    }
+  },
+  "minimum-stability": "dev",
+  "prefer-stable": true
+}

+ 120 - 0
config.php

@@ -0,0 +1,120 @@
+<?php
+declare(strict_types=1);
+
+return json_decode(<<<'JSON'
+[
+    {
+        "type": "switch",
+        "title": "系统通知",
+        "field": "system_notify",
+        "value": true,
+        "props": {
+            "activeText": "开启",
+            "inactiveText": "关闭"
+        }
+    },
+    {
+        "type": "switch",
+        "title": "活动通知",
+        "field": "activity_notify",
+        "value": true,
+        "props": {
+            "activeText": "开启",
+            "inactiveText": "关闭"
+        }
+    },
+    {
+        "type": "switch",
+        "title": "订单通知",
+        "field": "order_notify",
+        "value": true,
+        "props": {
+            "activeText": "开启",
+            "inactiveText": "关闭"
+        }
+    },
+    {
+        "type": "switch",
+        "title": "物流通知",
+        "field": "logistics_notify",
+        "value": false,
+        "props": {
+            "activeText": "开启",
+            "inactiveText": "关闭"
+        }
+    },
+    {
+        "type": "switch",
+        "title": "私信",
+        "field": "private_message",
+        "value": true,
+        "props": {
+            "activeText": "开启",
+            "inactiveText": "关闭"
+        }
+    },
+    {
+        "type": "switch",
+        "title": "邮件通知",
+        "field": "email_notify",
+        "value": false,
+        "props": {
+            "activeText": "开启",
+            "inactiveText": "关闭"
+        }
+    },
+    {
+        "type": "switch",
+        "title": "短信通知",
+        "field": "sms_notify",
+        "value": false,
+        "props": {
+            "activeText": "开启",
+            "inactiveText": "关闭"
+        }
+    },
+    {
+        "type": "switch",
+        "title": "未读消息提醒",
+        "field": "unread_reminder",
+        "value": false,
+        "props": {
+            "activeText": "开启",
+            "inactiveText": "关闭"
+        }
+    },
+    {
+        "type": "inputNumber",
+        "title": "公告保留天数",
+        "field": "announcement_retention_days",
+        "value": 90,
+        "props": {
+            "min": 1,
+            "max": 365,
+            "placeholder": "请输入公告保留天数"
+        }
+    },
+    {
+        "type": "inputNumber",
+        "title": "通知保留天数",
+        "field": "notification_retention_days",
+        "value": 30,
+        "props": {
+            "min": 1,
+            "max": 365,
+            "placeholder": "请输入通知保留天数"
+        }
+    },
+    {
+        "type": "inputNumber",
+        "title": "私信保留天数",
+        "field": "private_message_retention_days",
+        "value": 180,
+        "props": {
+            "min": 1,
+            "max": 365,
+            "placeholder": "请输入私信保留天数"
+        }
+    }
+]
+JSON, true);

+ 88 - 0
database/install.sql

@@ -0,0 +1,88 @@
+-- 系统公告表
+CREATE TABLE IF NOT EXISTS `cy_message_announcements` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '公告ID',
+  `title` varchar(255) NOT NULL COMMENT '公告标题',
+  `content` text NOT NULL COMMENT '公告内容',
+  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
+  `type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '类型:1-普通公告,2-重要公告,3-紧急公告',
+  `start_time` int(11) NOT NULL DEFAULT '0' COMMENT '开始时间',
+  `end_time` int(11) NOT NULL DEFAULT '0' COMMENT '结束时间',
+  `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
+  `update_time` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间',
+  `delete_time` int(11) DEFAULT NULL COMMENT '删除时间',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统公告表';
+
+-- 用户通知表
+CREATE TABLE IF NOT EXISTS `cy_message_notifications` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '通知ID',
+  `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '用户ID,0表示全部用户',
+  `title` varchar(255) NOT NULL COMMENT '通知标题',
+  `content` text NOT NULL COMMENT '通知内容',
+  `type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '类型:1-系统通知,2-活动通知,3-订单通知,4-物流通知,5-其他',
+  `is_read` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否已读:0-未读,1-已读',
+  `read_time` int(11) NOT NULL DEFAULT '0' COMMENT '阅读时间',
+  `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
+  `update_time` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间',
+  `delete_time` int(11) DEFAULT NULL COMMENT '删除时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_user_id` (`user_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户通知表';
+
+-- 私信表
+CREATE TABLE IF NOT EXISTS `cy_message_privates` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '私信ID',
+  `from_user_id` int(11) unsigned NOT NULL COMMENT '发送者用户ID,0表示系统',
+  `to_user_id` int(11) unsigned NOT NULL COMMENT '接收者用户ID',
+  `content` text NOT NULL COMMENT '私信内容',
+  `is_read` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否已读:0-未读,1-已读',
+  `read_time` int(11) NOT NULL DEFAULT '0' COMMENT '阅读时间',
+  `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
+  `update_time` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间',
+  `delete_time` int(11) DEFAULT NULL COMMENT '删除时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_from_user_id` (`from_user_id`),
+  KEY `idx_to_user_id` (`to_user_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='私信表';
+
+-- 消息设置表
+CREATE TABLE IF NOT EXISTS `cy_message_settings` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '设置ID',
+  `user_id` int(11) unsigned NOT NULL COMMENT '用户ID,0表示系统默认设置',
+  `system_notify` tinyint(1) NOT NULL DEFAULT '1' COMMENT '系统通知:0-关闭,1-开启',
+  `activity_notify` tinyint(1) NOT NULL DEFAULT '1' COMMENT '活动通知:0-关闭,1-开启',
+  `order_notify` tinyint(1) NOT NULL DEFAULT '1' COMMENT '订单通知:0-关闭,1-开启',
+  `logistics_notify` tinyint(1) NOT NULL DEFAULT '1' COMMENT '物流通知:0-关闭,1-开启',
+  `private_message` tinyint(1) NOT NULL DEFAULT '1' COMMENT '私信:0-关闭,1-开启',
+  `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
+  `update_time` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_user_id` (`user_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息设置表';
+
+-- 消息模板表
+CREATE TABLE IF NOT EXISTS `cy_message_templates` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '模板ID',
+  `code` varchar(50) NOT NULL COMMENT '模板代码',
+  `name` varchar(100) NOT NULL COMMENT '模板名称',
+  `title` varchar(255) NOT NULL COMMENT '标题模板',
+  `content` text NOT NULL COMMENT '内容模板',
+  `type` tinyint(1) NOT NULL DEFAULT '1' COMMENT '类型:1-系统通知,2-活动通知,3-订单通知,4-物流通知,5-私信,6-其他',
+  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
+  `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间',
+  `update_time` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_code` (`code`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息模板表';
+
+-- 插入默认消息模板数据
+INSERT INTO `cy_message_templates` (`code`, `name`, `title`, `content`, `type`, `status`, `create_time`, `update_time`) VALUES
+('system_welcome', '欢迎注册', '欢迎加入{site_name}', '亲爱的{user_name},欢迎您加入{site_name},祝您购物愉快!', 1, 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
+('order_created', '订单创建', '您的订单已创建', '亲爱的{user_name},您的订单{order_no}已创建成功,请及时支付。', 3, 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
+('order_paid', '订单支付', '您的订单已支付', '亲爱的{user_name},您的订单{order_no}已支付成功,我们将尽快为您发货。', 3, 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
+('order_shipped', '订单发货', '您的订单已发货', '亲爱的{user_name},您的订单{order_no}已发货,物流单号:{tracking_no}。', 4, 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
+('order_completed', '订单完成', '您的订单已完成', '亲爱的{user_name},您的订单{order_no}已完成,感谢您的购买!', 3, 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP());
+
+-- 插入默认系统设置
+INSERT INTO `cy_message_settings` (`user_id`, `system_notify`, `activity_notify`, `order_notify`, `logistics_notify`, `private_message`, `create_time`, `update_time`) VALUES
+(0, 1, 1, 1, 1, 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP());

+ 191 - 0
database/migrations/20250724_create_message_tables.php

@@ -0,0 +1,191 @@
+<?php
+declare(strict_types=1);
+
+use think\migration\Migrator;
+use think\migration\db\Column;
+
+class CreateMessageTables extends Migrator
+{
+    /**
+     * 执行迁移
+     */
+    public function up()
+    {
+        // 创建系统公告表
+        $this->table('message_announcements', [
+            'engine' => 'InnoDB',
+            'comment' => '系统公告表',
+            'charset' => 'utf8mb4',
+            'collation' => 'utf8mb4_general_ci',
+        ])
+            ->addColumn('title', 'string', ['limit' => 255, 'null' => false, 'comment' => '公告标题'])
+            ->addColumn('content', 'text', ['null' => false, 'comment' => '公告内容'])
+            ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => false, 'comment' => '状态:0-禁用,1-启用'])
+            ->addColumn('type', 'integer', ['limit' => 1, 'default' => 1, 'null' => false, 'comment' => '类型:1-普通公告,2-重要公告,3-紧急公告'])
+            ->addColumn('start_time', 'integer', ['default' => 0, 'null' => false, 'comment' => '开始时间'])
+            ->addColumn('end_time', 'integer', ['default' => 0, 'null' => false, 'comment' => '结束时间'])
+            ->addColumn('create_time', 'integer', ['default' => 0, 'null' => false, 'comment' => '创建时间'])
+            ->addColumn('update_time', 'integer', ['default' => 0, 'null' => false, 'comment' => '更新时间'])
+            ->addColumn('delete_time', 'integer', ['null' => true, 'comment' => '删除时间'])
+            ->create();
+
+        // 创建用户通知表
+        $this->table('message_notifications', [
+            'engine' => 'InnoDB',
+            'comment' => '用户通知表',
+            'charset' => 'utf8mb4',
+            'collation' => 'utf8mb4_general_ci',
+        ])
+            ->addColumn('user_id', 'integer', ['limit' => 11, 'default' => 0, 'null' => false, 'comment' => '用户ID,0表示全部用户'])
+            ->addColumn('title', 'string', ['limit' => 255, 'null' => false, 'comment' => '通知标题'])
+            ->addColumn('content', 'text', ['null' => false, 'comment' => '通知内容'])
+            ->addColumn('type', 'integer', ['limit' => 1, 'default' => 1, 'null' => false, 'comment' => '类型:1-系统通知,2-活动通知,3-订单通知,4-物流通知,5-其他'])
+            ->addColumn('is_read', 'integer', ['limit' => 1, 'default' => 0, 'null' => false, 'comment' => '是否已读:0-未读,1-已读'])
+            ->addColumn('read_time', 'integer', ['default' => 0, 'null' => false, 'comment' => '阅读时间'])
+            ->addColumn('create_time', 'integer', ['default' => 0, 'null' => false, 'comment' => '创建时间'])
+            ->addColumn('update_time', 'integer', ['default' => 0, 'null' => false, 'comment' => '更新时间'])
+            ->addColumn('delete_time', 'integer', ['null' => true, 'comment' => '删除时间'])
+            ->addIndex(['user_id'], ['name' => 'idx_user_id'])
+            ->create();
+
+        // 创建私信表
+        $this->table('message_privates', [
+            'engine' => 'InnoDB',
+            'comment' => '私信表',
+            'charset' => 'utf8mb4',
+            'collation' => 'utf8mb4_general_ci',
+        ])
+            ->addColumn('from_user_id', 'integer', ['limit' => 11, 'null' => false, 'comment' => '发送者用户ID,0表示系统'])
+            ->addColumn('to_user_id', 'integer', ['limit' => 11, 'null' => false, 'comment' => '接收者用户ID'])
+            ->addColumn('content', 'text', ['null' => false, 'comment' => '私信内容'])
+            ->addColumn('is_read', 'integer', ['limit' => 1, 'default' => 0, 'null' => false, 'comment' => '是否已读:0-未读,1-已读'])
+            ->addColumn('read_time', 'integer', ['default' => 0, 'null' => false, 'comment' => '阅读时间'])
+            ->addColumn('create_time', 'integer', ['default' => 0, 'null' => false, 'comment' => '创建时间'])
+            ->addColumn('update_time', 'integer', ['default' => 0, 'null' => false, 'comment' => '更新时间'])
+            ->addColumn('delete_time', 'integer', ['null' => true, 'comment' => '删除时间'])
+            ->addIndex(['from_user_id'], ['name' => 'idx_from_user_id'])
+            ->addIndex(['to_user_id'], ['name' => 'idx_to_user_id'])
+            ->create();
+
+        // 创建消息设置表
+        $this->table('message_settings', [
+            'engine' => 'InnoDB',
+            'comment' => '消息设置表',
+            'charset' => 'utf8mb4',
+            'collation' => 'utf8mb4_general_ci',
+        ])
+            ->addColumn('user_id', 'integer', ['limit' => 11, 'null' => false, 'comment' => '用户ID,0表示系统默认设置'])
+            ->addColumn('system_notify', 'integer', ['limit' => 1, 'default' => 1, 'null' => false, 'comment' => '系统通知:0-关闭,1-开启'])
+            ->addColumn('activity_notify', 'integer', ['limit' => 1, 'default' => 1, 'null' => false, 'comment' => '活动通知:0-关闭,1-开启'])
+            ->addColumn('order_notify', 'integer', ['limit' => 1, 'default' => 1, 'null' => false, 'comment' => '订单通知:0-关闭,1-开启'])
+            ->addColumn('logistics_notify', 'integer', ['limit' => 1, 'default' => 1, 'null' => false, 'comment' => '物流通知:0-关闭,1-开启'])
+            ->addColumn('private_message', 'integer', ['limit' => 1, 'default' => 1, 'null' => false, 'comment' => '私信:0-关闭,1-开启'])
+            ->addColumn('create_time', 'integer', ['default' => 0, 'null' => false, 'comment' => '创建时间'])
+            ->addColumn('update_time', 'integer', ['default' => 0, 'null' => false, 'comment' => '更新时间'])
+            ->addIndex(['user_id'], ['name' => 'idx_user_id', 'unique' => true])
+            ->create();
+
+        // 创建消息模板表
+        $this->table('message_templates', [
+            'engine' => 'InnoDB',
+            'comment' => '消息模板表',
+            'charset' => 'utf8mb4',
+            'collation' => 'utf8mb4_general_ci',
+        ])
+            ->addColumn('code', 'string', ['limit' => 50, 'null' => false, 'comment' => '模板代码'])
+            ->addColumn('name', 'string', ['limit' => 100, 'null' => false, 'comment' => '模板名称'])
+            ->addColumn('title', 'string', ['limit' => 255, 'null' => false, 'comment' => '标题模板'])
+            ->addColumn('content', 'text', ['null' => false, 'comment' => '内容模板'])
+            ->addColumn('type', 'integer', ['limit' => 1, 'default' => 1, 'null' => false, 'comment' => '类型:1-系统通知,2-活动通知,3-订单通知,4-物流通知,5-私信,6-其他'])
+            ->addColumn('status', 'integer', ['limit' => 1, 'default' => 1, 'null' => false, 'comment' => '状态:0-禁用,1-启用'])
+            ->addColumn('create_time', 'integer', ['default' => 0, 'null' => false, 'comment' => '创建时间'])
+            ->addColumn('update_time', 'integer', ['default' => 0, 'null' => false, 'comment' => '更新时间'])
+            ->addIndex(['code'], ['name' => 'idx_code', 'unique' => true])
+            ->create();
+
+        // 插入默认消息模板数据
+        $currentTime = time();
+        $this->table('message_templates')
+            ->insert([
+                [
+                    'code' => 'system_welcome',
+                    'name' => '欢迎注册',
+                    'title' => '欢迎加入{site_name}',
+                    'content' => '亲爱的{user_name},欢迎您加入{site_name},祝您购物愉快!',
+                    'type' => 1,
+                    'status' => 1,
+                    'create_time' => $currentTime,
+                    'update_time' => $currentTime
+                ],
+                [
+                    'code' => 'order_created',
+                    'name' => '订单创建',
+                    'title' => '您的订单已创建',
+                    'content' => '亲爱的{user_name},您的订单{order_no}已创建成功,请及时支付。',
+                    'type' => 3,
+                    'status' => 1,
+                    'create_time' => $currentTime,
+                    'update_time' => $currentTime
+                ],
+                [
+                    'code' => 'order_paid',
+                    'name' => '订单支付',
+                    'title' => '您的订单已支付',
+                    'content' => '亲爱的{user_name},您的订单{order_no}已支付成功,我们将尽快为您发货。',
+                    'type' => 3,
+                    'status' => 1,
+                    'create_time' => $currentTime,
+                    'update_time' => $currentTime
+                ],
+                [
+                    'code' => 'order_shipped',
+                    'name' => '订单发货',
+                    'title' => '您的订单已发货',
+                    'content' => '亲爱的{user_name},您的订单{order_no}已发货,物流单号:{tracking_no}。',
+                    'type' => 4,
+                    'status' => 1,
+                    'create_time' => $currentTime,
+                    'update_time' => $currentTime
+                ],
+                [
+                    'code' => 'order_completed',
+                    'name' => '订单完成',
+                    'title' => '您的订单已完成',
+                    'content' => '亲爱的{user_name},您的订单{order_no}已完成,感谢您的购买!',
+                    'type' => 3,
+                    'status' => 1,
+                    'create_time' => $currentTime,
+                    'update_time' => $currentTime
+                ]
+            ])
+            ->save();
+
+        // 插入默认系统设置
+        $this->table('message_settings')
+            ->insert([
+                [
+                    'user_id' => 0,
+                    'system_notify' => 1,
+                    'activity_notify' => 1,
+                    'order_notify' => 1,
+                    'logistics_notify' => 1,
+                    'private_message' => 1,
+                    'create_time' => $currentTime,
+                    'update_time' => $currentTime
+                ]
+            ])
+            ->save();
+    }
+
+    /**
+     * 回滚迁移
+     */
+    public function down()
+    {
+        $this->table('message_announcements')->drop();
+        $this->table('message_notifications')->drop();
+        $this->table('message_privates')->drop();
+        $this->table('message_settings')->drop();
+        $this->table('message_templates')->drop();
+    }
+}

+ 14 - 0
database/uninstall.sql

@@ -0,0 +1,14 @@
+-- 删除系统公告表
+DROP TABLE IF EXISTS `cy_message_announcements`;
+
+-- 删除用户通知表
+DROP TABLE IF EXISTS `cy_message_notifications`;
+
+-- 删除私信表
+DROP TABLE IF EXISTS `cy_message_privates`;
+
+-- 删除消息设置表
+DROP TABLE IF EXISTS `cy_message_settings`;
+
+-- 删除消息模板表
+DROP TABLE IF EXISTS `cy_message_templates`;

+ 63 - 0
info.php

@@ -0,0 +1,63 @@
+<?php
+declare(strict_types=1);
+
+return [
+    'id' => 'message',
+    'name' => '系统消息',
+    'description' => '系统消息模块,支持站内通知、系统公告、用户私信等功能',
+    'version' => '1.0.0',
+    'core_version' => '^1.0',
+    'author' => 'SixShop',
+    'image' => '',
+    'license' => 'MIT',
+    'website' => 'https://www.sixshop.com',
+    'email' => 'support@sixshop.com',
+    'require' => [
+        'php' => '>=7.4.0',
+        'framework' => '>=1.0.0',
+        'core' => '>=1.0.0',
+    ],
+    'config' => [
+        'admin' => [
+            'menu' => [
+                [
+                    'name' => '消息管理',
+                    'icon' => 'layui-icon-notice',
+                    'children' => [
+                        [
+                            'name' => '公告管理',
+                            'uri' => 'message/announcement',
+                        ],
+                        [
+                            'name' => '通知管理',
+                            'uri' => 'message/notification',
+                        ],
+                        [
+                            'name' => '私信管理',
+                            'uri' => 'message/private',
+                        ],
+                        [
+                            'name' => '消息设置',
+                            'uri' => 'message/setting',
+                        ],
+                    ],
+                ],
+            ],
+        ],
+    ],
+    'install' => [
+        'sql' => [
+            'install.sql'
+        ]
+    ],
+    'uninstall' => [
+        'sql' => [
+            'uninstall.sql'
+        ]
+    ],
+    'upgrade' => [
+        'sql' => [
+            'upgrade.sql'
+        ]
+    ]
+];

+ 68 - 0
route/admin.php

@@ -0,0 +1,68 @@
+<?php
+declare(strict_types=1);
+
+use SixDec\Message\Controller\Admin\{
+    AnnouncementController,
+    NotificationController,
+    PrivateMessageController,
+    MessageTemplateController,
+    MessageSettingController
+};
+use think\facade\Route;
+
+// 后台管理API路由
+// 路由前缀: /admin/message
+
+// 公告管理路由
+Route::resource('announcement', AnnouncementController::class, function () {
+    Route::get('/', [AnnouncementController::class, 'getList']); // 覆盖默认的index路由
+    Route::get('detail', [AnnouncementController::class, 'getDetail']);
+    //Route::post('add', [AnnouncementController::class, 'add']);
+    Route::post('update', [AnnouncementController::class, 'update']);
+    Route::post('delete', [AnnouncementController::class, 'delete']);
+    Route::post('batch_delete', [AnnouncementController::class, 'batchDelete']);
+    Route::post('update_status', [AnnouncementController::class, 'updateStatus']);
+})->middleware(['auth']);
+
+// 通知管理路由
+Route::resource('notification', NotificationController::class, function () {
+    Route::get('/', [NotificationController::class, 'getList']); // 覆盖默认的index路由
+    Route::get('detail', [NotificationController::class, 'getDetail']);
+    Route::post('send_to_user', [NotificationController::class, 'sendToUser']);
+    Route::post('send_to_users', [NotificationController::class, 'sendToUsers']);
+    Route::post('send_global', [NotificationController::class, 'sendGlobal']);
+    Route::post('delete', [NotificationController::class, 'delete']);
+    Route::post('batch_delete', [NotificationController::class, 'batchDelete']);
+})->middleware(['auth']);
+
+// 私信管理路由
+Route::resource('private_message', PrivateMessageController::class, function () {
+    Route::get('/', [PrivateMessageController::class, 'getList']); // 覆盖默认的index路由
+    Route::get('detail', [PrivateMessageController::class, 'getDetail']);
+    Route::post('send_system_message', [PrivateMessageController::class, 'sendSystemMessage']);
+    Route::post('batch_send_system_message', [PrivateMessageController::class, 'batchSendSystemMessage']);
+    Route::post('delete', [PrivateMessageController::class, 'delete']);
+    Route::post('batch_delete', [PrivateMessageController::class, 'batchDelete']);
+})->middleware(['auth']);
+
+// 消息模板管理路由
+Route::resource('template', MessageTemplateController::class, function () {
+    Route::get('detail', [MessageTemplateController::class, 'getDetail']);
+    //Route::put('add_template', [MessageTemplateController::class, 'save']);
+    Route::post('batch_delete', [MessageTemplateController::class, 'batchDelete']);
+    Route::post('update_status', [MessageTemplateController::class, 'updateStatus']);
+})->middleware(['auth']);
+
+// 消息设置管理路由
+Route::group('setting', function () {
+    // 获取消息设置列表
+    Route::get('list', [MessageSettingController::class, 'getList']);
+    // 获取系统默认设置
+    Route::get('default', [MessageSettingController::class, 'getDefaultSetting']);
+    // 获取用户消息设置
+    Route::get('user', [MessageSettingController::class, 'getUserSetting']);
+    // 更新系统默认设置
+    Route::post('update_default', [MessageSettingController::class, 'updateDefaultSetting']);
+    // 更新用户消息设置
+    Route::post('update_user', [MessageSettingController::class, 'updateUserSetting']);
+})->middleware(['auth']);

+ 69 - 0
route/api.php

@@ -0,0 +1,69 @@
+<?php
+declare(strict_types=1);
+
+use SixDec\Message\Controller\Api\{
+    AnnouncementController,
+    NotificationController,
+    PrivateMessageController,
+    MessageSettingController
+};
+use think\facade\Route;
+
+// API路由
+// 路由前缀: /api/message
+
+// 公告相关路由
+Route::group('announcement', function () {
+    // 获取公告列表
+    Route::get('list', [AnnouncementController::class, 'getList']);
+    // 获取公告详情
+    Route::get('detail', [AnnouncementController::class, 'getDetail']);
+})->middleware(['auth']);
+
+// 通知相关路由
+Route::group('notification', function () {
+    // 获取通知列表
+    Route::get('list', [NotificationController::class, 'getList']);
+    // 获取通知详情
+    Route::get('detail', [NotificationController::class, 'getDetail']);
+    // 标记通知为已读
+    Route::post('mark_read', [NotificationController::class, 'markAsRead']);
+    // 批量标记通知为已读
+    Route::post('batch_mark_read', [NotificationController::class, 'batchMarkAsRead']);
+    // 标记所有通知为已读
+    Route::post('mark_all_read', [NotificationController::class, 'markAllAsRead']);
+    // 删除通知
+    Route::post('delete', [NotificationController::class, 'delete']);
+    // 批量删除通知
+    Route::post('batch_delete', [NotificationController::class, 'batchDelete']);
+    // 获取未读通知数量
+    Route::get('unread_count', [NotificationController::class, 'getUnreadCount']);
+})->middleware(['auth']);
+
+// 私信相关路由
+Route::group('private_message', function () {
+    // 获取对话列表
+    Route::get('conversation_list', [PrivateMessageController::class, 'getConversationList']);
+    // 获取与指定用户的对话消息
+    Route::get('conversation_messages', [PrivateMessageController::class, 'getConversationMessages']);
+    // 发送私信
+    Route::post('send', [PrivateMessageController::class, 'send']);
+    // 标记私信为已读
+    Route::post('mark_read', [PrivateMessageController::class, 'markAsRead']);
+    // 标记与指定用户的所有私信为已读
+    Route::post('mark_all_read', [PrivateMessageController::class, 'markAllAsRead']);
+    // 删除私信
+    Route::post('delete', [PrivateMessageController::class, 'delete']);
+    // 删除与指定用户的所有对话
+    Route::post('delete_conversation', [PrivateMessageController::class, 'deleteConversation']);
+    // 获取未读私信数量
+    Route::get('unread_count', [PrivateMessageController::class, 'getUnreadCount']);
+})->middleware(['auth']);
+
+// 消息设置相关路由
+Route::group('setting', function () {
+    // 获取用户消息设置
+    Route::get('user', [MessageSettingController::class, 'getUserSetting']);
+    // 更新用户消息设置
+    Route::post('update', [MessageSettingController::class, 'updateSetting']);
+})->middleware(['auth']);

+ 234 - 0
src/Controller/Admin/AnnouncementController.php

@@ -0,0 +1,234 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Controller\Admin;
+
+use think\Request;
+
+/**
+ * 系统公告后台控制器
+ */
+class AnnouncementController extends BaseController
+{
+    /**
+     * 默认入口方法(兼容资源路由)
+     */
+    public function index()
+    {
+        return $this->getList(request());
+    }
+    
+    /**
+     * 获取公告列表
+     */
+    public function getList(Request $request)
+    {
+        $page = $request->param('page/d', 1);
+        $limit = $request->param('limit/d', 10);
+        $title = $request->param('title', '');
+        $status = $request->param('status', '');
+        $startTime = $request->param('start_time', '');
+        $endTime = $request->param('end_time', '');
+        
+        $params = [];
+        if (!empty($title)) {
+            $params['title'] = $title;
+        }
+        if ($status !== '') {
+            $params['status'] = $status;
+        }
+        if (!empty($startTime)) {
+            $params['start_time'] = $startTime;
+        }
+        if (!empty($endTime)) {
+            $params['end_time'] = $endTime;
+        }
+        
+        $result = $this->announcementModel->getList($params, $page, $limit);
+        
+        return $this->success('获取成功', $result);
+    }
+    
+    /**
+     * 获取公告详情
+     */
+    public function getDetail(Request $request)
+    {
+        $id = $request->param('id/d', 0);
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $detail = $this->announcementModel->getDetail($id);
+        if (!$detail) {
+            return $this->error('公告不存在');
+        }
+        
+        return $this->success('获取成功', $detail);
+    }
+    
+    /**
+     * 添加公告
+     */
+    public function save(Request $request)
+    {
+        $title = $request->param('title', '');
+        $content = $request->param('content', '');
+        $type = $request->param('type/d', 1);
+        $status = $request->param('status/d', 1);
+        $startTime = $request->param('start_time', '');
+        $endTime = $request->param('end_time', '');
+        $sort = $request->param('sort/d', 100);
+        
+        if (empty($title)) {
+            return $this->error('标题不能为空');
+        }
+        
+        if (empty($content)) {
+            return $this->error('内容不能为空');
+        }
+        
+        $data = [
+            'title' => $title,
+            'content' => $content,
+            'type' => $type,
+            'status' => $status,
+            'sort' => $sort,
+        ];
+        
+        if (!empty($startTime)) {
+            $data['start_time'] = strtotime($startTime);
+        }
+        
+        if (!empty($endTime)) {
+            $data['end_time'] = strtotime($endTime);
+        }
+        
+        $result = $this->announcementModel->add($data);
+        if (!$result) {
+            return $this->error('添加失败');
+        }
+        
+        return $this->success('添加成功', ['id' => $result]);
+    }
+    
+    /**
+     * 更新公告
+     */
+    public function update(Request $request)
+    {
+        $id = $request->param('id/d', 0);
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $title = $request->param('title', '');
+        $content = $request->param('content', '');
+        $type = $request->param('type/d', null);
+        $status = $request->param('status/d', null);
+        $startTime = $request->param('start_time', '');
+        $endTime = $request->param('end_time', '');
+        $sort = $request->param('sort/d', null);
+        
+        $data = [];
+        
+        if (!empty($title)) {
+            $data['title'] = $title;
+        }
+        
+        if (!empty($content)) {
+            $data['content'] = $content;
+        }
+        
+        if ($type !== null) {
+            $data['type'] = $type;
+        }
+        
+        if ($status !== null) {
+            $data['status'] = $status;
+        }
+        
+        if ($sort !== null) {
+            $data['sort'] = $sort;
+        }
+        
+        if (!empty($startTime)) {
+            $data['start_time'] = strtotime($startTime);
+        }
+        
+        if (!empty($endTime)) {
+            $data['end_time'] = strtotime($endTime);
+        }
+        
+        if (empty($data)) {
+            return $this->error('没有需要更新的数据');
+        }
+        
+        $result = $this->announcementModel->update($id, $data);
+        if (!$result) {
+            return $this->error('更新失败');
+        }
+        
+        return $this->success('更新成功');
+    }
+    
+    /**
+     * 删除公告
+     */
+    public function delete(Request $request)
+    {
+        $id = $request->param('id/d', 0);
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $result = $this->announcementModel->delete($id);
+        if (!$result) {
+            return $this->error('删除失败');
+        }
+        
+        return $this->success('删除成功');
+    }
+    
+    /**
+     * 批量删除公告
+     */
+    public function batchDelete(Request $request)
+    {
+        $ids = $request->param('ids');
+        if (empty($ids)) {
+            return $this->error('参数错误');
+        }
+        
+        if (!is_array($ids)) {
+            $ids = explode(',', $ids);
+        }
+        
+        $result = $this->announcementModel->batchDelete($ids);
+        if (!$result) {
+            return $this->error('删除失败');
+        }
+        
+        return $this->success('删除成功');
+    }
+    
+    /**
+     * 更新公告状态
+     */
+    public function updateStatus(Request $request)
+    {
+        $id = $request->param('id/d', 0);
+        $status = $request->param('status/d', 0);
+        
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $result = $this->announcementModel->update($id, ['status' => $status]);
+        if (!$result) {
+            return $this->error('操作失败');
+        }
+        
+        return $this->success('操作成功');
+    }
+}

+ 56 - 0
src/Controller/Admin/BaseController.php

@@ -0,0 +1,56 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Controller\Admin;
+
+use app\admin\controller\Base;
+use SixDec\Message\Model\AnnouncementModel;
+use SixDec\Message\Model\NotificationModel;
+use SixDec\Message\Model\PrivateMessageModel;
+use SixDec\Message\Model\MessageSettingModel;
+use SixDec\Message\Model\MessageTemplateModel;
+
+/**
+ * 消息模块后台控制器基类
+ */
+class BaseController extends Base
+{
+    /**
+     * @var AnnouncementModel
+     */
+    protected $announcementModel;
+    
+    /**
+     * @var NotificationModel
+     */
+    protected $notificationModel;
+    
+    /**
+     * @var PrivateMessageModel
+     */
+    protected $privateMessageModel;
+    
+    /**
+     * @var MessageSettingModel
+     */
+    protected $messageSettingModel;
+    
+    /**
+     * @var MessageTemplateModel
+     */
+    protected $messageTemplateModel;
+    
+    /**
+     * 初始化
+     */
+    public function initialize()
+    {
+        parent::initialize();
+        
+        $this->announcementModel = new AnnouncementModel();
+        $this->notificationModel = new NotificationModel();
+        $this->privateMessageModel = new PrivateMessageModel();
+        $this->messageSettingModel = new MessageSettingModel();
+        $this->messageTemplateModel = new MessageTemplateModel();
+    }
+}

+ 127 - 0
src/Controller/Admin/MessageSettingController.php

@@ -0,0 +1,127 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Controller\Admin;
+
+use think\Request;
+
+/**
+ * 消息设置后台控制器
+ */
+class MessageSettingController extends BaseController
+{
+    /**
+     * 获取消息设置列表
+     */
+    public function getList(Request $request)
+    {
+        $page = $request->param('page/d', 1);
+        $limit = $request->param('limit/d', 10);
+        $userId = $request->param('user_id/d', null);
+        
+        $params = [];
+        if ($userId !== null) {
+            $params['user_id'] = $userId;
+        }
+        
+        $result = $this->messageSettingModel->getList($params, $page, $limit);
+        
+        return $this->success('获取成功', $result);
+    }
+    
+    /**
+     * 获取系统默认设置
+     */
+    public function getDefaultSetting(Request $request)
+    {
+        $setting = $this->messageSettingModel->getDefaultSetting();
+        if (!$setting) {
+            return $this->error('获取设置失败');
+        }
+        
+        return $this->success('获取成功', $setting);
+    }
+    
+    /**
+     * 获取用户消息设置
+     */
+    public function getUserSetting(Request $request)
+    {
+        $userId = $request->param('user_id/d', 0);
+        if ($userId <= 0) {
+            return $this->error('用户ID不能为空');
+        }
+        
+        $setting = $this->messageSettingModel->getUserSetting($userId);
+        if (!$setting) {
+            return $this->error('获取设置失败');
+        }
+        
+        return $this->success('获取成功', $setting);
+    }
+    
+    /**
+     * 更新系统默认设置
+     */
+    public function updateDefaultSetting(Request $request)
+    {
+        $data = [
+            'system_notify' => $request->param('system_notify/d', null),
+            'activity_notify' => $request->param('activity_notify/d', null),
+            'order_notify' => $request->param('order_notify/d', null),
+            'logistics_notify' => $request->param('logistics_notify/d', null),
+            'private_message' => $request->param('private_message/d', null),
+        ];
+        
+        // 过滤空值
+        $data = array_filter($data, function($value) {
+            return $value !== null;
+        });
+        
+        if (empty($data)) {
+            return $this->error('参数错误');
+        }
+        
+        $result = $this->messageSettingModel->updateDefaultSetting($data);
+        if (!$result) {
+            return $this->error('更新失败');
+        }
+        
+        return $this->success('更新成功');
+    }
+    
+    /**
+     * 更新用户消息设置
+     */
+    public function updateUserSetting(Request $request)
+    {
+        $userId = $request->param('user_id/d', 0);
+        if ($userId <= 0) {
+            return $this->error('用户ID不能为空');
+        }
+        
+        $data = [
+            'system_notify' => $request->param('system_notify/d', null),
+            'activity_notify' => $request->param('activity_notify/d', null),
+            'order_notify' => $request->param('order_notify/d', null),
+            'logistics_notify' => $request->param('logistics_notify/d', null),
+            'private_message' => $request->param('private_message/d', null),
+        ];
+        
+        // 过滤空值
+        $data = array_filter($data, function($value) {
+            return $value !== null;
+        });
+        
+        if (empty($data)) {
+            return $this->error('参数错误');
+        }
+        
+        $result = $this->messageSettingModel->updateSetting($userId, $data);
+        if (!$result) {
+            return $this->error('更新失败');
+        }
+        
+        return $this->success('更新成功');
+    }
+}

+ 243 - 0
src/Controller/Admin/MessageTemplateController.php

@@ -0,0 +1,243 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Controller\Admin;
+
+use think\Request;
+
+/**
+ * 消息模板后台控制器
+ */
+class MessageTemplateController extends BaseController
+{
+    /**
+     * 默认入口方法(兼容资源路由)
+     */
+    public function index()
+    {
+        return $this->getList(request());
+    }
+    
+    /**
+     * 获取模板列表
+     */
+    public function getList(Request $request)
+    {
+        $page = $request->param('page/d', 1);
+        $limit = $request->param('limit/d', 10);
+        $code = $request->param('code', '');
+        $name = $request->param('name', '');
+        $type = $request->param('type/d', 0);
+        $status = $request->param('status', '');
+        
+        $params = [];
+        if (!empty($code)) {
+            $params['code'] = $code;
+        }
+        if (!empty($name)) {
+            $params['name'] = $name;
+        }
+        if ($type > 0) {
+            $params['type'] = $type;
+        }
+        if ($status !== '') {
+            $params['status'] = $status;
+        }
+        
+        $result = $this->messageTemplateModel->getList($params, $page, $limit);
+        
+        return $this->success('获取成功', $result);
+    }
+    
+    /**
+     * 获取模板详情
+     */
+    public function getDetail(Request $request)
+    {
+        $id = $request->param('id/d', 0);
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $detail = $this->messageTemplateModel->getDetail($id);
+        if (!$detail) {
+            return $this->error('模板不存在');
+        }
+        
+        return $this->success('获取成功', $detail);
+    }
+    
+    /**
+     * 添加模板
+     */
+    public function save(Request $request)
+    {
+        $code = $request->param('code', '');
+        $name = $request->param('name', '');
+        $type = $request->param('type/d', 1);
+        $title = $request->param('title', '');
+        $content = $request->param('content', '');
+        $status = $request->param('status/d', 1);
+        $remark = $request->param('remark', '');
+        
+        if (empty($code)) {
+            return $this->error('模板代码不能为空');
+        }
+        
+        if (empty($name)) {
+            return $this->error('模板名称不能为空');
+        }
+        
+        if (empty($title)) {
+            return $this->error('标题模板不能为空');
+        }
+        
+        if (empty($content)) {
+            return $this->error('内容模板不能为空');
+        }
+        
+        $data = [
+            'code' => $code,
+            'name' => $name,
+            'type' => $type,
+            'title' => $title,
+            'content' => $content,
+            'status' => $status,
+            'remark' => $remark,
+        ];
+        
+        $result = $this->messageTemplateModel->add($data);
+        if (!$result) {
+            return $this->error('添加失败,模板代码可能已存在');
+        }
+       
+        return $this->success('添加成功', ['id' => $result]);
+    }
+    
+    /**
+     * 更新模板
+     */
+    public function update(Request $request, $id = 0)
+    {
+        // 确保ID是整数类型
+        $id = (int)$id;
+        
+        // 如果路径参数中的ID无效,尝试从请求参数中获取
+        if ($id <= 0) {
+            $id = $request->param('id/d', 0);
+        }
+        
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $code = $request->param('code', '');
+        $name = $request->param('name', '');
+        $type = $request->param('type/d', null);
+        $title = $request->param('title', '');
+        $content = $request->param('content', '');
+        $status = $request->param('status/d', null);
+        $remark = $request->param('remark', '');
+        
+        $data = [];
+        
+        if (!empty($code)) {
+            $data['code'] = $code;
+        }
+        
+        if (!empty($name)) {
+            $data['name'] = $name;
+        }
+        
+        if ($type !== null) {
+            $data['type'] = $type;
+        }
+        
+        if (!empty($title)) {
+            $data['title'] = $title;
+        }
+        
+        if (!empty($content)) {
+            $data['content'] = $content;
+        }
+        
+        if ($status !== null) {
+            $data['status'] = $status;
+        }
+        
+        if ($remark !== null) {
+            $data['remark'] = $remark;
+        }
+        
+        if (empty($data)) {
+            return $this->error('没有需要更新的数据');
+        }
+        
+        $result = $this->messageTemplateModel->update($id, $data);
+        if (!$result) {
+            return $this->error('更新失败,模板代码可能已存在');
+        }
+        
+        return $this->success('更新成功');
+    }
+    
+    /**
+     * 删除模板
+     */
+    public function delete(Request $request)
+    {
+        $id = $request->param('id/d', 0);
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $result = $this->messageTemplateModel->delete($id);
+        if (!$result) {
+            return $this->error('删除失败');
+        }
+        
+        return $this->success('删除成功');
+    }
+    
+    /**
+     * 批量删除模板
+     */
+    public function batchDelete(Request $request)
+    {
+        $ids = $request->param('ids');
+        if (empty($ids)) {
+            return $this->error('参数错误');
+        }
+        
+        if (!is_array($ids)) {
+            $ids = explode(',', $ids);
+        }
+        
+        $result = $this->messageTemplateModel->batchDelete($ids);
+        if (!$result) {
+            return $this->error('删除失败');
+        }
+        
+        return $this->success('删除成功');
+    }
+    
+    /**
+     * 更新模板状态
+     */
+    public function updateStatus(Request $request)
+    {
+        $id = $request->param('id/d', 0);
+        $status = $request->param('status/d', 0);
+        
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $result = $this->messageTemplateModel->update($id, ['status' => $status]);
+        if (!$result) {
+            return $this->error('操作失败');
+        }
+        
+        return $this->success('操作成功');
+    }
+}

+ 230 - 0
src/Controller/Admin/NotificationController.php

@@ -0,0 +1,230 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Controller\Admin;
+
+use think\Request;
+
+/**
+ * 用户通知后台控制器
+ */
+class NotificationController extends BaseController
+{
+    /**
+     * 默认入口方法(兼容资源路由)
+     */
+    public function index()
+    {
+        return $this->getList(request());
+    }
+    
+    /**
+     * 获取通知列表
+     */
+    public function getList(Request $request)
+    {
+        $page = $request->param('page/d', 1);
+        $limit = $request->param('limit/d', 10);
+        $userId = $request->param('user_id/d', 0);
+        $type = $request->param('type/d', 0);
+        $isRead = $request->param('is_read', '');
+        $title = $request->param('title', '');
+        $startTime = $request->param('start_time', '');
+        $endTime = $request->param('end_time', '');
+        
+        $params = [];
+        if ($userId > 0) {
+            $params['user_id'] = $userId;
+        }
+        if ($type > 0) {
+            $params['type'] = $type;
+        }
+        if ($isRead !== '') {
+            $params['is_read'] = $isRead;
+        }
+        if (!empty($title)) {
+            $params['title'] = $title;
+        }
+        if (!empty($startTime)) {
+            $params['start_time'] = $startTime;
+        }
+        if (!empty($endTime)) {
+            $params['end_time'] = $endTime;
+        }
+        
+        $result = $this->notificationModel->getList($params, $page, $limit);
+        
+        return $this->success('获取成功', $result);
+    }
+    
+    /**
+     * 获取通知详情
+     */
+    public function getDetail(Request $request)
+    {
+        $id = $request->param('id/d', 0);
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $detail = $this->notificationModel->getDetail($id);
+        if (!$detail) {
+            return $this->error('通知不存在');
+        }
+        
+        return $this->success('获取成功', $detail);
+    }
+    
+    /**
+     * 发送通知给单个用户
+     */
+    public function sendToUser(Request $request)
+    {
+        $userId = $request->param('user_id/d', 0);
+        $title = $request->param('title', '');
+        $content = $request->param('content', '');
+        $type = $request->param('type/d', 1);
+        $url = $request->param('url', '');
+        
+        if ($userId <= 0) {
+            return $this->error('用户ID不能为空');
+        }
+        
+        if (empty($title)) {
+            return $this->error('标题不能为空');
+        }
+        
+        if (empty($content)) {
+            return $this->error('内容不能为空');
+        }
+        
+        $data = [
+            'title' => $title,
+            'content' => $content,
+            'type' => $type,
+            'url' => $url,
+        ];
+        
+        $result = $this->notificationModel->sendToUser($userId, $data);
+        if (!$result) {
+            return $this->error('发送失败');
+        }
+        
+        return $this->success('发送成功', ['id' => $result]);
+    }
+    
+    /**
+     * 发送通知给多个用户
+     */
+    public function sendToUsers(Request $request)
+    {
+        $userIds = $request->param('user_ids');
+        $title = $request->param('title', '');
+        $content = $request->param('content', '');
+        $type = $request->param('type/d', 1);
+        $url = $request->param('url', '');
+        
+        if (empty($userIds)) {
+            return $this->error('用户ID不能为空');
+        }
+        
+        if (!is_array($userIds)) {
+            $userIds = explode(',', $userIds);
+        }
+        
+        if (empty($title)) {
+            return $this->error('标题不能为空');
+        }
+        
+        if (empty($content)) {
+            return $this->error('内容不能为空');
+        }
+        
+        $data = [
+            'title' => $title,
+            'content' => $content,
+            'type' => $type,
+            'url' => $url,
+        ];
+        
+        $result = $this->notificationModel->sendToUsers($userIds, $data);
+        if (!$result) {
+            return $this->error('发送失败');
+        }
+        
+        return $this->success('发送成功', ['count' => count($result)]);
+    }
+    
+    /**
+     * 发送全局通知
+     */
+    public function sendGlobal(Request $request)
+    {
+        $title = $request->param('title', '');
+        $content = $request->param('content', '');
+        $type = $request->param('type/d', 1);
+        $url = $request->param('url', '');
+        
+        if (empty($title)) {
+            return $this->error('标题不能为空');
+        }
+        
+        if (empty($content)) {
+            return $this->error('内容不能为空');
+        }
+        
+        $data = [
+            'title' => $title,
+            'content' => $content,
+            'type' => $type,
+            'url' => $url,
+        ];
+        
+        $result = $this->notificationModel->sendGlobal($data);
+        if (!$result) {
+            return $this->error('发送失败');
+        }
+        
+        return $this->success('发送成功', ['id' => $result]);
+    }
+    
+    /**
+     * 删除通知
+     */
+    public function delete(Request $request)
+    {
+        $id = $request->param('id/d', 0);
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $result = $this->notificationModel->adminDelete($id);
+        if (!$result) {
+            return $this->error('删除失败');
+        }
+        
+        return $this->success('删除成功');
+    }
+    
+    /**
+     * 批量删除通知
+     */
+    public function batchDelete(Request $request)
+    {
+        $ids = $request->param('ids');
+        if (empty($ids)) {
+            return $this->error('参数错误');
+        }
+        
+        if (!is_array($ids)) {
+            $ids = explode(',', $ids);
+        }
+        
+        $result = $this->notificationModel->adminBatchDelete($ids);
+        if (!$result) {
+            return $this->error('删除失败');
+        }
+        
+        return $this->success('删除成功');
+    }
+}

+ 179 - 0
src/Controller/Admin/PrivateMessageController.php

@@ -0,0 +1,179 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Controller\Admin;
+
+use think\Request;
+
+/**
+ * 私信后台控制器
+ */
+class PrivateMessageController extends BaseController
+{
+    /**
+     * 默认入口方法(兼容资源路由)
+     */
+    public function index()
+    {
+        return $this->getList(request());
+    }
+    
+    /**
+     * 获取私信列表
+     */
+    public function getList(Request $request)
+    {
+        $page = $request->param('page/d', 1);
+        $limit = $request->param('limit/d', 10);
+        $fromUserId = $request->param('from_user_id/d', 0);
+        $toUserId = $request->param('to_user_id/d', 0);
+        $isRead = $request->param('is_read', '');
+        $content = $request->param('content', '');
+        $startTime = $request->param('start_time', '');
+        $endTime = $request->param('end_time', '');
+        
+        $params = [];
+        if ($fromUserId > 0) {
+            $params['from_user_id'] = $fromUserId;
+        }
+        if ($toUserId > 0) {
+            $params['to_user_id'] = $toUserId;
+        }
+        if ($isRead !== '') {
+            $params['is_read'] = $isRead;
+        }
+        if (!empty($content)) {
+            $params['content'] = $content;
+        }
+        if (!empty($startTime)) {
+            $params['start_time'] = $startTime;
+        }
+        if (!empty($endTime)) {
+            $params['end_time'] = $endTime;
+        }
+        
+        $result = $this->privateMessageModel->getList($params, $page, $limit);
+        
+        return $this->success('获取成功', $result);
+    }
+    
+    /**
+     * 获取私信详情
+     */
+    public function getDetail(Request $request)
+    {
+        $id = $request->param('id/d', 0);
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $detail = $this->privateMessageModel->getDetail($id);
+        if (!$detail) {
+            return $this->error('私信不存在');
+        }
+        
+        return $this->success('获取成功', $detail);
+    }
+    
+    /**
+     * 发送系统私信给用户
+     */
+    public function sendSystemMessage(Request $request)
+    {
+        $toUserId = $request->param('to_user_id/d', 0);
+        $content = $request->param('content', '');
+        
+        if ($toUserId <= 0) {
+            return $this->error('接收者ID不能为空');
+        }
+        
+        if (empty($content)) {
+            return $this->error('内容不能为空');
+        }
+        
+        $result = $this->privateMessageModel->sendSystemMessage($toUserId, $content);
+        if (!$result) {
+            return $this->error('发送失败');
+        }
+        
+        return $this->success('发送成功', ['id' => $result]);
+    }
+    
+    /**
+     * 批量发送系统私信
+     */
+    public function batchSendSystemMessage(Request $request)
+    {
+        $userIds = $request->param('user_ids');
+        $content = $request->param('content', '');
+        
+        if (empty($userIds)) {
+            return $this->error('接收者ID不能为空');
+        }
+        
+        if (!is_array($userIds)) {
+            $userIds = explode(',', $userIds);
+        }
+        
+        if (empty($content)) {
+            return $this->error('内容不能为空');
+        }
+        
+        $successCount = 0;
+        foreach ($userIds as $userId) {
+            $result = $this->privateMessageModel->sendSystemMessage((int)$userId, $content);
+            if ($result) {
+                $successCount++;
+            }
+        }
+        
+        return $this->success('发送成功', ['success_count' => $successCount, 'total_count' => count($userIds)]);
+    }
+    
+    /**
+     * 删除私信
+     */
+    public function delete(Request $request)
+    {
+        $id = $request->param('id/d', 0);
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        // 管理员可以删除任何私信,不需要验证用户ID
+        $entity = $this->privateMessageModel->getDetail($id);
+        if (!$entity) {
+            return $this->error('私信不存在');
+        }
+        
+        $result = $entity->delete();
+        if (!$result) {
+            return $this->error('删除失败');
+        }
+        
+        return $this->success('删除成功');
+    }
+    
+    /**
+     * 批量删除私信
+     */
+    public function batchDelete(Request $request)
+    {
+        $ids = $request->param('ids');
+        if (empty($ids)) {
+            return $this->error('参数错误');
+        }
+        
+        if (!is_array($ids)) {
+            $ids = explode(',', $ids);
+        }
+        
+        // 管理员可以批量删除私信,不需要验证用户ID
+        $result = $this->privateMessageModel->batchDelete($ids, 0);
+        if (!$result) {
+            return $this->error('删除失败');
+        }
+        
+        return $this->success('删除成功');
+    }
+}

+ 49 - 0
src/Controller/Api/AnnouncementController.php

@@ -0,0 +1,49 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Controller\Api;
+
+use think\Request;
+
+/**
+ * 系统公告API控制器
+ */
+class AnnouncementController extends BaseController
+{
+    /**
+     * 获取公告列表
+     */
+    public function getList(Request $request)
+    {
+        $page = $request->param('page/d', 1);
+        $limit = $request->param('limit/d', 10);
+        
+        // 只获取有效的公告
+        $list = $this->announcementModel->getValidList($limit);
+        
+        return $this->success('获取成功', $list);
+    }
+    
+    /**
+     * 获取公告详情
+     */
+    public function getDetail(Request $request)
+    {
+        $id = $request->param('id/d', 0);
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $detail = $this->announcementModel->getDetail($id);
+        if (!$detail) {
+            return $this->error('公告不存在');
+        }
+        
+        // 检查公告是否有效
+        if (!$detail->isValid()) {
+            return $this->error('公告已过期或未启用');
+        }
+        
+        return $this->success('获取成功', $detail);
+    }
+}

+ 67 - 0
src/Controller/Api/BaseController.php

@@ -0,0 +1,67 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Controller\Api;
+
+use app\api\controller\Base;
+use SixDec\Message\Model\AnnouncementModel;
+use SixDec\Message\Model\NotificationModel;
+use SixDec\Message\Model\PrivateMessageModel;
+use SixDec\Message\Model\MessageSettingModel;
+use SixDec\Message\Model\MessageTemplateModel;
+
+/**
+ * 消息模块API控制器基类
+ */
+class BaseController extends Base
+{
+    /**
+     * @var AnnouncementModel
+     */
+    protected $announcementModel;
+    
+    /**
+     * @var NotificationModel
+     */
+    protected $notificationModel;
+    
+    /**
+     * @var PrivateMessageModel
+     */
+    protected $privateMessageModel;
+    
+    /**
+     * @var MessageSettingModel
+     */
+    protected $messageSettingModel;
+    
+    /**
+     * @var MessageTemplateModel
+     */
+    protected $messageTemplateModel;
+    
+    /**
+     * 初始化
+     */
+    public function initialize()
+    {
+        parent::initialize();
+        
+        $this->announcementModel = new AnnouncementModel();
+        $this->notificationModel = new NotificationModel();
+        $this->privateMessageModel = new PrivateMessageModel();
+        $this->messageSettingModel = new MessageSettingModel();
+        $this->messageTemplateModel = new MessageTemplateModel();
+    }
+    
+    /**
+     * 获取当前用户ID
+     *
+     * @param bool $required 是否必需用户ID
+     * @return int
+     */
+    public function getUserId($required = true)
+    {
+        return request()->userID ?: 0;
+    }
+}

+ 65 - 0
src/Controller/Api/MessageSettingController.php

@@ -0,0 +1,65 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Controller\Api;
+
+use think\Request;
+
+/**
+ * 消息设置API控制器
+ */
+class MessageSettingController extends BaseController
+{
+    /**
+     * 获取用户消息设置
+     */
+    public function getUserSetting(Request $request)
+    {
+        $userId = $this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $setting = $this->messageSettingModel->getUserSetting($userId);
+        if (!$setting) {
+            return $this->error('获取设置失败');
+        }
+        
+        return $this->success('获取成功', $setting);
+    }
+    
+    /**
+     * 更新用户消息设置
+     */
+    public function updateSetting(Request $request)
+    {
+        $userId = $this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $data = [
+            'system_notify' => $request->param('system_notify/d', null),
+            'activity_notify' => $request->param('activity_notify/d', null),
+            'order_notify' => $request->param('order_notify/d', null),
+            'logistics_notify' => $request->param('logistics_notify/d', null),
+            'private_message' => $request->param('private_message/d', null),
+        ];
+        
+        // 过滤空值
+        $data = array_filter($data, function($value) {
+            return $value !== null;
+        });
+        
+        if (empty($data)) {
+            return $this->error('参数错误');
+        }
+        
+        $result = $this->messageSettingModel->updateSetting($userId, $data);
+        if (!$result) {
+            return $this->error('更新失败');
+        }
+        
+        return $this->success('更新成功');
+    }
+}

+ 204 - 0
src/Controller/Api/NotificationController.php

@@ -0,0 +1,204 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Controller\Api;
+
+use think\Request;
+
+/**
+ * 用户通知API控制器
+ */
+class NotificationController extends BaseController
+{
+    /**
+     * 获取用户通知列表
+     */
+    public function getList(Request $request)
+    {
+        $userId = (int)$this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $page = $request->param('page/d', 1);
+        $limit = $request->param('limit/d', 10);
+        $type = $request->param('type/d', 0);
+        $isRead = $request->param('is_read/d', null);
+        
+        $params = [];
+        if ($type > 0) {
+            $params['type'] = $type;
+        }
+        if ($isRead !== null) {
+            $params['is_read'] = $isRead;
+        }
+        
+        $result = $this->notificationModel->getUserNotifications($userId, $params, $page, $limit);
+        
+        return $this->success('获取成功', $result);
+    }
+    
+    /**
+     * 获取通知详情
+     */
+    public function getDetail(Request $request)
+    {
+        $userId = (int)$this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $id = $request->param('id/d', 0);
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $detail = $this->notificationModel->getDetail($id);
+        if (!$detail) {
+            return $this->error('通知不存在');
+        }
+        
+        // 检查权限
+        if ($detail->user_id > 0 && $detail->user_id != $userId) {
+            return $this->error('无权限查看');
+        }
+        
+        // 标记为已读
+        $this->notificationModel->markAsRead($id, $userId);
+        
+        return $this->success('获取成功', $detail);
+    }
+    
+    /**
+     * 标记通知为已读
+     */
+    public function markAsRead(Request $request)
+    {
+        $userId = (int)$this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $id = $request->param('id/d', 0);
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $result = $this->notificationModel->markAsRead($id, $userId);
+        if (!$result) {
+            return $this->error('操作失败');
+        }
+        
+        return $this->success('操作成功');
+    }
+    
+    /**
+     * 批量标记通知为已读
+     */
+    public function batchMarkAsRead(Request $request)
+    {
+        $userId = (int)$this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $ids = $request->param('ids');
+        if (empty($ids)) {
+            return $this->error('参数错误');
+        }
+        
+        if (!is_array($ids)) {
+            $ids = explode(',', $ids);
+        }
+        
+        $result = $this->notificationModel->batchMarkAsRead($ids, $userId);
+        if (!$result) {
+            return $this->error('操作失败');
+        }
+        
+        return $this->success('操作成功');
+    }
+    
+    /**
+     * 标记所有通知为已读
+     */
+    public function markAllAsRead(Request $request)
+    {
+        $userId = (int)$this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $result = $this->notificationModel->markAllAsRead($userId);
+        if (!$result) {
+            return $this->error('操作失败');
+        }
+        
+        return $this->success('操作成功');
+    }
+    
+    /**
+     * 删除通知
+     */
+    public function delete(Request $request)
+    {
+        $userId = (int)$this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $id = $request->param('id/d', 0);
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $result = $this->notificationModel->delete($id, $userId);
+        if (!$result) {
+            return $this->error('操作失败');
+        }
+        
+        return $this->success('删除成功');
+    }
+    
+    /**
+     * 批量删除通知
+     */
+    public function batchDelete(Request $request)
+    {
+        $userId = (int)$this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $ids = $request->param('ids');
+        if (empty($ids)) {
+            return $this->error('参数错误');
+        }
+        
+        if (!is_array($ids)) {
+            $ids = explode(',', $ids);
+        }
+        
+        $result = $this->notificationModel->batchDelete($ids, $userId);
+        if (!$result) {
+            return $this->error('操作失败');
+        }
+        
+        return $this->success('删除成功');
+    }
+    
+    /**
+     * 获取未读通知数量
+     */
+    public function getUnreadCount(Request $request)
+    {
+        $userId = (int)$this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $count = $this->notificationModel->getUnreadCount($userId);
+        
+        return $this->success('获取成功', ['count' => $count]);
+    }
+}

+ 194 - 0
src/Controller/Api/PrivateMessageController.php

@@ -0,0 +1,194 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Controller\Api;
+
+use think\Request;
+
+/**
+ * 私信API控制器
+ */
+class PrivateMessageController extends BaseController
+{
+    /**
+     * 获取对话列表
+     */
+    public function getConversationList(Request $request)
+    {
+        $userId = $this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $page = $request->param('page/d', 1);
+        $limit = $request->param('limit/d', 10);
+        
+        $result = $this->privateMessageModel->getConversationList($userId, $page, $limit);
+        
+        return $this->success('获取成功', $result);
+    }
+    
+    /**
+     * 获取与指定用户的对话消息
+     */
+    public function getConversationMessages(Request $request)
+    {
+        $userId = $this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $partnerId = $request->param('partner_id/d', 0);
+        if ($partnerId < 0) {
+            return $this->error('参数错误');
+        }
+        
+        $page = $request->param('page/d', 1);
+        $limit = $request->param('limit/d', 20);
+        
+        $result = $this->privateMessageModel->getConversationMessages($userId, $partnerId, $page, $limit);
+        
+        return $this->success('获取成功', $result);
+    }
+    
+    /**
+     * 发送私信
+     */
+    public function send(Request $request)
+    {
+        $userId = $this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $toUserId = $request->param('to_user_id/d', 0);
+        $content = $request->param('content', '');
+        
+        if ($toUserId <= 0) {
+            return $this->error('接收者不能为空');
+        }
+        
+        if (empty($content)) {
+            return $this->error('消息内容不能为空');
+        }
+        
+        // 不能给自己发私信
+        if ($toUserId == $userId) {
+            return $this->error('不能给自己发送私信');
+        }
+        
+        $result = $this->privateMessageModel->send($userId, $toUserId, $content);
+        if (!$result) {
+            return $this->error('发送失败,对方可能已关闭私信功能');
+        }
+        
+        return $this->success('发送成功', ['id' => $result]);
+    }
+    
+    /**
+     * 标记私信为已读
+     */
+    public function markAsRead(Request $request)
+    {
+        $userId = $this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $id = $request->param('id/d', 0);
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $result = $this->privateMessageModel->markAsRead($id, $userId);
+        if (!$result) {
+            return $this->error('操作失败');
+        }
+        
+        return $this->success('操作成功');
+    }
+    
+    /**
+     * 标记与指定用户的所有私信为已读
+     */
+    public function markAllAsRead(Request $request)
+    {
+        $userId = $this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $partnerId = $request->param('partner_id/d', 0);
+        if ($partnerId < 0) {
+            return $this->error('参数错误');
+        }
+        
+        $result = $this->privateMessageModel->markAllAsRead($userId, $partnerId);
+        if (!$result) {
+            return $this->error('操作失败');
+        }
+        
+        return $this->success('操作成功');
+    }
+    
+    /**
+     * 删除私信
+     */
+    public function delete(Request $request)
+    {
+        $userId = $this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $id = $request->param('id/d', 0);
+        if ($id <= 0) {
+            return $this->error('参数错误');
+        }
+        
+        $result = $this->privateMessageModel->delete($id, $userId);
+        if (!$result) {
+            return $this->error('操作失败');
+        }
+        
+        return $this->success('删除成功');
+    }
+    
+    /**
+     * 删除与指定用户的所有对话
+     */
+    public function deleteConversation(Request $request)
+    {
+        $userId = $this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $partnerId = $request->param('partner_id/d', 0);
+        if ($partnerId < 0) {
+            return $this->error('参数错误');
+        }
+        
+        $result = $this->privateMessageModel->deleteConversation($userId, $partnerId);
+        if (!$result) {
+            return $this->error('操作失败');
+        }
+        
+        return $this->success('删除成功');
+    }
+    
+    /**
+     * 获取未读私信数量
+     */
+    public function getUnreadCount(Request $request)
+    {
+        $userId = $this->getUserId();
+        if (!$userId) {
+            return $this->error('请先登录');
+        }
+        
+        $count = $this->privateMessageModel->getUnreadCount($userId);
+        
+        return $this->success('获取成功', ['count' => $count]);
+    }
+}

+ 75 - 0
src/Entity/AnnouncementEntity.php

@@ -0,0 +1,75 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Entity;
+
+use think\Model;
+
+/**
+ * 系统公告实体类
+ */
+class AnnouncementEntity extends Model
+{
+    // 设置表名
+    protected $name = 'message_announcements';
+    
+    // 设置字段信息
+    protected $schema = [
+        'id'          => 'int',
+        'title'       => 'string',
+        'content'     => 'string',
+        'status'      => 'int',
+        'type'        => 'int',
+        'start_time'  => 'int',
+        'end_time'    => 'int',
+        'create_time' => 'int',
+        'update_time' => 'int',
+        'delete_time' => 'int',
+    ];
+    
+    // 自动时间戳
+    protected $autoWriteTimestamp = true;
+    
+    // 软删除
+    protected $deleteTime = 'delete_time';
+    
+    /**
+     * 获取公告类型文本
+     */
+    public function getTypeTextAttr()
+    {
+        $types = [
+            1 => '普通公告',
+            2 => '重要公告',
+            3 => '紧急公告',
+        ];
+        
+        return $types[$this->type] ?? '未知类型';
+    }
+    
+    /**
+     * 获取状态文本
+     */
+    public function getStatusTextAttr()
+    {
+        $statuses = [
+            0 => '禁用',
+            1 => '启用',
+        ];
+        
+        return $statuses[$this->status] ?? '未知状态';
+    }
+    
+    /**
+     * 判断公告是否有效
+     */
+    public function isValid()
+    {
+        $now = time();
+        
+        // 检查状态和时间范围
+        return $this->status == 1 
+            && ($this->start_time == 0 || $this->start_time <= $now)
+            && ($this->end_time == 0 || $this->end_time >= $now);
+    }
+}

+ 103 - 0
src/Entity/MessageSettingEntity.php

@@ -0,0 +1,103 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Entity;
+
+use think\Model;
+
+/**
+ * 消息设置实体类
+ */
+class MessageSettingEntity extends Model
+{
+    // 设置表名
+    protected $name = 'message_settings';
+    
+    // 设置字段信息
+    protected $schema = [
+        'id'               => 'int',
+        'user_id'          => 'int',
+        'system_notify'    => 'int',
+        'activity_notify'  => 'int',
+        'order_notify'     => 'int',
+        'logistics_notify' => 'int',
+        'private_message'  => 'int',
+        'create_time'      => 'int',
+        'update_time'      => 'int',
+    ];
+    
+    // 自动时间戳
+    protected $autoWriteTimestamp = true;
+    
+    /**
+     * 关联用户
+     */
+    public function user()
+    {
+        return $this->belongsTo('app\common\model\User', 'user_id');
+    }
+    
+    /**
+     * 获取系统通知状态文本
+     */
+    public function getSystemNotifyTextAttr()
+    {
+        return $this->system_notify ? '开启' : '关闭';
+    }
+    
+    /**
+     * 获取活动通知状态文本
+     */
+    public function getActivityNotifyTextAttr()
+    {
+        return $this->activity_notify ? '开启' : '关闭';
+    }
+    
+    /**
+     * 获取订单通知状态文本
+     */
+    public function getOrderNotifyTextAttr()
+    {
+        return $this->order_notify ? '开启' : '关闭';
+    }
+    
+    /**
+     * 获取物流通知状态文本
+     */
+    public function getLogisticsNotifyTextAttr()
+    {
+        return $this->logistics_notify ? '开启' : '关闭';
+    }
+    
+    /**
+     * 获取私信状态文本
+     */
+    public function getPrivateMessageTextAttr()
+    {
+        return $this->private_message ? '开启' : '关闭';
+    }
+    
+    /**
+     * 检查指定类型的通知是否开启
+     *
+     * @param int $type 通知类型:1-系统通知,2-活动通知,3-订单通知,4-物流通知,5-私信
+     * @return bool
+     */
+    public function isNotifyEnabled($type)
+    {
+        switch ($type) {
+            case 1:
+                return (bool)$this->system_notify;
+            case 2:
+                return (bool)$this->activity_notify;
+            case 3:
+                return (bool)$this->order_notify;
+            case 4:
+                return (bool)$this->logistics_notify;
+            case 5:
+                return (bool)$this->private_message;
+            default:
+                return true;
+        }
+    }
+}

+ 84 - 0
src/Entity/MessageTemplateEntity.php

@@ -0,0 +1,84 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Entity;
+
+use think\Model;
+
+/**
+ * 消息模板实体类
+ */
+class MessageTemplateEntity extends Model
+{
+    // 设置表名
+    protected $name = 'message_templates';
+    
+    // 设置字段信息
+    protected $schema = [
+        'id'          => 'int',
+        'code'        => 'string',
+        'name'        => 'string',
+        'title'       => 'string',
+        'content'     => 'string',
+        'type'        => 'int',
+        'status'      => 'int',
+        'create_time' => 'int',
+        'update_time' => 'int',
+    ];
+    
+    // 自动时间戳
+    protected $autoWriteTimestamp = true;
+    
+    /**
+     * 获取模板类型文本
+     */
+    public function getTypeTextAttr()
+    {
+        $types = [
+            1 => '系统通知',
+            2 => '活动通知',
+            3 => '订单通知',
+            4 => '物流通知',
+            5 => '私信',
+            6 => '其他',
+        ];
+        
+        return $types[$this->type] ?? '未知类型';
+    }
+    
+    /**
+     * 获取状态文本
+     */
+    public function getStatusTextAttr()
+    {
+        $statuses = [
+            0 => '禁用',
+            1 => '启用',
+        ];
+        
+        return $statuses[$this->status] ?? '未知状态';
+    }
+    
+    /**
+     * 解析模板内容
+     *
+     * @param array $data 替换数据
+     * @return array 解析后的标题和内容
+     */
+    public function parse(array $data)
+    {
+        $title = $this->title;
+        $content = $this->content;
+        
+        // 替换变量
+        foreach ($data as $key => $value) {
+            $title = str_replace('{' . $key . '}', $value, $title);
+            $content = str_replace('{' . $key . '}', $value, $content);
+        }
+        
+        return [
+            'title' => $title,
+            'content' => $content,
+        ];
+    }
+}

+ 86 - 0
src/Entity/NotificationEntity.php

@@ -0,0 +1,86 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Entity;
+
+use think\Model;
+
+/**
+ * 用户通知实体类
+ */
+class NotificationEntity extends Model
+{
+    // 设置表名
+    protected $name = 'message_notifications';
+    
+    // 设置字段信息
+    protected $schema = [
+        'id'          => 'int',
+        'user_id'     => 'int',
+        'title'       => 'string',
+        'content'     => 'string',
+        'type'        => 'int',
+        'is_read'     => 'int',
+        'read_time'   => 'int',
+        'create_time' => 'int',
+        'update_time' => 'int',
+        'delete_time' => 'int',
+    ];
+    
+    // 自动时间戳
+    protected $autoWriteTimestamp = true;
+    
+    // 软删除
+    protected $deleteTime = 'delete_time';
+    
+    /**
+     * 获取通知类型文本
+     */
+    public function getTypeTextAttr()
+    {
+        $types = [
+            1 => '系统通知',
+            2 => '活动通知',
+            3 => '订单通知',
+            4 => '物流通知',
+            5 => '其他通知',
+        ];
+        
+        return $types[$this->type] ?? '未知类型';
+    }
+    
+    /**
+     * 获取是否已读文本
+     */
+    public function getIsReadTextAttr()
+    {
+        $statuses = [
+            0 => '未读',
+            1 => '已读',
+        ];
+        
+        return $statuses[$this->is_read] ?? '未知状态';
+    }
+    
+    /**
+     * 标记为已读
+     */
+    public function markAsRead()
+    {
+        if ($this->is_read == 0) {
+            $this->is_read = 1;
+            $this->read_time = time();
+            $this->save();
+        }
+        
+        return $this;
+    }
+    
+    /**
+     * 关联用户
+     */
+    public function user()
+    {
+        return $this->belongsTo('app\common\model\User', 'user_id');
+    }
+}

+ 85 - 0
src/Entity/PrivateMessageEntity.php

@@ -0,0 +1,85 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Entity;
+
+use think\Model;
+
+/**
+ * 私信实体类
+ */
+class PrivateMessageEntity extends Model
+{
+    // 设置表名
+    protected $name = 'message_privates';
+    
+    // 设置字段信息
+    protected $schema = [
+        'id'           => 'int',
+        'from_user_id' => 'int',
+        'to_user_id'   => 'int',
+        'content'      => 'string',
+        'is_read'      => 'int',
+        'read_time'    => 'int',
+        'create_time'  => 'int',
+        'update_time'  => 'int',
+        'delete_time'  => 'int',
+    ];
+    
+    // 自动时间戳
+    protected $autoWriteTimestamp = true;
+    
+    // 软删除
+    protected $deleteTime = 'delete_time';
+    
+    /**
+     * 获取是否已读文本
+     */
+    public function getIsReadTextAttr()
+    {
+        $statuses = [
+            0 => '未读',
+            1 => '已读',
+        ];
+        
+        return $statuses[$this->is_read] ?? '未知状态';
+    }
+    
+    /**
+     * 标记为已读
+     */
+    public function markAsRead()
+    {
+        if ($this->is_read == 0) {
+            $this->is_read = 1;
+            $this->read_time = time();
+            $this->save();
+        }
+        
+        return $this;
+    }
+    
+    /**
+     * 关联发送者用户
+     */
+    public function fromUser()
+    {
+        return $this->belongsTo('app\common\model\User', 'from_user_id');
+    }
+    
+    /**
+     * 关联接收者用户
+     */
+    public function toUser()
+    {
+        return $this->belongsTo('app\common\model\User', 'to_user_id');
+    }
+    
+    /**
+     * 判断是否为系统消息
+     */
+    public function isSystemMessage()
+    {
+        return $this->from_user_id == 0;
+    }
+}

+ 14 - 0
src/Extension.php

@@ -0,0 +1,14 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message;
+
+use SixShop\Core\ExtensionAbstract;
+
+class Extension extends ExtensionAbstract
+{
+    protected function getBaseDir(): string
+    {
+        return dirname(__DIR__);
+    }
+}

+ 163 - 0
src/Model/AnnouncementModel.php

@@ -0,0 +1,163 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Model;
+
+use SixDec\Message\Entity\AnnouncementEntity;
+use think\facade\Db;
+
+/**
+ * 系统公告模型
+ */
+class AnnouncementModel
+{
+    /**
+     * 获取公告列表
+     *
+     * @param array $params 查询参数
+     * @param int $page 页码
+     * @param int $limit 每页数量
+     * @return array
+     */
+    public function getList(array $params = [], int $page = 1, int $limit = 10)
+    {
+        $query = AnnouncementEntity::where('1=1');
+        
+        // 标题搜索
+        if (!empty($params['title'])) {
+            $query->where('title', 'like', '%' . $params['title'] . '%');
+        }
+        
+        // 状态筛选
+        if (isset($params['status']) && $params['status'] !== '') {
+            $query->where('status', $params['status']);
+        }
+        
+        // 类型筛选
+        if (isset($params['type']) && $params['type'] !== '') {
+            $query->where('type', $params['type']);
+        }
+        
+        // 时间范围筛选
+        if (!empty($params['start_time'])) {
+            $query->where('create_time', '>=', strtotime($params['start_time']));
+        }
+        if (!empty($params['end_time'])) {
+            $query->where('create_time', '<=', strtotime($params['end_time']));
+        }
+        
+        // 获取总数
+        $total = $query->count();
+        
+        // 获取分页数据
+        $list = $query->order('id', 'desc')
+            ->page($page, $limit)
+            ->select()
+            ->toArray();
+        
+        return [
+            'list' => $list,
+            'total' => $total,
+            'page' => $page,
+            'limit' => $limit,
+        ];
+    }
+    
+    /**
+     * 获取单个公告详情
+     *
+     * @param int $id 公告ID
+     * @return array|null
+     */
+    public function getDetail(int $id)
+    {
+        return AnnouncementEntity::find($id);
+    }
+    
+    /**
+     * 添加公告
+     *
+     * @param array $data 公告数据
+     * @return int|bool
+     */
+    public function add(array $data)
+    {
+        $entity = new AnnouncementEntity();
+        $result =$entity->save($data);
+        if ($result) {
+            return $entity->id ?: true; // 如果没有id属性,至少返回true表示成功
+        }
+        
+        return false;
+    }
+    
+    /**
+     * 更新公告
+     *
+     * @param int $id 公告ID
+     * @param array $data 更新数据
+     * @return bool
+     */
+    public function update(int $id, array $data)
+    {
+        $entity = AnnouncementEntity::find($id);
+        if (!$entity) {
+            return false;
+        }
+        
+        return $entity->save($data);
+    }
+    
+    /**
+     * 删除公告
+     *
+     * @param int $id 公告ID
+     * @return bool
+     */
+    public function delete(int $id)
+    {
+        $entity = AnnouncementEntity::find($id);
+        if (!$entity) {
+            return false;
+        }
+        
+        return $entity->delete();
+    }
+    
+    /**
+     * 批量删除公告
+     *
+     * @param array $ids 公告ID数组
+     * @return bool
+     */
+    public function batchDelete(array $ids)
+    {
+        return AnnouncementEntity::destroy($ids);
+    }
+    
+    /**
+     * 获取有效的公告列表
+     *
+     * @param int $limit 获取数量
+     * @return array
+     */
+    public function getValidList(int $limit = 5)
+    {
+        $now = time();
+        
+        return AnnouncementEntity::where('status', 1)
+            ->where(function ($query) use ($now) {
+                $query->where('start_time', 0)
+                    ->whereOr('start_time', '<=', $now);
+            })
+            ->where(function ($query) use ($now) {
+                $query->where('end_time', 0)
+                    ->whereOr('end_time', '>=', $now);
+            })
+            ->order('type', 'desc')
+            ->order('id', 'desc')
+            ->limit($limit)
+            ->select()
+            ->toArray();
+    }
+}

+ 154 - 0
src/Model/MessageSettingModel.php

@@ -0,0 +1,154 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Model;
+
+use SixDec\Message\Entity\MessageSettingEntity;
+
+/**
+ * 消息设置模型
+ */
+class MessageSettingModel
+{
+    /**
+     * 获取消息设置列表
+     *
+     * @param array $params 查询参数
+     * @param int $page 页码
+     * @param int $limit 每页数量
+     * @return array
+     */
+    public function getList(array $params = [], int $page = 1, int $limit = 10)
+    {
+        $query = MessageSettingEntity::where('1=1');
+        
+        // 用户ID筛选
+        if (isset($params['user_id']) && $params['user_id'] !== '') {
+            $query->where('user_id', $params['user_id']);
+        }
+        
+        // 获取总数
+        $total = $query->count();
+        
+        // 获取分页数据
+        $list = $query->order('id', 'desc')
+            ->page($page, $limit)
+            ->select()
+            ->toArray();
+        
+        return [
+            'list' => $list,
+            'total' => $total,
+            'page' => $page,
+            'limit' => $limit,
+        ];
+    }
+    
+    /**
+     * 获取系统默认设置
+     *
+     * @return array|null
+     */
+    public function getDefaultSetting()
+    {
+        return MessageSettingEntity::where('user_id', 0)->find();
+    }
+    
+    /**
+     * 获取用户消息设置
+     *
+     * @param int $userId 用户ID
+     * @return array|null
+     */
+    public function getUserSetting(int $userId)
+    {
+        if ($userId <= 0) {
+            return null;
+        }
+        
+        $setting = MessageSettingEntity::where('user_id', $userId)->find();
+        if (!$setting) {
+            // 如果用户没有设置,则使用系统默认设置
+            $defaultSetting = $this->getDefaultSetting();
+            if ($defaultSetting) {
+                $setting = new MessageSettingEntity();
+                $setting->user_id = $userId;
+                $setting->system_notify = $defaultSetting->system_notify;
+                $setting->activity_notify = $defaultSetting->activity_notify;
+                $setting->order_notify = $defaultSetting->order_notify;
+                $setting->logistics_notify = $defaultSetting->logistics_notify;
+                $setting->private_message = $defaultSetting->private_message;
+                $setting->save();
+            }
+        }
+        
+        return $setting;
+    }
+    
+    /**
+     * 更新或创建消息设置
+     *
+     * @param int $userId 用户ID
+     * @param array $data 设置数据
+     * @return bool
+     */
+    public function updateSetting(int $userId, array $data)
+    {
+        $setting = MessageSettingEntity::where('user_id', $userId)->find();
+        if (!$setting) {
+            $setting = new MessageSettingEntity();
+            $setting->user_id = $userId;
+        }
+        
+        // 更新设置
+        if (isset($data['system_notify'])) {
+            $setting->system_notify = (int)$data['system_notify'];
+        }
+        if (isset($data['activity_notify'])) {
+            $setting->activity_notify = (int)$data['activity_notify'];
+        }
+        if (isset($data['order_notify'])) {
+            $setting->order_notify = (int)$data['order_notify'];
+        }
+        if (isset($data['logistics_notify'])) {
+            $setting->logistics_notify = (int)$data['logistics_notify'];
+        }
+        if (isset($data['private_message'])) {
+            $setting->private_message = (int)$data['private_message'];
+        }
+        
+        return $setting->save();
+    }
+    
+    /**
+     * 更新系统默认设置
+     *
+     * @param array $data 设置数据
+     * @return bool
+     */
+    public function updateDefaultSetting(array $data)
+    {
+        return $this->updateSetting(0, $data);
+    }
+    
+    /**
+     * 检查指定类型的通知是否对用户开启
+     *
+     * @param int $userId 用户ID
+     * @param int $type 通知类型:1-系统通知,2-活动通知,3-订单通知,4-物流通知,5-私信
+     * @return bool
+     */
+    public function isNotifyEnabled(int $userId, int $type)
+    {
+        if ($userId <= 0) {
+            return true;
+        }
+        
+        $setting = $this->getUserSetting($userId);
+        if (!$setting) {
+            return true;
+        }
+        
+        return $setting->isNotifyEnabled($type);
+    }
+}

+ 183 - 0
src/Model/MessageTemplateModel.php

@@ -0,0 +1,183 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Model;
+
+use SixDec\Message\Entity\MessageTemplateEntity;
+
+/**
+ * 消息模板模型
+ */
+class MessageTemplateModel
+{
+    /**
+     * 获取模板列表
+     *
+     * @param array $params 查询参数
+     * @param int $page 页码
+     * @param int $limit 每页数量
+     * @return array
+     */
+    public function getList(array $params = [], int $page = 1, int $limit = 10)
+    {
+        $query = MessageTemplateEntity::where('1=1');
+        
+        // 模板代码搜索
+        if (!empty($params['code'])) {
+            $query->where('code', 'like', '%' . $params['code'] . '%');
+        }
+        
+        // 模板名称搜索
+        if (!empty($params['name'])) {
+            $query->where('name', 'like', '%' . $params['name'] . '%');
+        }
+        
+        // 类型筛选
+        if (isset($params['type']) && $params['type'] !== '') {
+            $query->where('type', $params['type']);
+        }
+        
+        // 状态筛选
+        if (isset($params['status']) && $params['status'] !== '') {
+            $query->where('status', $params['status']);
+        }
+        
+        // 获取总数
+        $total = $query->count();
+        
+        // 获取分页数据
+        $list = $query->order('id', 'desc')
+            ->page($page, $limit)
+            ->select()
+            ->toArray();
+        
+        return [
+            'list' => $list,
+            'total' => $total,
+            'page' => $page,
+            'limit' => $limit,
+        ];
+    }
+    
+    /**
+     * 获取单个模板详情
+     *
+     * @param int $id 模板ID
+     * @return array|null
+     */
+    public function getDetail(int $id)
+    {
+        return MessageTemplateEntity::find($id);
+    }
+    
+    /**
+     * 根据模板代码获取模板
+     *
+     * @param string $code 模板代码
+     * @return array|null
+     */
+    public function getByCode(string $code)
+    {
+        return MessageTemplateEntity::where('code', $code)
+            ->where('status', 1)
+            ->find();
+    }
+    
+    /**
+     * 添加模板
+     *
+     * @param array $data 模板数据
+     * @return int|bool
+     */
+    public function add(array $data)
+    {
+        // 检查模板代码是否已存在
+        $exists = MessageTemplateEntity::where('code', $data['code'])->find();
+        if ($exists) {
+            return false;
+        }
+        
+        $entity = new MessageTemplateEntity();
+        $result = $entity->save($data);
+        
+        // 确保保存成功并返回ID
+        if ($result) {
+            return $entity->id ?: true; // 如果没有id属性,至少返回true表示成功
+        }
+        
+        return false;
+    }
+    
+    /**
+     * 更新模板
+     *
+     * @param int $id 模板ID
+     * @param array $data 更新数据
+     * @return bool
+     */
+    public function update(int $id, array $data)
+    {
+        $entity = MessageTemplateEntity::find($id);
+        if (!$entity) {
+            return false;
+        }
+        
+        // 检查模板代码是否已存在
+        if (isset($data['code']) && $data['code'] != $entity->code) {
+            $exists = MessageTemplateEntity::where('code', $data['code'])->find();
+            if ($exists) {
+                return false;
+            }
+        }
+        
+        return $entity->save($data);
+    }
+    
+    /**
+     * 删除模板
+     *
+     * @param int $id 模板ID
+     * @return bool
+     */
+    public function delete(int $id)
+    {
+        $entity = MessageTemplateEntity::find($id);
+        if (!$entity) {
+            return false;
+        }
+        
+        return $entity->delete();
+    }
+    
+    /**
+     * 批量删除模板
+     *
+     * @param array $ids 模板ID数组
+     * @return bool
+     */
+    public function batchDelete(array $ids)
+    {
+        if (empty($ids)) {
+            return false;
+        }
+        
+        return MessageTemplateEntity::destroy($ids);
+    }
+    
+    /**
+     * 解析模板内容
+     *
+     * @param string $code 模板代码
+     * @param array $data 替换数据
+     * @return array|null 解析后的标题和内容
+     */
+    public function parseTemplate(string $code, array $data)
+    {
+        $template = $this->getByCode($code);
+        if (!$template) {
+            return null;
+        }
+        
+        return $template->parse($data);
+    }
+}

+ 376 - 0
src/Model/NotificationModel.php

@@ -0,0 +1,376 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Model;
+
+use SixDec\Message\Entity\NotificationEntity;
+use SixDec\Message\Entity\MessageSettingEntity;
+use think\facade\Db;
+
+/**
+ * 用户通知模型
+ */
+class NotificationModel
+{
+    /**
+     * 获取通知列表
+     *
+     * @param array $params 查询参数
+     * @param int $page 页码
+     * @param int $limit 每页数量
+     * @return array
+     */
+    public function getList(array $params = [], int $page = 1, int $limit = 10)
+    {
+        $query = NotificationEntity::where('1=1');
+        
+        // 用户ID筛选
+        if (!empty($params['user_id'])) {
+            $query->where('user_id', $params['user_id']);
+        }
+        
+        // 标题搜索
+        if (!empty($params['title'])) {
+            $query->where('title', 'like', '%' . $params['title'] . '%');
+        }
+        
+        // 类型筛选
+        if (isset($params['type']) && $params['type'] !== '') {
+            $query->where('type', $params['type']);
+        }
+        
+        // 是否已读筛选
+        if (isset($params['is_read']) && $params['is_read'] !== '') {
+            $query->where('is_read', $params['is_read']);
+        }
+        
+        // 时间范围筛选
+        if (!empty($params['start_time'])) {
+            $query->where('create_time', '>=', strtotime($params['start_time']));
+        }
+        if (!empty($params['end_time'])) {
+            $query->where('create_time', '<=', strtotime($params['end_time']));
+        }
+        
+        // 获取总数
+        $total = $query->count();
+        
+        // 获取分页数据
+        $list = $query->order('id', 'desc')
+            ->page($page, $limit)
+            ->select()
+            ->toArray();
+        
+        return [
+            'list' => $list,
+            'total' => $total,
+            'page' => $page,
+            'limit' => $limit,
+        ];
+    }
+    
+    /**
+     * 获取用户通知列表
+     *
+     * @param int $userId 用户ID
+     * @param array $params 查询参数
+     * @param int $page 页码
+     * @param int $limit 每页数量
+     * @return array
+     */
+    public function getUserNotifications(int $userId, array $params = [], int $page = 1, int $limit = 10)
+    {
+        $query = NotificationEntity::where(function ($query) use ($userId) {
+            $query->where('user_id', $userId)
+                ->whereOr('user_id', 0); // 包括全局通知
+        });
+        
+        // 类型筛选
+        if (isset($params['type']) && $params['type'] !== '') {
+            $query->where('type', $params['type']);
+        }
+        
+        // 是否已读筛选
+        if (isset($params['is_read']) && $params['is_read'] !== '') {
+            $query->where('is_read', $params['is_read']);
+        }
+        
+        // 获取总数
+        $total = $query->count();
+        
+        // 获取分页数据
+        $list = $query->order('id', 'desc')
+            ->page($page, $limit)
+            ->select()
+            ->toArray();
+        
+        // 获取未读数量
+        $unreadCount = NotificationEntity::where(function ($query) use ($userId) {
+            $query->where('user_id', $userId)
+                ->whereOr('user_id', 0);
+        })->where('is_read', 0)->count();
+        
+        return [
+            'list' => $list,
+            'total' => $total,
+            'unread_count' => $unreadCount,
+            'page' => $page,
+            'limit' => $limit,
+        ];
+    }
+    
+    /**
+     * 获取单个通知详情
+     *
+     * @param int $id 通知ID
+     * @return array|null
+     */
+    public function getDetail(int $id)
+    {
+        return NotificationEntity::find($id);
+    }
+    
+    /**
+     * 添加通知
+     *
+     * @param array $data 通知数据
+     * @return int|bool
+     */
+    public function add(array $data)
+    {
+        $entity = new NotificationEntity();
+        $entity->save($data);
+        
+        return $entity->id;
+    }
+    
+    /**
+     * 发送通知给用户
+     *
+     * @param int $userId 用户ID,0表示全部用户
+     * @param string $title 通知标题
+     * @param string $content 通知内容
+     * @param int $type 通知类型:1-系统通知,2-活动通知,3-订单通知,4-物流通知,5-其他
+     * @return int|bool
+     */
+    public function sendToUser(int $userId, string $title, string $content, int $type = 1)
+    {
+        // 检查用户的消息设置
+        if ($userId > 0) {
+            $setting = MessageSettingEntity::where('user_id', $userId)->find();
+            if ($setting && !$setting->isNotifyEnabled($type)) {
+                // 用户已关闭此类型的通知
+                return false;
+            }
+        }
+        
+        $data = [
+            'user_id' => $userId,
+            'title' => $title,
+            'content' => $content,
+            'type' => $type,
+            'is_read' => 0,
+            'read_time' => 0,
+            'create_time' => time(),
+            'update_time' => time(),
+        ];
+        
+        return $this->add($data);
+    }
+    
+    /**
+     * 批量发送通知给多个用户
+     *
+     * @param array $userIds 用户ID数组
+     * @param string $title 通知标题
+     * @param string $content 通知内容
+     * @param int $type 通知类型
+     * @return bool
+     */
+    public function sendToMultiUsers(array $userIds, string $title, string $content, int $type = 1)
+    {
+        if (empty($userIds)) {
+            return false;
+        }
+        
+        $time = time();
+        $data = [];
+        
+        foreach ($userIds as $userId) {
+            // 检查用户的消息设置
+            $setting = MessageSettingEntity::where('user_id', $userId)->find();
+            if ($setting && !$setting->isNotifyEnabled($type)) {
+                // 用户已关闭此类型的通知
+                continue;
+            }
+            
+            $data[] = [
+                'user_id' => $userId,
+                'title' => $title,
+                'content' => $content,
+                'type' => $type,
+                'is_read' => 0,
+                'read_time' => 0,
+                'create_time' => $time,
+                'update_time' => $time,
+            ];
+        }
+        
+        if (empty($data)) {
+            return false;
+        }
+        
+        return Db::name('message_notifications')->insertAll($data);
+    }
+    
+    /**
+     * 发送全局通知
+     *
+     * @param string $title 通知标题
+     * @param string $content 通知内容
+     * @param int $type 通知类型
+     * @return int|bool
+     */
+    public function sendGlobal(string $title, string $content, int $type = 1)
+    {
+        return $this->sendToUser(0, $title, $content, $type);
+    }
+    
+    /**
+     * 标记通知为已读
+     *
+     * @param int $id 通知ID
+     * @param int $userId 用户ID,用于验证权限
+     * @return bool
+     */
+    public function markAsRead(int $id, int $userId = 0)
+    {
+        $entity = NotificationEntity::find($id);
+        if (!$entity) {
+            return false;
+        }
+        
+        // 验证权限
+        if ($userId > 0 && $entity->user_id > 0 && $entity->user_id != $userId) {
+            return false;
+        }
+        
+        return $entity->markAsRead()->save();
+    }
+    
+    /**
+     * 批量标记通知为已读
+     *
+     * @param array $ids 通知ID数组
+     * @param int $userId 用户ID,用于验证权限
+     * @return bool
+     */
+    public function batchMarkAsRead(array $ids, int $userId = 0)
+    {
+        if (empty($ids)) {
+            return false;
+        }
+        
+        $query = NotificationEntity::where('id', 'in', $ids);
+        
+        // 验证权限
+        if ($userId > 0) {
+            $query->where(function ($query) use ($userId) {
+                $query->where('user_id', $userId)
+                    ->whereOr('user_id', 0);
+            });
+        }
+        
+        return $query->update([
+            'is_read' => 1,
+            'read_time' => time(),
+        ]);
+    }
+    
+    /**
+     * 标记用户所有通知为已读
+     *
+     * @param int $userId 用户ID
+     * @return bool
+     */
+    public function markAllAsRead(int $userId)
+    {
+        if ($userId <= 0) {
+            return false;
+        }
+        
+        return NotificationEntity::where(function ($query) use ($userId) {
+            $query->where('user_id', $userId)
+                ->whereOr('user_id', 0);
+        })->where('is_read', 0)->update([
+            'is_read' => 1,
+            'read_time' => time(),
+        ]);
+    }
+    
+    /**
+     * 删除通知
+     *
+     * @param int $id 通知ID
+     * @param int $userId 用户ID,用于验证权限
+     * @return bool
+     */
+    public function delete(int $id, int $userId = 0)
+    {
+        $entity = NotificationEntity::find($id);
+        if (!$entity) {
+            return false;
+        }
+        
+        // 验证权限
+        if ($userId > 0 && $entity->user_id > 0 && $entity->user_id != $userId) {
+            return false;
+        }
+        
+        return $entity->delete();
+    }
+    
+    /**
+     * 批量删除通知
+     *
+     * @param array $ids 通知ID数组
+     * @param int $userId 用户ID,用于验证权限
+     * @return bool
+     */
+    public function batchDelete(array $ids, int $userId = 0)
+    {
+        if (empty($ids)) {
+            return false;
+        }
+        
+        $query = NotificationEntity::where('id', 'in', $ids);
+        
+        // 验证权限
+        if ($userId > 0) {
+            $query->where(function ($query) use ($userId) {
+                $query->where('user_id', $userId)
+                    ->whereOr('user_id', 0);
+            });
+        }
+        
+        return $query->delete();
+    }
+    
+    /**
+     * 获取用户未读通知数量
+     *
+     * @param int $userId 用户ID
+     * @return int
+     */
+    public function getUnreadCount(int $userId)
+    {
+        if ($userId <= 0) {
+            return 0;
+        }
+        
+        return NotificationEntity::where(function ($query) use ($userId) {
+            $query->where('user_id', $userId)
+                ->whereOr('user_id', 0);
+        })->where('is_read', 0)->count();
+    }
+}

+ 455 - 0
src/Model/PrivateMessageModel.php

@@ -0,0 +1,455 @@
+<?php
+declare(strict_types=1);
+
+namespace SixDec\Message\Model;
+
+use SixDec\Message\Entity\PrivateMessageEntity;
+use SixDec\Message\Entity\MessageSettingEntity;
+use think\facade\Db;
+
+/**
+ * 私信模型
+ */
+class PrivateMessageModel
+{
+    /**
+     * 获取私信列表
+     *
+     * @param array $params 查询参数
+     * @param int $page 页码
+     * @param int $limit 每页数量
+     * @return array
+     */
+    public function getList(array $params = [], int $page = 1, int $limit = 10)
+    {
+        $query = PrivateMessageEntity::where('1=1');
+        
+        // 发送者ID筛选
+        if (!empty($params['from_user_id'])) {
+            $query->where('from_user_id', $params['from_user_id']);
+        }
+        
+        // 接收者ID筛选
+        if (!empty($params['to_user_id'])) {
+            $query->where('to_user_id', $params['to_user_id']);
+        }
+        
+        // 是否已读筛选
+        if (isset($params['is_read']) && $params['is_read'] !== '') {
+            $query->where('is_read', $params['is_read']);
+        }
+        
+        // 内容搜索
+        if (!empty($params['content'])) {
+            $query->where('content', 'like', '%' . $params['content'] . '%');
+        }
+        
+        // 时间范围筛选
+        if (!empty($params['start_time'])) {
+            $query->where('create_time', '>=', strtotime($params['start_time']));
+        }
+        if (!empty($params['end_time'])) {
+            $query->where('create_time', '<=', strtotime($params['end_time']));
+        }
+        
+        // 获取总数
+        $total = $query->count();
+        
+        // 获取分页数据
+        $list = $query->order('id', 'desc')
+            ->page($page, $limit)
+            ->select()
+            ->toArray();
+        
+        return [
+            'list' => $list,
+            'total' => $total,
+            'page' => $page,
+            'limit' => $limit,
+        ];
+    }
+    
+    /**
+     * 获取用户私信列表
+     *
+     * @param int $userId 用户ID
+     * @param array $params 查询参数
+     * @param int $page 页码
+     * @param int $limit 每页数量
+     * @return array
+     */
+    public function getUserMessages(int $userId, array $params = [], int $page = 1, int $limit = 10)
+    {
+        $query = PrivateMessageEntity::where(function ($query) use ($userId) {
+            $query->where('to_user_id', $userId)
+                ->whereOr('from_user_id', $userId);
+        });
+        
+        // 对话用户ID筛选
+        if (!empty($params['partner_id'])) {
+            $partnerId = $params['partner_id'];
+            $query->where(function ($query) use ($userId, $partnerId) {
+                $query->where(function ($query) use ($userId, $partnerId) {
+                    $query->where('from_user_id', $userId)
+                        ->where('to_user_id', $partnerId);
+                })->whereOr(function ($query) use ($userId, $partnerId) {
+                    $query->where('from_user_id', $partnerId)
+                        ->where('to_user_id', $userId);
+                });
+            });
+        }
+        
+        // 是否已读筛选
+        if (isset($params['is_read']) && $params['is_read'] !== '') {
+            $query->where('is_read', $params['is_read']);
+        }
+        
+        // 获取总数
+        $total = $query->count();
+        
+        // 获取分页数据
+        $list = $query->order('id', 'desc')
+            ->page($page, $limit)
+            ->select()
+            ->toArray();
+        
+        // 获取未读数量
+        $unreadCount = PrivateMessageEntity::where('to_user_id', $userId)
+            ->where('is_read', 0)
+            ->count();
+        
+        return [
+            'list' => $list,
+            'total' => $total,
+            'unread_count' => $unreadCount,
+            'page' => $page,
+            'limit' => $limit,
+        ];
+    }
+    
+    /**
+     * 获取用户的对话列表
+     *
+     * @param int $userId 用户ID
+     * @param int $page 页码
+     * @param int $limit 每页数量
+     * @return array
+     */
+    public function getConversationList(int $userId, int $page = 1, int $limit = 10)
+    {
+        // 获取与用户相关的所有对话
+        $rawSql = "SELECT 
+                CASE 
+                    WHEN from_user_id = {$userId} THEN to_user_id 
+                    ELSE from_user_id 
+                END AS partner_id,
+                MAX(id) AS last_message_id
+            FROM cy_message_privates
+            WHERE (from_user_id = {$userId} OR to_user_id = {$userId})
+            AND delete_time IS NULL
+            GROUP BY partner_id
+            ORDER BY last_message_id DESC
+            LIMIT " . (($page - 1) * $limit) . ", {$limit}";
+        
+        $conversations = Db::query($rawSql);
+        
+        // 获取总数
+        $totalSql = "SELECT COUNT(DISTINCT CASE WHEN from_user_id = {$userId} THEN to_user_id ELSE from_user_id END) AS total
+            FROM cy_message_privates
+            WHERE (from_user_id = {$userId} OR to_user_id = {$userId})
+            AND delete_time IS NULL";
+        
+        $totalResult = Db::query($totalSql);
+        $total = $totalResult[0]['total'] ?? 0;
+        
+        // 获取每个对话的最后一条消息
+        $result = [];
+        foreach ($conversations as $conversation) {
+            $partnerId = $conversation['partner_id'];
+            $lastMessageId = $conversation['last_message_id'];
+            
+            // 获取最后一条消息
+            $lastMessage = PrivateMessageEntity::find($lastMessageId);
+            if (!$lastMessage) {
+                continue;
+            }
+            
+            // 获取未读消息数量
+            $unreadCount = PrivateMessageEntity::where('from_user_id', $partnerId)
+                ->where('to_user_id', $userId)
+                ->where('is_read', 0)
+                ->count();
+            
+            // 获取对话用户信息
+            $partnerInfo = null;
+            if ($partnerId > 0) {
+                $partnerInfo = Db::name('user')
+                    ->field('id, nickname, avatar')
+                    ->where('id', $partnerId)
+                    ->find();
+            } else {
+                $partnerInfo = [
+                    'id' => 0,
+                    'nickname' => '系统',
+                    'avatar' => '/static/images/system-avatar.png',
+                ];
+            }
+            
+            $result[] = [
+                'partner_id' => $partnerId,
+                'partner_info' => $partnerInfo,
+                'last_message' => $lastMessage->toArray(),
+                'unread_count' => $unreadCount,
+            ];
+        }
+        
+        return [
+            'list' => $result,
+            'total' => $total,
+            'page' => $page,
+            'limit' => $limit,
+        ];
+    }
+    
+    /**
+     * 获取两个用户之间的对话消息
+     *
+     * @param int $userId 当前用户ID
+     * @param int $partnerId 对话用户ID
+     * @param int $page 页码
+     * @param int $limit 每页数量
+     * @return array
+     */
+    public function getConversationMessages(int $userId, int $partnerId, int $page = 1, int $limit = 20)
+    {
+        $query = PrivateMessageEntity::where(function ($query) use ($userId, $partnerId) {
+            $query->where(function ($query) use ($userId, $partnerId) {
+                $query->where('from_user_id', $userId)
+                    ->where('to_user_id', $partnerId);
+            })->whereOr(function ($query) use ($userId, $partnerId) {
+                $query->where('from_user_id', $partnerId)
+                    ->where('to_user_id', $userId);
+            });
+        });
+        
+        // 获取总数
+        $total = $query->count();
+        
+        // 获取分页数据
+        $list = $query->order('id', 'desc')
+            ->page($page, $limit)
+            ->select()
+            ->toArray();
+        
+        // 自动标记接收的消息为已读
+        PrivateMessageEntity::where('from_user_id', $partnerId)
+            ->where('to_user_id', $userId)
+            ->where('is_read', 0)
+            ->update([
+                'is_read' => 1,
+                'read_time' => time(),
+            ]);
+        
+        return [
+            'list' => $list,
+            'total' => $total,
+            'page' => $page,
+            'limit' => $limit,
+        ];
+    }
+    
+    /**
+     * 获取单个私信详情
+     *
+     * @param int $id 私信ID
+     * @return array|null
+     */
+    public function getDetail(int $id)
+    {
+        return PrivateMessageEntity::find($id);
+    }
+    
+    /**
+     * 发送私信
+     *
+     * @param int $fromUserId 发送者用户ID,0表示系统
+     * @param int $toUserId 接收者用户ID
+     * @param string $content 私信内容
+     * @return int|bool
+     */
+    public function send(int $fromUserId, int $toUserId, string $content)
+    {
+        if ($toUserId <= 0) {
+            return false;
+        }
+        
+        // 检查接收者的消息设置
+        $setting = MessageSettingEntity::where('user_id', $toUserId)->find();
+        if ($setting && !$setting->isNotifyEnabled(5)) {
+            // 用户已关闭私信
+            return false;
+        }
+        
+        $entity = new PrivateMessageEntity();
+        $entity->from_user_id = $fromUserId;
+        $entity->to_user_id = $toUserId;
+        $entity->content = $content;
+        $entity->is_read = 0;
+        $entity->read_time = 0;
+        $entity->save();
+        
+        return $entity->id;
+    }
+    
+    /**
+     * 发送系统私信
+     *
+     * @param int $toUserId 接收者用户ID
+     * @param string $content 私信内容
+     * @return int|bool
+     */
+    public function sendSystemMessage(int $toUserId, string $content)
+    {
+        return $this->send(0, $toUserId, $content);
+    }
+    
+    /**
+     * 标记私信为已读
+     *
+     * @param int $id 私信ID
+     * @param int $userId 用户ID,用于验证权限
+     * @return bool
+     */
+    public function markAsRead(int $id, int $userId)
+    {
+        $entity = PrivateMessageEntity::find($id);
+        if (!$entity || $entity->to_user_id != $userId) {
+            return false;
+        }
+        
+        return $entity->markAsRead()->save();
+    }
+    
+    /**
+     * 批量标记私信为已读
+     *
+     * @param array $ids 私信ID数组
+     * @param int $userId 用户ID,用于验证权限
+     * @return bool
+     */
+    public function batchMarkAsRead(array $ids, int $userId)
+    {
+        if (empty($ids) || $userId <= 0) {
+            return false;
+        }
+        
+        return PrivateMessageEntity::where('id', 'in', $ids)
+            ->where('to_user_id', $userId)
+            ->where('is_read', 0)
+            ->update([
+                'is_read' => 1,
+                'read_time' => time(),
+            ]);
+    }
+    
+    /**
+     * 标记与指定用户的所有私信为已读
+     *
+     * @param int $userId 当前用户ID
+     * @param int $partnerId 对话用户ID
+     * @return bool
+     */
+    public function markAllAsRead(int $userId, int $partnerId)
+    {
+        if ($userId <= 0) {
+            return false;
+        }
+        
+        return PrivateMessageEntity::where('from_user_id', $partnerId)
+            ->where('to_user_id', $userId)
+            ->where('is_read', 0)
+            ->update([
+                'is_read' => 1,
+                'read_time' => time(),
+            ]);
+    }
+    
+    /**
+     * 删除私信
+     *
+     * @param int $id 私信ID
+     * @param int $userId 用户ID,用于验证权限
+     * @return bool
+     */
+    public function delete(int $id, int $userId)
+    {
+        $entity = PrivateMessageEntity::find($id);
+        if (!$entity || ($entity->from_user_id != $userId && $entity->to_user_id != $userId)) {
+            return false;
+        }
+        
+        return $entity->delete();
+    }
+    
+    /**
+     * 批量删除私信
+     *
+     * @param array $ids 私信ID数组
+     * @param int $userId 用户ID,用于验证权限
+     * @return bool
+     */
+    public function batchDelete(array $ids, int $userId)
+    {
+        if (empty($ids) || $userId <= 0) {
+            return false;
+        }
+        
+        return PrivateMessageEntity::where('id', 'in', $ids)
+            ->where(function ($query) use ($userId) {
+                $query->where('from_user_id', $userId)
+                    ->whereOr('to_user_id', $userId);
+            })
+            ->delete();
+    }
+    
+    /**
+     * 删除与指定用户的所有对话
+     *
+     * @param int $userId 当前用户ID
+     * @param int $partnerId 对话用户ID
+     * @return bool
+     */
+    public function deleteConversation(int $userId, int $partnerId)
+    {
+        if ($userId <= 0 || $partnerId < 0) {
+            return false;
+        }
+        
+        return PrivateMessageEntity::where(function ($query) use ($userId, $partnerId) {
+            $query->where(function ($query) use ($userId, $partnerId) {
+                $query->where('from_user_id', $userId)
+                    ->where('to_user_id', $partnerId);
+            })->whereOr(function ($query) use ($userId, $partnerId) {
+                $query->where('from_user_id', $partnerId)
+                    ->where('to_user_id', $userId);
+            });
+        })->delete();
+    }
+    
+    /**
+     * 获取用户未读私信数量
+     *
+     * @param int $userId 用户ID
+     * @return int
+     */
+    public function getUnreadCount(int $userId)
+    {
+        if ($userId <= 0) {
+            return 0;
+        }
+        
+        return PrivateMessageEntity::where('to_user_id', $userId)
+            ->where('is_read', 0)
+            ->count();
+    }
+}