Compare commits

...
50 Commits
Author SHA1 Message Date
hero920103 e4eab9ad89 更新 2026-04-01 10:12:37 +08:00
hero920103 2a6b14f698 增加企业产品分类 2026-03-21 14:12:21 +08:00
hero920103 9281c1dc35 提交文件 2026-03-21 09:10:24 +08:00
hero920103 d30a2531a7 更新blog界面 2026-03-20 20:35:00 +08:00
hero920103 b3a25ceb82 增加产品模块 2026-03-20 19:48:32 +08:00
hero920103 14c305f518 增加特色服务 2026-03-20 09:12:57 +08:00
hero920103 a7908e0677 新增模板,修复模板选择bug 2026-03-19 15:47:01 +08:00
hero920103 bf21006d4e 更新模板功能 2026-03-12 17:53:36 +08:00
hero920103 1f8d876cef 增加友链功能 2026-03-11 23:44:55 +08:00
hero920103 e46a4100c7 更新若干bug 2026-03-11 17:05:58 +08:00
hero920103 f134b0e759 更新若干bug 2026-03-11 11:11:10 +08:00
hero920103 5dffe62adc 完善模板功能 2026-03-10 22:59:46 +08:00
hero920103 98156991c1 修复若干bug 2026-03-10 22:24:06 +08:00
hero920103 7cdb591f69 更新租户端网站显示 2026-03-10 18:05:10 +08:00
hero920103 c00229f2ee 更新上传规则 2026-03-09 23:01:39 +08:00
hero920103 e27cc8f457 批量更新租户规则 2026-03-09 23:00:20 +08:00
hero920103 e09b4c639c 优化tenant模式 2026-03-09 18:27:05 +08:00
hero920103 bf0daf987b 更新网站架构 2026-03-09 16:35:17 +08:00
hero920103 4cbc52c51b 更新显示架构 2026-03-09 09:40:36 +08:00
hero920103 99fc99f8ad 更新后端传tenantid 2026-02-26 23:43:01 +08:00
hero920103 21480f43b1 改组织架构不合理地方 2026-02-26 20:56:58 +08:00
hero920103 050d1adc0b 增加极验模块 2026-02-26 17:38:11 +08:00
hero920103 f80e1f71d4 增加登录验证 2026-02-26 11:47:46 +08:00
hero920103 bfa5292962 更新 2026-02-26 09:53:51 +08:00
hero920103 1e8b6fe77d 增加租户模式 2026-02-26 09:40:36 +08:00
hero920103 67244496f1 erp功能增加 2026-02-25 10:58:44 +08:00
hero920103 c389fec971 增加erp组织架构 2026-02-24 16:52:22 +08:00
hero920103 25e3923ad3 登录界面记住我功能 2026-02-24 12:56:42 +08:00
hero920103 63e9cb5c48 增加模块分类 2026-02-24 10:35:18 +08:00
hero920103 0bf9b77aed 批量更新 2026-02-24 10:12:05 +08:00
hero920103 0ed654b5b8 新增api应用模块 2026-02-07 00:06:27 +08:00
hero920103 badc502fbb 更新路由配置和登录界面接口 2026-02-06 20:29:35 +08:00
hero920103 776aacfa2e 更新前端相关 2026-02-06 00:06:11 +08:00
hero920103 a32b1656b0 增加邮件发送功能 2026-02-05 22:27:49 +08:00
hero920103 27819199a4 修复裁剪头像代码 2026-02-05 17:42:17 +08:00
hero920103 0b6a7d0efb 完善仪表盘统计 2026-02-05 15:23:35 +08:00
hero920103 399d61e2f2 增加dashborad统计 2026-02-05 14:40:22 +08:00
hero920103 36fe5e2ab9 更新宝贝监护功能 2026-02-04 23:46:07 +08:00
hero920103 516c043924 增加宝贝监护功能 2026-02-04 18:02:36 +08:00
hero920103 769d595e30 增加路由模块 2026-02-04 11:36:40 +08:00
hero920103 7826025d42 优化路由模块 2026-02-04 11:33:53 +08:00
hero920103 85e52cea87 修复数据库参数类型错误 2026-01-30 16:30:53 +08:00
hero920103 581738da6e 更新模块市场功能 2026-01-30 16:19:53 +08:00
hero920103 5e997747c5 增加个人中心 2026-01-29 17:37:51 +08:00
hero920103 59b3a10d77 更新结构 2026-01-29 10:36:41 +08:00
hero920103 9e6a6bd9fe 增加环境忽略 2026-01-29 10:04:42 +08:00
hero920103 878e2dbcac 增加环境忽略 2026-01-29 10:04:37 +08:00
hero920103 18dfebf0de 更新公共跨域 2026-01-28 23:37:51 +08:00
hero920103 743d8d9ddf 删除无用文件 2026-01-28 23:14:52 +08:00
hero920103 aa2612bcb9 更新首页样式及跨域 2026-01-28 23:01:54 +08:00
500 changed files with 151361 additions and 13049 deletions
-11
View File
@@ -1,11 +0,0 @@
APP_DEBUG = true
DB_TYPE = mysql
DB_HOST = 10.168.1.239
DB_NAME = official_website
DB_USER = official
DB_PASS = meitian@#!
DB_PORT = 3306
DB_CHARSET = utf8
DEFAULT_LANG = zh-cn
+7
View File
@@ -1,5 +1,6 @@
# Logs
logs
runtime
*.log
npm-debug.log*
yarn-debug.log*
@@ -22,3 +23,9 @@ dist-ssr
*.njsproj
*.sln
*.sw?
.env
.env.*
!.example.env
public\storage
-100
View File
@@ -1,100 +0,0 @@
# Redis 配置说明
## 1. 安装 Redis 扩展
在项目根目录执行:
```bash
composer require predis/predis
```
或者如果使用 phpredis 扩展(需要 PHP Redis 扩展):
```bash
# 确保已安装 PHP Redis 扩展
# 然后直接使用,无需 composer 安装
```
## 2. 配置环境变量
在项目根目录创建 `.env` 文件(如果不存在),添加以下配置:
```env
# Redis 配置
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
REDIS_PREFIX=
# 缓存配置
CACHE_DRIVER=redis
# Session 配置
SESSION_DRIVER=redis
SESSION_PREFIX=
```
## 3. 配置文件说明
### cache.php
- 默认缓存驱动已设置为 `redis`
- Redis 连接配置已添加
### session.php
- Session 驱动已设置为 `redis`
- 使用 Redis 存储 Session
## 4. 使用说明
### 缓存使用
```php
use think\facade\Cache;
// 设置缓存
Cache::set('key', 'value', 3600);
// 获取缓存
$value = Cache::get('key');
// 删除缓存
Cache::delete('key');
```
### Session 使用
```php
use think\facade\Session;
// 设置 Session
Session::set('key', 'value');
// 获取 Session
$value = Session::get('key');
```
## 5. 验证 Redis 连接
确保 Redis 服务已启动:
```bash
# Windows
redis-server
# Linux/Mac
sudo systemctl start redis
# 或
redis-server
```
测试连接:
```bash
redis-cli ping
# 应该返回 PONG
```
## 6. 注意事项
1. 如果 Redis 未安装或连接失败,可以临时将 `CACHE_DRIVER``SESSION_DRIVER` 改回 `file`
2. Redis 密码如果为空,可以不设置 `REDIS_PASSWORD`
3. `REDIS_PREFIX` 用于区分不同项目的缓存,建议设置
+11
View File
@@ -187,4 +187,15 @@ abstract class BaseController
}
}
/**
* 获取当前用户的 tid
*
* @return int 租户ID
*/
protected function getTenantId(): int
{
$userInfo = $this->getAdminUserInfo();
return isset($userInfo['tid']) ? intval($userInfo['tid']) : 0;
}
}
@@ -0,0 +1,417 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\BabyHealth;
use app\admin\BaseController;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\AppsBabyhealthBabys;
use app\model\AppsBabyhealthUsers;
class BabysController extends BaseController
{
/**
* 获取宝贝信息
* @return Json
*/
public function getBabyList()
{
try {
$babylist = AppsBabyhealthBabys::where('delete_time', null)
->field('id, name, nickname, sex, avatar, birth, father, mother, height, weight, status, create_time, update_time')
->select()
->toArray();
// 查询每个宝贝的父母名称
foreach ($babylist as &$baby) {
$parentNames = $this->findParentNames(
(int)($baby['father'] ?? 0),
(int)($baby['mother'] ?? 0)
);
$baby['father'] = $parentNames['father_name'];
$baby['mother'] = $parentNames['mother_name'];
}
// 记录操作日志
$this->logSuccess('宝贝管理', '获取宝贝信息', ['data' => $babylist]);
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $babylist
]);
} catch (DbException $e) {
// 记录失败日志
$this->logFail('宝贝管理', '获取宝贝信息', $e->getMessage());
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage(),
'data' => []
]);
}
}
/**
* 创建宝贝信息
* @return Json
*/
public function createBaby()
{
try {
// 获取请求参数
$data = $this->request->post();
// 准备宝贝数据
$babyData = [
'name' => $data['name'],
'nickname' => $data['nickname'],
'sex' => $data['sex'],
'birth' => $data['birth'],
'height' => $data['height'],
'weight' => $data['weight'],
'avatar' => $data['avatar'],
'status' => $data['status'],
'create_time' => date('Y-m-d H:i:s'),
];
// 创建宝贝信息
$result = AppsBabyhealthBabys::insertGetId($babyData);
if ($result === false) {
return json([
'code' => 500,
'msg' => '创建失败'
]);
}
// 记录操作日志
$this->logSuccess('宝贝管理', '创建宝贝信息', ['id' => $result]);
return json([
'code' => 200,
'msg' => '创建成功',
'data' => ['id' => $result]
]);
} catch (ValidateException $e) {
// 记录失败日志
$this->logFail('宝贝管理', '创建宝贝信息', $e->getMessage());
return json([
'code' => 400,
'msg' => $e->getError()
]);
} catch (DbException $e) {
// 记录失败日志
$this->logFail('宝贝管理', '创建宝贝信息', $e->getMessage());
return json([
'code' => 500,
'msg' => '创建失败:' . $e->getMessage()
]);
}
}
/**
* 编辑宝贝信息
* @param int $id Baby ID
* @return Json
*/
public function editBaby(int $id)
{
try {
// 获取请求参数
$data = $this->request->post();
// 检查宝贝信息是否存在
$baby = AppsBabyhealthBabys::where('id', $id)
->where('delete_time', null)
->find();
if (!$baby) {
return json([
'code' => 404,
'msg' => '宝贝信息不存在'
]);
}
// 准备更新数据
$updateData = [
'name' => $data['name'],
'nickname' => $data['nickname'],
'sex' => $data['sex'],
'birth' => $data['birth'],
'height' => $data['height'],
'weight' => $data['weight'],
'avatar' => $data['avatar'],
'status' => $data['status'],
'update_time' => date('Y-m-d H:i:s'),
];
// 执行更新
AppsBabyhealthBabys::where('id', $id)->update($updateData);
// 获取更新后的宝贝信息
$updatedBaby = AppsBabyhealthBabys::where('id', $id)->find();
// 记录操作日志
$this->logSuccess('宝贝管理', '更新宝贝信息', ['id' => $id]);
return json([
'code' => 200,
'msg' => '更新成功',
'data' => $updatedBaby ? $updatedBaby->toArray() : []
]);
} catch (ValidateException $e) {
// 记录失败日志
$this->logFail('宝贝管理', '更新宝贝信息', $e->getMessage());
return json([
'code' => 400,
'msg' => $e->getError()
]);
} catch (DbException $e) {
// 记录失败日志
$this->logFail('宝贝管理', '更新宝贝信息', $e->getMessage());
return json([
'code' => 500,
'msg' => '更新失败:' . $e->getMessage()
]);
}
}
/**
* 删除宝贝信息
* @param int $id Baby ID
* @return Json
*/
public function deleteBaby(int $id)
{
try {
// 检查宝贝信息是否存在
$baby = AppsBabyhealthBabys::where('id', $id)
->where('delete_time', null)
->find();
if (!$baby) {
return json([
'code' => 404,
'msg' => '宝贝信息不存在'
]);
}
// 逻辑删除宝贝信息
$result = AppsBabyhealthBabys::where('id', $id)
->where('delete_time', null)
->update([
'delete_time' => date('Y-m-d H:i:s'),
'update_time' => date('Y-m-d H:i:s')
]);
if ($result === false) {
return json([
'code' => 500,
'msg' => '删除失败'
]);
}
// 记录操作日志
$this->logSuccess('宝贝管理', '删除宝贝信息', ['id' => $id]);
return json([
'code' => 200,
'msg' => '删除成功'
]);
} catch (DbException $e) {
// 记录失败日志
$this->logFail('宝贝管理', '删除宝贝信息', $e->getMessage());
return json([
'code' => 500,
'msg' => '删除失败:' . $e->getMessage()
]);
}
}
/**
* 预览宝贝信息
* @param int $id Baby ID
* @return Json
*/
public function getBabyDetail(int $id)
{
try {
// 查询宝贝信息
$baby = AppsBabyhealthBabys::where('id', $id)
->where('delete_time', null)
->find();
if (!$baby) {
return json([
'code' => 404,
'msg' => '宝贝信息不存在'
]);
}
// 记录操作日志
$this->logSuccess('宝贝监护-宝贝管理', '获取宝贝信息', ['id' => $id]);
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $baby->toArray()
]);
} catch (DbException $e) {
// 记录失败日志
$this->logFail('宝贝监护-宝贝管理', '获取宝贝信息', $e->getMessage());
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage(),
'data' => []
]);
}
}
/**
* 绑定父母
* @param int $id Baby ID
* @return Json
*/
public function bindParent(int $id)
{
try {
// 获取请求参数(支持POST
$data = $this->request->post();
// 检查宝贝信息是否存在
$baby = AppsBabyhealthBabys::where('id', $id)
->where('delete_time', null)
->find();
if (!$baby) {
return json([
'code' => 404,
'msg' => '宝贝信息不存在'
]);
}
// 准备更新数据
$updateData = [
'update_time' => date('Y-m-d H:i:s'),
];
// 更新父亲ID
if (isset($data['father_id'])) {
$updateData['father_id'] = $data['father_id'];
}
// 更新母亲ID
if (isset($data['mother_id'])) {
$updateData['mother_id'] = $data['mother_id'];
}
// 执行更新
AppsBabyhealthBabys::where('id', $id)->update($updateData);
// 记录操作日志
$this->logSuccess('宝贝管理', '绑定父母', ['id' => $id, 'father_id' => $data['father_id'] ?? null, 'mother_id' => $data['mother_id'] ?? null]);
return json([
'code' => 200,
'msg' => '绑定成功'
]);
} catch (DbException $e) {
// 记录失败日志
$this->logFail('宝贝管理', '绑定父母', $e->getMessage());
return json([
'code' => 500,
'msg' => '绑定失败:' . $e->getMessage()
]);
}
}
/**
* 获取父母名称
* @param int $id 宝贝ID
* @return Json
*/
public function getParents(int $id)
{
try {
// 查询宝贝的父母ID
$baby = AppsBabyhealthBabys::where('id', $id)
->where('delete_time', null)
->field('father, mother')
->find();
if (!$baby) {
return json([
'code' => 404,
'msg' => '宝贝信息不存在'
]);
}
// 获取父母名称
$parentNames = $this->findParentNames(
(int)($baby['father'] ?? 0),
(int)($baby['mother'] ?? 0)
);
// 记录操作日志
$this->logSuccess('宝贝管理', '获取父母信息', ['id' => $id]);
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'father_id' => $baby['father'] ?? null,
'mother_id' => $baby['mother'] ?? null,
'father' => $parentNames['father_name'],
'mother' => $parentNames['mother_name']
]
]);
} catch (DbException $e) {
// 记录失败日志
$this->logFail('宝贝管理', '获取父母信息', $e->getMessage());
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage(),
'data' => []
]);
}
}
/**
* 查找父母名称
* @param int $father 父亲ID
* @param int $mother 母亲ID
* @return array
*/
private function findParentNames(int $father = 0, int $mother = 0): array
{
$result = [
'father_name' => '',
'mother_name' => ''
];
// 查询父亲名称
if ($father > 0) {
$father = \app\model\AppsBabyhealthUsers::where('id', $father)
->where('delete_time', null)
->field('name')
->find();
if ($father) {
$result['father_name'] = $father['name'];
}
}
// 查询母亲名称
if ($mother > 0) {
$mother = \app\model\AppsBabyhealthUsers::where('id', $mother)
->where('delete_time', null)
->field('name')
->find();
if ($mother) {
$result['mother_name'] = $mother['name'];
}
}
return $result;
}
}
@@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\BabyHealth;
use app\admin\BaseController;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\AppsBabyhealthBabys;
use app\model\AppsBabyhealthUsers;
class DashboradController extends BaseController
{
/**
* dashborad总体输出
*/
public function getDashborad()
{
try {
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'userCounts' => $this->getUserCounts(),
'babyCounts' => $this->getBabyCounts()
]
]);
} catch (DbException $e) {
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage()
]);
}
}
/**
* 统计宝贝数量
* @return array
*/
private function getBabyCounts(): array
{
try {
// 总数
$total = AppsBabyhealthBabys::where('delete_time', null)->count();
// 男宝宝数 (sex = 1)
$maleCount = AppsBabyhealthBabys::where('delete_time', null)->where('sex', 1)->count();
// 女宝宝数 (sex = 2)
$femaleCount = AppsBabyhealthBabys::where('delete_time', null)->where('sex', 2)->count();
// 未知性别 (sex = 0)
$unknownCount = AppsBabyhealthBabys::where('delete_time', null)->where('sex', 0)->count();
return [
'total' => $total,
'male' => $maleCount,
'female' => $femaleCount,
'unknown' => $unknownCount
];
} catch (DbException $e) {
// 返回空数组而不是抛出异常,让外层处理
error_log('统计宝贝数量失败: ' . $e->getMessage());
return [
'total' => 0,
'male' => 0,
'female' => 0,
'unknown' => 0
];
}
}
/**
* 统计用户数量
* @return array
*/
private function getUserCounts(): array
{
try {
// 总数
$total = AppsBabyhealthUsers::where('delete_time', null)->count();
// 父亲 (sex = 1)
$fatherCount = AppsBabyhealthUsers::where('delete_time', null)->where('sex', 1)->count();
// 母亲 (sex = 2)
$motherCount = AppsBabyhealthUsers::where('delete_time', null)->where('sex', 2)->count();
// 未知性别 (sex = 0)
$unknownCount = AppsBabyhealthUsers::where('delete_time', null)->where('sex', 0)->count();
return [
'total' => $total,
'father' => $fatherCount,
'mother' => $motherCount,
'unknown' => $unknownCount
];
} catch (DbException $e) {
// 返回空数组而不是抛出异常,让外层处理
error_log('统计用户数量失败: ' . $e->getMessage());
return [
'total' => 0,
'father' => 0,
'mother' => 0,
'unknown' => 0
];
}
}
}
@@ -0,0 +1,258 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\BabyHealth;
use app\admin\BaseController;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\AppsBabyhealthUsers;
class UserController extends BaseController
{
/**
* 获取用户信息
* @return Json
*/
public function getUserList()
{
try {
$babylist = AppsBabyhealthUsers::where('delete_time', null)
->field('id, account, phone, email, name, sex, avatar, birth, status, create_time, update_time')
->select()
->toArray();
// 记录操作日志
$this->logSuccess('宝贝监护-用户管理', '获取用户信息', ['data' => $babylist]);
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $babylist
]);
} catch (DbException $e) {
// 记录失败日志
$this->logFail('宝贝监护-用户管理', '获取用户信息', $e->getMessage());
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage(),
'data' => []
]);
}
}
/**
* 创建用户信息
* @return Json
*/
public function createUser()
{
try {
// 获取请求参数
$data = $this->request->param();
// 准备用户数据,密码进行 MD5 加密
$babyData = [
'account' => $data['account'],
'password' => !empty($data['password']) ? md5($data['password']) : '',
'name' => $data['name'],
'sex' => $data['sex'],
'birth' => $data['birth'],
'phone' => $data['phone'],
'email' => $data['email'],
'avatar' => $data['avatar'],
'status' => $data['status'],
'create_time' => date('Y-m-d H:i:s'),
];
// 创建用户信息
$result = AppsBabyhealthUsers::insertGetId($babyData);
if ($result === false) {
return json([
'code' => 500,
'msg' => '创建失败'
]);
}
// 记录操作日志
$this->logSuccess('宝贝监护-用户管理', '创建用户信息', ['id' => $result]);
return json([
'code' => 200,
'msg' => '创建成功',
'data' => ['id' => $result]
]);
} catch (ValidateException $e) {
// 记录失败日志
$this->logFail('宝贝监护-用户管理', '创建用户信息', $e->getMessage());
return json([
'code' => 400,
'msg' => $e->getError()
]);
} catch (DbException $e) {
// 记录失败日志
$this->logFail('宝贝监护-用户管理', '创建用户信息', $e->getMessage());
return json([
'code' => 500,
'msg' => '创建失败:' . $e->getMessage()
]);
}
}
/**
* 编辑用户信息
* @param int $id User ID
* @return Json
*/
public function updateUser(int $id)
{
try {
// 获取请求参数
$data = $this->request->param();
// 检查用户信息是否存在
$baby = AppsBabyhealthUsers::where('id', $id)
->where('delete_time', null)
->find();
if (!$baby) {
return json([
'code' => 404,
'msg' => '用户信息不存在'
]);
}
// 准备更新数据
$updateData = [
'name' => $data['name'],
'sex' => $data['sex'],
'birth' => $data['birth'],
'phone' => $data['phone'],
'email' => $data['email'],
'avatar' => $data['avatar'],
'status' => $data['status'],
'update_time' => date('Y-m-d H:i:s'),
];
// 执行更新
AppsBabyhealthUsers::where('id', $id)->update($updateData);
// 获取更新后的用户信息
$updatedBaby = AppsBabyhealthUsers::where('id', $id)->find();
// 记录操作日志
$this->logSuccess('宝贝监护-用户管理', '更新用户信息', ['id' => $id]);
return json([
'code' => 200,
'msg' => '更新成功',
'data' => $updatedBaby ? $updatedBaby->toArray() : []
]);
} catch (ValidateException $e) {
// 记录失败日志
$this->logFail('宝贝监护-用户管理', '更新用户信息', $e->getMessage());
return json([
'code' => 400,
'msg' => $e->getError()
]);
} catch (DbException $e) {
// 记录失败日志
$this->logFail('宝贝监护-用户管理', '更新用户信息', $e->getMessage());
return json([
'code' => 500,
'msg' => '更新失败:' . $e->getMessage()
]);
}
}
/**
* 删除用户信息
* @param int $id User ID
* @return Json
*/
public function deleteUser(int $id)
{
try {
// 检查用户信息是否存在
$baby = AppsBabyhealthUsers::where('id', $id)
->where('delete_time', null)
->find();
if (!$baby) {
return json([
'code' => 404,
'msg' => '用户信息不存在'
]);
}
// 逻辑删除用户信息
$result = AppsBabyhealthUsers::where('id', $id)
->where('delete_time', null)
->update([
'delete_time' => date('Y-m-d H:i:s'),
'update_time' => date('Y-m-d H:i:s')
]);
if ($result === false) {
return json([
'code' => 500,
'msg' => '删除失败'
]);
}
// 记录操作日志
$this->logSuccess('宝贝监护-用户管理', '删除用户信息', ['id' => $id]);
return json([
'code' => 200,
'msg' => '删除成功'
]);
} catch (DbException $e) {
// 记录失败日志
$this->logFail('宝贝监护-用户管理', '删除用户信息', $e->getMessage());
return json([
'code' => 500,
'msg' => '删除失败:' . $e->getMessage()
]);
}
}
/**
* 预览用户信息
* @param int $id User ID
* @return Json
*/
public function getUserDetail(int $id)
{
try {
// 查询用户信息
$baby = AppsBabyhealthUsers::where('id', $id)
->where('delete_time', null)
->find();
if (!$baby) {
return json([
'code' => 404,
'msg' => '用户信息不存在'
]);
}
// 记录操作日志
$this->logSuccess('宝贝监护-用户管理', '获取用户信息', ['id' => $id]);
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $baby->toArray()
]);
} catch (DbException $e) {
// 记录失败日志
$this->logFail('宝贝监护-用户管理', '获取用户信息', $e->getMessage());
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage(),
'data' => []
]);
}
}
}
@@ -2,16 +2,16 @@
declare(strict_types=1);
namespace app\admin\controller;
namespace app\admin\controller\Cms\Analytics;
use app\admin\BaseController;
use think\exception\ValidateException;
use think\facade\Db;
use think\response\Json;
use think\db\exception\DbException;
use app\model\Articles;
use app\model\ArticlesCategory;
use app\model\AdminUser;
use app\model\Cms\Articles;
use app\model\Cms\ArticlesCategory;
use app\model\System\AdminUser;
class AnalyticsController extends BaseController
{
@@ -23,80 +23,47 @@ class AnalyticsController extends BaseController
public function getContentStats()
{
try {
$today = date('Y-m-d');
$yesterday = date('Y-m-d', strtotime('-1 day'));
$monthStart = date('Y-m-01');
$yesterdayMonthStart = date('Y-m-01', strtotime('-1 day'));
$monthEnd = date('Y-m-t 23:59:59');
// 总发布量
$totalArticles = Articles::where('delete_time', null)
->where('tid', $this->getTenantId())
->where('status', 2)
->count();
// 昨日发布
$yesterdayTotalArticles = Articles::where('delete_time', null)
// 昨日新增发布
$yesterdayArticles = Articles::where('delete_time', null)
->where('tid', $this->getTenantId())
->where('status', 2)
->where('publish_date', '>=', $yesterday . ' 00:00:00')
->where('publish_date', '<=', $yesterday . ' 23:59:59')
->count();
// 本月新增
// 本月新增发布
$monthNewArticles = Articles::where('delete_time', null)
->where('tid', $this->getTenantId())
->where('status', 2)
->where('publish_date', '>=', $monthStart . ' 00:00:00')
->where('publish_date', '<=', $monthEnd)
->count();
// 上月同期新增(用于对比)
$lastMonthStart = date('Y-m-01', strtotime('-1 month'));
$lastMonthEnd = date('Y-m-t 23:59:59', strtotime('-1 month'));
$lastMonthNewArticles = Articles::where('delete_time', null)
->where('status', 2)
->where('publish_date', '>=', $lastMonthStart . ' 00:00:00')
->where('publish_date', '<=', $lastMonthEnd)
->count();
// 总点赞量
$totalLikes = Articles::where('delete_time', null)
->where('tid', $this->getTenantId())
->where('status', 2)
->sum('likes');
// 昨日点赞量
$yesterdayLikes = Articles::where('delete_time', null)
->where('status', 2)
->where('update_time', '>=', $yesterday . ' 00:00:00')
->where('update_time', '<=', $yesterday . ' 23:59:59')
->sum('likes');
// 总访问量
$totalViews = Articles::where('delete_time', null)
->where('tid', $this->getTenantId())
->where('status', 2)
->sum('views');
// 昨日访问量
$yesterdayViews = Articles::where('delete_time', null)
->where('status', 2)
->where('update_time', '>=', $yesterday . ' 00:00:00')
->where('update_time', '<=', $yesterday . ' 23:59:59')
->sum('views');
// 计算增长率
$articleGrowth = $yesterdayTotalArticles > 0
? round(($totalArticles - $yesterdayTotalArticles) / $yesterdayTotalArticles * 100, 2)
: 0;
$monthGrowth = $lastMonthNewArticles > 0
? round(($monthNewArticles - $lastMonthNewArticles) / $lastMonthNewArticles * 100, 2)
: 0;
$likesGrowth = $yesterdayLikes > 0
? round(($totalLikes - $yesterdayLikes) / $yesterdayLikes * 100, 2)
: 0;
$viewsGrowth = $yesterdayViews > 0
? round(($totalViews - $yesterdayViews) / $yesterdayViews * 100, 2)
: 0;
// 热门内容TOP5
$hotArticles = Articles::where('delete_time', null)
->where('tid', $this->getTenantId())
->where('status', 2)
->order('views', 'desc')
->limit(5)
@@ -107,28 +74,11 @@ class AnalyticsController extends BaseController
->select();
$stats = [
'overview' => [
'total_articles' => [
'value' => $totalArticles,
'yesterday' => $yesterdayTotalArticles,
'growth' => $articleGrowth
],
'month_new' => [
'value' => $monthNewArticles,
'last_month' => $lastMonthNewArticles,
'growth' => $monthGrowth
],
'total_likes' => [
'value' => (int)$totalLikes,
'yesterday' => (int)$yesterdayLikes,
'growth' => $likesGrowth
],
'total_views' => [
'value' => (int)$totalViews,
'yesterday' => (int)$yesterdayViews,
'growth' => $viewsGrowth
],
],
'total_articles' => $totalArticles,
'yesterday_articles' => $yesterdayArticles,
'month_articles' => $monthNewArticles,
'total_likes' => (int)$totalLikes,
'total_views' => (int)$totalViews,
'hot_articles' => $hotArticles->toArray()
];
@@ -156,7 +106,9 @@ class AnalyticsController extends BaseController
{
try {
$stats = [
'total_users' => AdminUser::where('delete_time', null)->count(),
'total_users' => AdminUser::where('delete_time', null)
->where('tid', $this->getTenantId())
->count(),
];
// 记录操作日志
$this->logSuccess('用户统计', '获取用户统计', ['data' => $stats]);
@@ -1,7 +1,7 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\Article;
namespace app\admin\controller\Cms\Article;
use app\admin\BaseController;
use think\exception\ValidateException;
@@ -9,6 +9,7 @@ use think\facade\Db;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\Cms\ArticlesCategory;
class ArticleCategoryController extends BaseController
{
@@ -24,8 +25,8 @@ class ArticleCategoryController extends BaseController
$keyword = $keyword ?? '';
// 获取文章分类列表
$categories = Db::name('mete_articles_category')
->where('delete_time', null)
$categories = ArticlesCategory::where('delete_time', null)
->where('tid', $this->getTenantId())
->where('status', 1)
->where('name', 'like', '%' . $keyword . '%')
->order('sort', 'desc')
@@ -55,8 +56,8 @@ class ArticleCategoryController extends BaseController
{
try {
// 获取文章分类列表
$categories = Db::name('mete_articles_category')
->where('delete_time', null)
$categories = ArticlesCategory::where('delete_time', null)
->where('tid', $this->getTenantId())
->where('status', 1)
->field('id,cid,name,image,sort')
->order('sort', 'desc')
@@ -93,10 +94,11 @@ class ArticleCategoryController extends BaseController
$data['cid'] = isset($data['cid']) ? (int)$data['cid'] : 0;
$data['sort'] = isset($data['sort']) ? (int)$data['sort'] : 0;
$data['status']= isset($data['status']) ? (int)$data['status'] : 1;
$data['tid'] = $this->getTenantId();
$data['create_time'] = date('Y-m-d H:i:s');
$data['update_time'] = $data['create_time'];
$id = Db::name('mete_articles_category')->insertGetId($data);
$id = ArticlesCategory::insertGetId($data);
return json(['code'=>200,'msg'=>'success','data'=>['id'=>$id]]);
} catch (ValidateException $ve) {
// 记录失败日志
@@ -122,7 +124,7 @@ class ArticleCategoryController extends BaseController
return json(['code'=>400,'msg'=>'无更新数据','data'=>null]);
}
$data['update_time'] = date('Y-m-d H:i:s');
$affected = Db::name('mete_articles_category')->where('id',$id)->update($data);
$affected = ArticlesCategory::where('id',$id)->where('tid', $this->getTenantId())->update($data);
if ($affected) {
// 记录成功日志
$this->logSuccess('文章分类管理', '更新文章分类', ['id' => $id]);
@@ -144,8 +146,8 @@ class ArticleCategoryController extends BaseController
public function deleteCategory(int $id): Json
{
try {
$affected = Db::name('mete_articles_category')
->where('id', $id)
$affected = ArticlesCategory::where('id', $id)
->where('tid', $this->getTenantId())
->update(['delete_time' => date('Y-m-d H:i:s')]);
if ($affected) {
@@ -179,7 +181,7 @@ class ArticleCategoryController extends BaseController
}
$data['status'] = isset($data['status']) ? (int)$data['status'] : 1;
$data['update_time'] = date('Y-m-d H:i:s');
$affected = Db::name('mete_articles_category')->where('id',$id)->update($data);
$affected = ArticlesCategory::where('id',$id)->where('tid', $this->getTenantId())->update($data);
if ($affected) {
// 记录成功日志
$this->logSuccess('文章分类管理', '更新分类状态', ['id' => $id]);
@@ -1,7 +1,7 @@
<?php
declare (strict_types = 1);
namespace app\admin\controller\Article;
namespace app\admin\controller\Cms\Article;
use app\admin\BaseController;
use think\exception\ValidateException;
@@ -10,6 +10,8 @@ use think\facade\Request;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\Cms\Articles;
use app\model\System\AdminUser;
class ArticleController extends BaseController
{
@@ -21,18 +23,20 @@ class ArticleController extends BaseController
* @param int $pageSize 每页条数(默认10
* @return Json
*/
public function getArticlesList(string $keyword = '', string $cate = '', int $page = 1, int $pageSize = 10): Json
public function getArticlesList(string $author = '', string $keyword = '', string $cate = '', int $page = 1, int $pageSize = 10): Json
{
try {
// 安全处理参数(防止非法参数)
$page = max(1, $page); // 页码最小为1
$pageSize = max(1, min(100, $pageSize)); // 每页条数限制1-100条
$author = trim($author);
$keyword = trim($keyword);
$cate = trim($cate);
// 输出参数值
trace('查询参数:', 'debug');
trace([
'author' => $author,
'keyword' => $keyword,
'cate' => $cate,
'page' => $page,
@@ -40,8 +44,9 @@ class ArticleController extends BaseController
], 'debug');
// 构建查询条件,合并表连接
$query = Db::name('mete_articles')->alias('a')
$query = Articles::alias('a')
->where('a.delete_time', null)
->where('a.tid', $this->getTenantId())
->leftJoin('mete_admin_user u', 'a.publisher = u.id')
->leftJoin('mete_articles_category c', 'a.cate = c.id');
@@ -49,6 +54,11 @@ class ArticleController extends BaseController
if ($keyword) {
$query->where('a.title', 'like', '%' . $keyword . '%');
}
// 作者筛选
if ($author) {
$query->where('a.publisher', 'like', '%' . $author . '%');
}
// 分类筛选
if ($cate) {
@@ -98,8 +108,7 @@ class ArticleController extends BaseController
$newTitleLength = strlen($newTitleLower);
// 获取所有文章标题
$allTitles = Db::name('mete_articles')
->field('id, title')
$allTitles = Articles::field('id, title')
->select();
$similarArticles = [];
@@ -180,10 +189,11 @@ class ArticleController extends BaseController
'is_trans' => $data['is_trans'] ?? 0,
'transurl' => $data['is_trans'] == 1 ? ($data['transurl'] ?? '') : '',
'publisher' => $userId,
'tid' => $this->getTenantId(),
'create_time' => date('Y-m-d H:i:s'),
];
$id = Db::name('mete_articles')->insertGetId($insertData);
$id = Articles::insertGetId($insertData);
// 记录成功日志
$this->logSuccess('文章管理', '创建文章', ['id' => $id]);
@@ -206,10 +216,17 @@ class ArticleController extends BaseController
public function getArticle(int $id): Json
{
try {
$article = Db::name('mete_articles')
->where('id', $id)
->field('id,title,cate,image,desc,author,content,is_trans,transurl,views,likes,publisher,create_time,publish_date,update_time')
->find();
$article = Articles::alias('a')
->where('a.id', $id)
->where('a.tid', $this->getTenantId())
->leftJoin('mete_admin_user u', 'a.publisher = u.id')
->field('a.id,a.title,a.cate,a.image,a.desc,a.author,a.content,a.is_trans,a.transurl,a.views,a.likes,u.name as publisher_name,a.create_time,a.publish_date,a.update_time')
->find()
->toArray();
// 将 publisher_name 赋值给 publisher
$article['publisher'] = $article['publisher_name'] ?? '';
unset($article['publisher_name']);
if (!$article) {
return json(['code' => 404, 'msg' => '文章不存在', 'data' => null]);
@@ -234,12 +251,17 @@ class ArticleController extends BaseController
return json(['code'=>400,'msg'=>'无更新数据','data'=>null]);
}
$data['update_time'] = date('Y-m-d H:i:s');
$affected = Db::name('mete_articles')->where('id',$id)->update($data);
$article = Articles::where('id', $id)->where('tid', $this->getTenantId())->find();
if (!$article) {
return json(['code'=>404,'msg'=>'文章不存在','data'=>null]);
}
$affected = Articles::where('id',$id)->where('tid', $this->getTenantId())->update($data);
if ($affected) {
// 记录成功日志
$this->logSuccess('文章管理', '编辑文章', ['id' => $id]);
return json(['code'=>200,'msg'=>'success','data'=>null]);
}
// 记录成功日志
$this->logSuccess('文章管理', '编辑文章', ['id' => $id]);
return json(['code'=>404,'msg'=>'not found','data'=>null]);
} catch (\Exception $e) {
// 记录失败日志
@@ -256,24 +278,18 @@ class ArticleController extends BaseController
public function deleteArticle(int $id): Json
{
try {
$affected = Db::name('mete_articles')
->where('id', $id)
$affected = Articles::where('id', $id)
->where('tid', $this->getTenantId())
->update(['delete_time' => date('Y-m-d H:i:s')]);
if ($affected) {
// 记录成功日志
$this->logSuccess('文章管理', '删除文章', ['id' => $id]);
$this->logSuccess('文章管理', '删除文章', ['id' => $id]);
return json(['code' => 200, 'msg' => 'success', 'data' => null]);
}
return json(['code' => 404, 'msg' => 'not found', 'data' => null]);
} catch (\Exception $e) {
// 记录失败日志
$this->logFail('文章管理', '删除文章', $e->getMessage());
return json([
'code' => 500,
'msg' => 'fail' . $e->getMessage(),
'data' => $e->getTraceAsString()
]);
$this->logFail('文章管理', '删除文章', $e->getMessage());
return json(['code' => 500, 'msg' => 'fail' . $e->getMessage(), 'data' => null]);
}
}
@@ -290,7 +306,7 @@ class ArticleController extends BaseController
return json(['code'=>400,'msg'=>'无更新数据','data'=>null]);
}
$data['update_time'] = date('Y-m-d H:i:s');
$affected = Db::name('mete_articles')->where('id',$id)->update($data);
$affected = Articles::where('id',$id)->where('tid', $this->getTenantId())->update($data);
if ($affected) {
// 记录成功日志
$this->logSuccess('文章管理', '更新文章分类状态', ['id' => $id]);
@@ -329,8 +345,8 @@ class ArticleController extends BaseController
'publisher' => $user_id,
];
$affected = Db::name('mete_articles')
->where('id', $id)
$affected = Articles::where('id', $id)
->where('tid', $this->getTenantId())
->update($data);
if ($affected) {
@@ -366,8 +382,8 @@ class ArticleController extends BaseController
'update_time' => $currentTime
];
$affected = Db::name('mete_articles')
->where('id', $id)
$affected = Articles::where('id', $id)
->where('tid', $this->getTenantId())
->update($data);
if ($affected) {
@@ -400,8 +416,8 @@ class ArticleController extends BaseController
'recommend' => 1, // 推荐状态
];
$affected = Db::name('mete_articles')
->where('id', $id)
$affected = Articles::where('id', $id)
->where('tid', $this->getTenantId())
->update($data);
if ($affected) {
@@ -434,8 +450,8 @@ class ArticleController extends BaseController
'recommend' => 0, // 取消推荐状态
];
$affected = Db::name('mete_articles')
->where('id', $id)
$affected = Articles::where('id', $id)
->where('tid', $this->getTenantId())
->update($data);
if ($affected) {
@@ -464,8 +480,8 @@ class ArticleController extends BaseController
'top' => 1, // 置顶状态
];
$affected = Db::name('mete_articles')
->where('id', $id)
$affected = Articles::where('id', $id)
->where('tid', $this->getTenantId())
->update($data);
if ($affected) {
@@ -494,8 +510,8 @@ class ArticleController extends BaseController
'top' => 0, // 取消置顶状态
];
$affected = Db::name('mete_articles')
->where('id', $id)
$affected = Articles::where('id', $id)
->where('tid', $this->getTenantId())
->update($data);
if ($affected) {
@@ -539,9 +555,9 @@ class ArticleController extends BaseController
], 'debug');
// 构建查询条件
$query = Db::name('mete_articles')
->alias('a')
->where('a.delete_time', null);
$query = Articles::alias('a')
->where('a.delete_time', null)
->where('a.tid', $this->getTenantId());
// 关键词筛选(标题模糊匹配)
if (!empty($keyword)) {
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace app\admin\controller;
namespace app\admin\controller\Cms\Banner;
use app\admin\BaseController;
use think\exception\ValidateException;
@@ -10,7 +10,7 @@ use think\facade\Db;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\Banner;
use app\model\Cms\Banner;
class BannerController extends BaseController
{
@@ -22,7 +22,8 @@ class BannerController extends BaseController
{
try {
$banners = Banner::where('delete_time', null)
->order('sort', 'asc')
->where('tid', $this->getTenantId())
->order('sort desc, id desc')
->field('id, title, desc, url, image, sort, create_time, update_time')
->select()
->toArray();
@@ -66,6 +67,7 @@ class BannerController extends BaseController
// 准备Banner数据
$bannerData = [
'title' => $data['title'],
'tid' => $this->getTenantId(),
'desc' => $data['desc'] ?? '',
'url' => $data['url'] ?? '',
'image' => $data['image'] ?? '',
@@ -127,8 +129,9 @@ class BannerController extends BaseController
'sort|排序号' => 'integer',
]);
// 检查Banner是否存在
// 检查Banner是否存在(验证tid
$banner = Banner::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
@@ -150,10 +153,14 @@ class BannerController extends BaseController
];
// 执行更新
Banner::where('id', $id)->update($updateData);
Banner::where('id', $id)
->where('tid', $this->getTenantId())
->update($updateData);
// 获取更新后的Banner信息
$updatedBanner = Banner::where('id', $id)->find();
$updatedBanner = Banner::where('id', $id)
->where('tid', $this->getTenantId())
->find();
// 记录操作日志
$this->logSuccess('Banner管理', '更新Banner', ['id' => $id]);
@@ -187,8 +194,9 @@ class BannerController extends BaseController
public function deleteBanner(int $id)
{
try {
// 检查Banner是否存在
// 检查Banner是否存在(验证tid
$banner = Banner::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
@@ -201,6 +209,7 @@ class BannerController extends BaseController
// 逻辑删除Banner
$result = Banner::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->update([
'delete_time' => time(),
@@ -0,0 +1,186 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\Cms\Demand;
use app\admin\BaseController;
use Symfony\Component\VarDumper\VarDumper;
use think\exception\ValidateException;
use think\facade\Request;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\Cms\Demand;
use app\model\Cms\DemandCategory;
class DemandController extends BaseController
{
/**
* 获取需求列表
* @return Json
*/
public function getDemandList(): Json
{
// 查询分类
$demandList = Demand::where('delete_time', null)
->where('tid', $this->getTenantId())
->order('id', 'desc')
->select();
if (!$demandList) {
return json([
'code' => 200,
'msg' => 'success',
'list' => [],
]);
}
return json([
'code' => 200,
'msg' => 'success',
'list' => $demandList,
]);
}
/**
* 增加需求
* @return Json
*/
public function addDemand(): Json
{
try {
$data = Request::only(['title', 'desc', 'applicant', 'status']);
// 验证数据
if (empty($data['title']) || empty($data['desc'])) {
return json([
'code' => 400,
'msg' => '标题和描述不能为空',
]);
}
// 创建需求
$demand = Demand::create([
'title' => $data['title'],
'desc' => $data['desc'],
'applicant' => $data['applicant'] ?? '',
'status' => $data['status'] ?? 'pending',
'tid' => $this->getTenantId(),
]);
return json([
'code' => 200,
'msg' => '添加成功',
'data' => $demand,
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '添加失败:' . $e->getMessage(),
]);
}
}
/**
* 编辑需求
* @return Json
*/
public function editDemand(): Json
{
try {
$id = Request::param('id');
$data = Request::only(['title', 'desc', 'applicant', 'status']);
// 验证数据
if (empty($id)) {
return json([
'code' => 400,
'msg' => '需求ID不能为空',
]);
}
if (empty($data['title']) || empty($data['desc'])) {
return json([
'code' => 400,
'msg' => '标题和描述不能为空',
]);
}
// 查找需求(验证tid
$demand = Demand::where('id', $id)
->where('tid', $this->getTenantId())
->find();
if (!$demand) {
return json([
'code' => 404,
'msg' => '需求不存在',
]);
}
// 更新需求
$demand->save([
'title' => $data['title'],
'desc' => $data['desc'],
'applicant' => $data['applicant'] ?? '',
'status' => $data['status'] ?? 'pending',
]);
return json([
'code' => 200,
'msg' => '编辑成功',
'data' => $demand,
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '编辑失败:' . $e->getMessage(),
]);
}
}
/**
* 删除需求
* @return Json
*/
public function deleteDemand(): Json
{
try {
$id = Request::param('id');
// 验证数据
if (empty($id)) {
return json([
'code' => 400,
'msg' => '需求ID不能为空',
]);
}
// 查找需求(验证tid
$demand = Demand::where('id', $id)
->where('tid', $this->getTenantId())
->find();
if (!$demand) {
return json([
'code' => 404,
'msg' => '需求不存在',
]);
}
// 软删除
$demand->delete();
return json([
'code' => 200,
'msg' => '删除成功',
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '删除失败:' . $e->getMessage(),
]);
}
}
}
@@ -0,0 +1,227 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\Cms\Domain;
use app\admin\BaseController;
use think\facade\Db;
use think\Request;
use app\model\System\SystemDomainPool;
/**
* 主域名池管理控制器
*/
class DomainPoolController extends BaseController
{
/**
* 获取域名池列表
*/
public function index(Request $request)
{
$page = $request->param('page', 1, 'int');
$pageSize = $request->param('pageSize', 10, 'int');
$mainDomain = $request->param('main_domain', '');
$status = $request->param('status', '');
$where = [['delete_time', '=', null]];
if ($mainDomain) {
$where[] = ['main_domain', 'like', "%$mainDomain%"];
}
if ($status !== '' && $status !== null) {
$where[] = ['status', '=', $status];
}
$list = SystemDomainPool::where($where)
->page($page, $pageSize)
->order('id', 'desc')
->select()
->toArray();
$total = SystemDomainPool::where($where)
->count();
return json([
'code' => 200,
'msg' => 'success',
'data' => [
'list' => $list,
'total' => $total
]
]);
}
/**
* 获取启用状态的主域名列表(供租户选择)
*/
public function getEnabledDomains()
{
$list = SystemDomainPool::where('status', 1)
->where('delete_time', null)
->select()
->toArray();
return json([
'code' => 200,
'msg' => 'success',
'data' => $list
]);
}
/**
* 创建主域名
*/
public function create(Request $request)
{
$mainDomain = $request->param('main_domain', '');
if (empty($mainDomain)) {
return json([
'code' => 400,
'msg' => '主域名不能为空'
]);
}
// 检查域名是否已存在
$exists = SystemDomainPool::where('main_domain', $mainDomain)
->where('delete_time', null)
->find();
if ($exists) {
return json([
'code' => 400,
'msg' => '该域名已存在'
]);
}
$now = date('Y-m-d H:i:s');
$id = SystemDomainPool::insertGetId([
'main_domain' => $mainDomain,
'status' => 1,
'create_time' => $now,
'update_time' => $now
]);
return json([
'code' => 200,
'msg' => '创建成功',
'data' => ['id' => $id]
]);
}
/**
* 编辑主域名
*/
public function update(Request $request)
{
$id = $request->param('id', 0, 'int');
$mainDomain = $request->param('main_domain', '');
$status = $request->param('status', null);
if ($id <= 0) {
return json([
'code' => 400,
'msg' => '参数错误'
]);
}
if (empty($mainDomain)) {
return json([
'code' => 400,
'msg' => '主域名不能为空'
]);
}
// 检查域名是否与其他记录重复
$exists = SystemDomainPool::where('main_domain', $mainDomain)
->where('id', '<>', $id)
->where('delete_time', null)
->find();
if ($exists) {
return json([
'code' => 400,
'msg' => '该域名已存在'
]);
}
$data = [
'main_domain' => $mainDomain,
'update_time' => date('Y-m-d H:i:s')
];
if ($status !== null) {
$data['status'] = $status;
}
SystemDomainPool::where('id', $id)->update($data);
return json([
'code' => 200,
'msg' => '更新成功'
]);
}
/**
* 删除主域名(软删除)
*/
public function delete($id)
{
if ($id <= 0) {
return json([
'code' => 400,
'msg' => '参数错误'
]);
}
SystemDomainPool::where('id', $id)
->update([
'delete_time' => date('Y-m-d H:i:s')
]);
return json([
'code' => 200,
'msg' => '删除成功'
]);
}
/**
* 切换主域名状态
*/
public function toggleStatus(Request $request)
{
$id = $request->param('id', 0, 'int');
if ($id <= 0) {
return json([
'code' => 400,
'msg' => '参数错误'
]);
}
$domain = SystemDomainPool::where('id', $id)
->find();
if (!$domain) {
return json([
'code' => 404,
'msg' => '域名不存在'
]);
}
$newStatus = $domain['status'] == 1 ? 0 : 1;
SystemDomainPool::where('id', $id)
->update([
'status' => $newStatus,
'update_time' => date('Y-m-d H:i:s')
]);
return json([
'code' => 200,
'msg' => '状态更新成功',
'data' => ['status' => $newStatus]
]);
}
}
@@ -0,0 +1,315 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\Cms\Domain;
use app\admin\BaseController;
use think\facade\Db;
use think\Request;
use think\facade\Session;
use app\model\Tenant\TenantDomain;
use app\model\Tenant\Tenant;
use app\model\System\SystemDomainPool;
/**
* 租户域名绑定控制器
*/
class TenantDomainController extends BaseController
{
/**
* 获取租户域名列表(管理员查看所有)
*/
public function index(Request $request)
{
$page = $request->param('page', 1, 'int');
$pageSize = $request->param('pageSize', 10, 'int');
$tenantId = $request->param('tid', 0, 'int');
$status = $request->param('status', '');
$subDomain = $request->param('sub_domain', '');
$where = [['delete_time', '=', null]];
if ($tenantId > 0) {
$where[] = ['tid', '=', $tenantId];
}
if ($status !== '' && $status !== null) {
$where[] = ['status', '=', $status];
}
if ($subDomain) {
$where[] = ['sub_domain', 'like', "%$subDomain%"];
}
$list = TenantDomain::where($where)
->page($page, $pageSize)
->order('id', 'desc')
->select()
->toArray();
$total = TenantDomain::where($where)
->count();
// 获取租户名称
$tenantIds = array_column($list, 'tid');
$tenants = [];
if ($tenantIds) {
$tenantList = Tenant::whereIn('id', $tenantIds)
->select()
->toArray();
$tenants = array_column($tenantList, null, 'id');
}
// 附加租户名称
foreach ($list as &$item) {
$item['tenant_name'] = $tenants[$item['tid']]['tenant_name'] ?? '';
}
return json([
'code' => 200,
'msg' => 'success',
'data' => [
'list' => $list,
'total' => $total
]
]);
}
/**
* 获取当前租户的域名列表(租户端)
*/
public function myDomains(Request $request)
{
$tid = $request->param('tid', 0, 'int');
if ($tid <= 0) {
return json([
'code' => 400,
'msg' => '租户ID不能为空'
]);
}
$list = TenantDomain::where('tid', $tid)
->where('delete_time', null)
->order('id', 'desc')
->select()
->toArray();
return json([
'code' => 200,
'msg' => 'success',
'data' => $list
]);
}
/**
* 租户申请二级域名
*/
public function apply(Request $request)
{
$tid = $request->param('tid', 0, 'int');
$subDomain = $request->param('sub_domain', '');
$mainDomain = $request->param('main_domain', '');
if ($tid <= 0) {
return json([
'code' => 400,
'msg' => '租户ID不能为空'
]);
}
// 检查该租户是否已有域名数据
$hasDomain = TenantDomain::where('tid', $tid)
->where('delete_time', null)
->find();
if ($hasDomain) {
return json([
'code' => 400,
'msg' => '该租户已有域名,请删除后再次申请'
]);
}
if (empty($subDomain)) {
return json([
'code' => 400,
'msg' => '二级域名前缀不能为空'
]);
}
if (empty($mainDomain)) {
return json([
'code' => 400,
'msg' => '请选择主域名'
]);
}
// 验证域名格式(只能包含字母、数字、连字符)
if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$/', $subDomain)) {
return json([
'code' => 400,
'msg' => '二级域名前缀格式不正确'
]);
}
// 检查主域名是否存在且启用
$mainDomainInfo = SystemDomainPool::where('main_domain', $mainDomain)
->where('status', 1)
->where('delete_time', null)
->find();
if (!$mainDomainInfo) {
return json([
'code' => 400,
'msg' => '主域名不存在或已禁用'
]);
}
// 检查二级域名是否已被使用
$exists = TenantDomain::where('sub_domain', $subDomain)
->where('main_domain', $mainDomain)
->where('delete_time', null)
->find();
if ($exists) {
return json([
'code' => 400,
'msg' => '该二级域名已被使用'
]);
}
$fullDomain = $subDomain . '.' . $mainDomain;
$now = date('Y-m-d H:i:s');
$id = TenantDomain::insertGetId([
'tid' => $tid,
'sub_domain' => $subDomain,
'main_domain' => $mainDomain,
'full_domain' => $fullDomain,
'status' => 0, // 审核中
'create_time' => $now,
'update_time' => $now
]);
return json([
'code' => 200,
'msg' => '申请提交成功,等待审核',
'data' => ['id' => $id]
]);
}
/**
* 审核租户域名(通过/拒绝)
*/
public function audit(Request $request)
{
$id = $request->param('id', 0, 'int');
$action = $request->param('action', ''); // 'approve' 或 'reject'
if ($id <= 0) {
return json([
'code' => 400,
'msg' => '参数错误'
]);
}
$domain = TenantDomain::where('id', $id)
->find();
if (!$domain) {
return json([
'code' => 404,
'msg' => '域名不存在'
]);
}
if ($domain['status'] != 0) {
return json([
'code' => 400,
'msg' => '该域名已审核过了'
]);
}
$newStatus = $action === 'approve' ? 1 : 2; // 1-已生效 2-已拒绝
TenantDomain::where('id', $id)
->update([
'status' => $newStatus,
'update_time' => date('Y-m-d H:i:s')
]);
$msg = $action === 'approve' ? '审核通过' : '已拒绝';
return json([
'code' => 200,
'msg' => $msg
]);
}
/**
* 禁用/启用租户域名
*/
public function toggleStatus(Request $request)
{
$id = $request->param('id', 0, 'int');
if ($id <= 0) {
return json([
'code' => 400,
'msg' => '参数错误'
]);
}
$domain = TenantDomain::where('id', $id)
->find();
if (!$domain) {
return json([
'code' => 404,
'msg' => '域名不存在'
]);
}
// 只有已生效的域名才能被禁用
if ($domain['status'] != 1) {
return json([
'code' => 400,
'msg' => '只有已生效的域名才能被禁用'
]);
}
// 切换为禁用状态
TenantDomain::where('id', $id)
->update([
'status' => 2,
'update_time' => date('Y-m-d H:i:s')
]);
return json([
'code' => 200,
'msg' => '已禁用'
]);
}
/**
* 删除租户域名(软删除)
*/
public function delete($id)
{
if ($id <= 0) {
return json([
'code' => 400,
'msg' => '参数错误'
]);
}
TenantDomain::where('id', $id)
->update([
'delete_time' => date('Y-m-d H:i:s')
]);
return json([
'code' => 200,
'msg' => '删除成功'
]);
}
}
@@ -0,0 +1,280 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\Cms\Friendlink;
use app\admin\BaseController;
use think\response\Json;
use app\model\Cms\Friendlink;
use think\exception\ValidateException;
/**
* 友情链接管理控制器
*/
class FriendlinkController extends BaseController
{
/**
* 获取友情链接列表
* @return Json
*/
public function getList(): Json
{
try {
$page = (int)$this->request->param('page', 1);
$limit = (int)$this->request->param('limit', 10);
$keyword = $this->request->param('keyword', '');
$status = $this->request->param('status', '');
$query = Friendlink::where('delete_time', null)
->where('tid', $this->getTenantId());
// 关键词搜索
if (!empty($keyword)) {
$query->where('link_name', 'like', '%' . $keyword . '%');
}
// 状态筛选
if ($status !== '') {
$query->where('status', (int)$status);
}
$total = $query->count();
$list = $query->order('sort', 'asc')
->order('id', 'desc')
->page($page, $limit)
->select()
->toArray();
return json([
'code' => 200,
'msg' => 'success',
'data' => [
'list' => $list,
'total' => $total
]
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage(),
'data' => []
]);
}
}
/**
* 获取所有友情链接(用于下拉选择)
* @return Json
*/
public function getAll(): Json
{
try {
$list = Friendlink::where('delete_time', null)
->where('tid', $this->getTenantId())
->where('status', 1)
->order('sort', 'asc')
->field('id,link_name')
->select()
->toArray();
return json([
'code' => 200,
'msg' => 'success',
'data' => $list
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage(),
'data' => []
]);
}
}
/**
* 添加友情链接
* @return Json
*/
public function add(): Json
{
try {
$data = $this->request->param();
// 验证参数
$this->validate($data, [
'link_name|链接名称' => 'require|max:100',
'link_url|链接地址' => 'require|url|max:255',
'sort|排序' => 'integer',
'status|状态' => 'in:0,1'
]);
$friendlink = new Friendlink();
$friendlink->tid = $this->getTenantId();
$friendlink->link_name = $data['link_name'];
$friendlink->link_url = $data['link_url'];
$friendlink->link_logo = $data['link_logo'] ?? '';
$friendlink->description = $data['description'] ?? '';
$friendlink->sort = $data['sort'] ?? 0;
$friendlink->status = $data['status'] ?? 1;
$friendlink->create_time = date('Y-m-d H:i:s');
$friendlink->save();
$this->logSuccess('友情链接', '添加链接', ['id' => $friendlink->id]);
return json([
'code' => 200,
'msg' => '添加成功',
'data' => $friendlink->toArray()
]);
} catch (ValidateException $e) {
return json([
'code' => 400,
'msg' => $e->getError()
]);
} catch (\Exception $e) {
$this->logFail('友情链接', '添加链接', $e->getMessage());
return json([
'code' => 500,
'msg' => '添加失败:' . $e->getMessage()
]);
}
}
/**
* 更新友情链接
* @param int $id
* @return Json
*/
public function update(int $id): Json
{
try {
$data = $this->request->param();
$friendlink = Friendlink::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
if (!$friendlink) {
return json([
'code' => 404,
'msg' => '链接不存在'
]);
}
// 验证参数
if (isset($data['link_name'])) {
$this->validate($data, [
'link_name|链接名称' => 'require|max:100'
]);
}
if (isset($data['link_url'])) {
$this->validate($data, [
'link_url|链接地址' => 'require|url|max:255'
]);
}
if (isset($data['link_name'])) $friendlink->link_name = $data['link_name'];
if (isset($data['link_url'])) $friendlink->link_url = $data['link_url'];
if (isset($data['link_logo'])) $friendlink->link_logo = $data['link_logo'];
if (isset($data['description'])) $friendlink->description = $data['description'];
if (isset($data['sort'])) $friendlink->sort = $data['sort'];
if (isset($data['status'])) $friendlink->status = $data['status'];
$friendlink->update_time = date('Y-m-d H:i:s');
$friendlink->save();
$this->logSuccess('友情链接', '更新链接', ['id' => $id]);
return json([
'code' => 200,
'msg' => '更新成功',
'data' => $friendlink->toArray()
]);
} catch (ValidateException $e) {
return json([
'code' => 400,
'msg' => $e->getError()
]);
} catch (\Exception $e) {
$this->logFail('友情链接', '更新链接', $e->getMessage());
return json([
'code' => 500,
'msg' => '更新失败:' . $e->getMessage()
]);
}
}
/**
* 删除友情链接
* @param int $id
* @return Json
*/
public function delete(int $id): Json
{
try {
$friendlink = Friendlink::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
if (!$friendlink) {
return json([
'code' => 404,
'msg' => '链接不存在'
]);
}
$friendlink->delete();
$this->logSuccess('友情链接', '删除链接', ['id' => $id]);
return json([
'code' => 200,
'msg' => '删除成功'
]);
} catch (\Exception $e) {
$this->logFail('友情链接', '删除链接', $e->getMessage());
return json([
'code' => 500,
'msg' => '删除失败:' . $e->getMessage()
]);
}
}
/**
* 批量删除友情链接
* @return Json
*/
public function batchDelete(): Json
{
try {
$ids = $this->request->param('ids', []);
if (empty($ids)) {
return json([
'code' => 400,
'msg' => '请选择要删除的链接'
]);
}
Friendlink::whereIn('id', $ids)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->update(['delete_time' => date('Y-m-d H:i:s')]);
$this->logSuccess('友情链接', '批量删除链接', ['ids' => $ids]);
return json([
'code' => 200,
'msg' => '批量删除成功'
]);
} catch (\Exception $e) {
$this->logFail('友情链接', '批量删除链接', $e->getMessage());
return json([
'code' => 500,
'msg' => '批量删除失败:' . $e->getMessage()
]);
}
}
}
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace app\admin\controller;
namespace app\admin\controller\Cms\FrontMenu;
use app\admin\BaseController;
use think\exception\ValidateException;
@@ -10,11 +10,11 @@ use think\facade\Db;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\FrontMenu;
use app\model\Cms\FrontMenu;
class FrontMenuController extends BaseController
{
/**
/**
* 获取前端导航接口
* @return \think\response\Json
*/
@@ -23,6 +23,7 @@ class FrontMenuController extends BaseController
try {
// 获取所有未删除的菜单
$frontMenus = FrontMenu::where('delete_time', null)
->where('tid', $this->getTenantId())
->field('id,pid,title,type,image,path,component_path,sort,desc')
->order('sort', 'desc')
->select()
@@ -69,6 +70,7 @@ class FrontMenuController extends BaseController
// 准备菜单数据
$menuData = [
'pid' => $data['pid'] ?? 0,
'tid' => $this->getTenantId(),
'title' => $data['title'],
'type' => $data['type'],
'sort' => $data['sort'] ?? 0,
@@ -130,6 +132,7 @@ class FrontMenuController extends BaseController
// 准备更新数据
$updateData = [
'title' => $data['title'],
'tid' => $this->getTenantId(),
'pid' => $data['pid'] ?? null,
'type' => $data['type'],
'path' => $data['path'] ?? null,
@@ -163,7 +166,7 @@ class FrontMenuController extends BaseController
} catch (DbException $e) {
// 记录失败日志
$this->logFail('前端导航管理', '更新前端导航', $e->getMessage());
return json([
return json([
'code' => 500,
'msg' => 'fail' . $e->getMessage(),
'data' => $e->getTraceAsString()
@@ -176,7 +179,8 @@ class FrontMenuController extends BaseController
{
try {
// 检查是否有子前端导航
$hasChildren = FrontMenu::where('pid', $id)
$hasChildren = FrontMenu::where('pid', $id)
->where('tid', $this->getTenantId())
->where('pid', $id)
->where('delete_time', null)
->count() > 0;
@@ -192,6 +196,7 @@ $hasChildren = FrontMenu::where('pid', $id)
$result = FrontMenu::where('id', $id)
->where('id', $id)
->where('delete_time', null)
->where('tid', $this->getTenantId())
->update([
'delete_time' => date('Y-m-d H:i:s'),
'update_time' => date('Y-m-d H:i:s')
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace app\admin\controller;
namespace app\admin\controller\Cms\OnePage;
use app\admin\BaseController;
use think\exception\ValidateException;
@@ -10,7 +10,7 @@ use think\facade\Db;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\OnePage;
use app\model\Cms\OnePage;
class OnePageController extends BaseController
{
@@ -22,6 +22,7 @@ class OnePageController extends BaseController
{
try {
$onePages = OnePage::where('delete_time', null)
->where('tid', $this->getTenantId())
->order('sort', 'asc')
->field('id, title, content, path, sort, status, create_time, update_time')
->select()
@@ -69,6 +70,7 @@ class OnePageController extends BaseController
// 检查路由是否已存在
$exists = OnePage::where('path', $data['path'])
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
if ($exists) {
@@ -85,6 +87,7 @@ class OnePageController extends BaseController
'path' => $data['path'],
'sort' => $data['sort'] ?? 0,
'status' => $data['status'] ?? 1,
'tid' => $this->getTenantId(),
'create_time' => date('Y-m-d H:i:s'),
];
@@ -150,8 +153,9 @@ class OnePageController extends BaseController
]);
}
// 检查单页是否存在
// 检查单页是否存在(验证tid
$onePage = OnePage::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
@@ -165,6 +169,7 @@ class OnePageController extends BaseController
// 检查路由是否被其他单页使用
$exists = OnePage::where('path', $data['path'])
->where('id', '<>', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
if ($exists) {
@@ -222,8 +227,9 @@ class OnePageController extends BaseController
public function deleteOnePage(int $id)
{
try {
// 检查单页是否存在
// 检查单页是否存在(验证tid
$onePage = OnePage::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
@@ -236,6 +242,7 @@ class OnePageController extends BaseController
// 逻辑删除单页
$result = OnePage::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->update([
'delete_time' => date('Y-m-d H:i:s'),
@@ -0,0 +1,188 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\Cms\Products;
use app\admin\BaseController;
use think\response\Json;
use app\model\Cms\Products;
use think\exception\ValidateException;
/**
* 特色产品管理控制器
*/
class ProductsController extends BaseController
{
/**
* 获取特色产品列表
* @return Json
*/
public function productsList(): Json
{
try {
$page = (int)$this->request->param('page', 1);
$limit = (int)$this->request->param('limit', 10);
$keyword = $this->request->param('keyword', '');
$query = Products::where('delete_time', null)
->where('tid', $this->getTenantId());
// 关键词搜索
if (!empty($keyword)) {
$query->where('title', 'like', '%' . $keyword . '%');
}
$total = $query->count();
$list = $query->order('sort', 'asc')
->order('id', 'desc')
->page($page, $limit)
->select()
->toArray();
return json([
'code' => 200,
'msg' => 'success',
'data' => [
'list' => $list,
'total' => $total
]
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage(),
'data' => []
]);
}
}
/**
* 添加特色产品
* @return Json
*/
public function addProducts(): Json
{
try {
$data = $this->request->param();
$product = new Products();
$product->tid = $this->getTenantId();
$product->title = $data['title'];
$product->url = $data['url'];
$product->thumb = $data['thumb'] ?? '';
$product->desc = $data['desc'] ?? '';
$product->content = $data['content'] ?? '';
$product->sort = $data['sort'] ?? 0;
$product->create_time = date('Y-m-d H:i:s');
$product->save();
$this->logSuccess('特色产品', '添加产品', ['id' => $product->id]);
return json([
'code' => 200,
'msg' => '添加成功',
'data' => $product->toArray()
]);
} catch (ValidateException $e) {
return json([
'code' => 400,
'msg' => $e->getError()
]);
} catch (\Exception $e) {
$this->logFail('特色产品', '添加产品', $e->getMessage());
return json([
'code' => 500,
'msg' => '添加失败:' . $e->getMessage()
]);
}
}
/**
* 更新特色产品
* @param int $id
* @return Json
*/
public function editProducts(int $id): Json
{
try {
$data = $this->request->param();
$product = Products::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
if (!$product) {
return json([
'code' => 404,
'msg' => '产品不存在'
]);
}
if (isset($data['title'])) $product->title = $data['title'];
if (isset($data['url'])) $product->url = $data['url'];
if (isset($data['thumb'])) $product->thumb = $data['thumb'];
if (isset($data['desc'])) $product->desc = $data['desc'];
if (isset($data['sort'])) $product->sort = $data['sort'];
$product->update_time = date('Y-m-d H:i:s');
$product->save();
$this->logSuccess('特色产品', '更新产品', ['id' => $id]);
return json([
'code' => 200,
'msg' => '更新成功',
'data' => $product->toArray()
]);
} catch (ValidateException $e) {
return json([
'code' => 400,
'msg' => $e->getError()
]);
} catch (\Exception $e) {
$this->logFail('特色产品', '更新产品', $e->getMessage());
return json([
'code' => 500,
'msg' => '更新失败:' . $e->getMessage()
]);
}
}
/**
* 删除特色产品
* @param int $id
* @return Json
*/
public function deleteProducts(int $id): Json
{
try {
$product = Products::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
if (!$product) {
return json([
'code' => 404,
'msg' => '产品不存在'
]);
}
$product->delete();
$this->logSuccess('特色产品', '删除产品', ['id' => $id]);
return json([
'code' => 200,
'msg' => '删除成功'
]);
} catch (\Exception $e) {
$this->logFail('特色产品', '删除产品', $e->getMessage());
return json([
'code' => 500,
'msg' => '删除失败:' . $e->getMessage()
]);
}
}
}
@@ -0,0 +1,206 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\Cms\Products;
use app\admin\BaseController;
use app\model\Cms\ProductsTypes;
use think\exception\ValidateException;
use think\response\Json;
/**
* 产品分类管理控制器
*/
class ProductsTypesController extends BaseController
{
/**
* 获取产品分类列表
* @return Json
*/
public function productsTypesList(): Json
{
try {
$page = (int)$this->request->param('page', 1);
$limit = (int)$this->request->param('limit', 10);
$keyword = (string)$this->request->param('keyword', '');
$query = ProductsTypes::where('delete_time', null)
->where('tid', $this->getTenantId());
if ($keyword !== '') {
$query->where('title', 'like', '%' . $keyword . '%');
}
$total = $query->count();
$list = $query->order('sort', 'asc')
->order('id', 'desc')
->page($page, $limit)
->select()
->toArray();
return json([
'code' => 200,
'msg' => 'success',
'data' => [
'list' => $list,
'total' => $total
]
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage(),
'data' => [
'list' => [],
'total' => 0
]
]);
}
}
/**
* 添加产品分类
* @return Json
*/
public function addProductsTypes(): Json
{
try {
$data = $this->request->param();
$title = (string)($data['title'] ?? '');
if ($title === '') {
return json([
'code' => 400,
'msg' => '分类名称不能为空'
]);
}
$type = new ProductsTypes();
$type->tid = $this->getTenantId();
$type->pid = isset($data['pid']) ? (int)$data['pid'] : 0;
$type->title = $title;
$type->desc = (string)($data['desc'] ?? '');
$type->sort = isset($data['sort']) ? (int)$data['sort'] : 0;
$type->create_time = date('Y-m-d H:i:s');
$type->update_time = date('Y-m-d H:i:s');
$type->save();
$this->logSuccess('产品分类管理', '添加分类', ['id' => $type->id]);
return json([
'code' => 200,
'msg' => '添加成功',
'data' => $type->toArray()
]);
} catch (ValidateException $e) {
return json([
'code' => 400,
'msg' => $e->getError()
]);
} catch (\Exception $e) {
$this->logFail('产品分类管理', '添加分类', $e->getMessage());
return json([
'code' => 500,
'msg' => '添加失败:' . $e->getMessage()
]);
}
}
/**
* 更新产品分类
* @param int $id
* @return Json
*/
public function editProductsTypes(int $id): Json
{
try {
$data = $this->request->param();
$type = ProductsTypes::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
if (!$type) {
return json([
'code' => 404,
'msg' => '分类不存在'
]);
}
if (isset($data['title'])) $type->title = (string)$data['title'];
if (isset($data['pid'])) $type->pid = (int)$data['pid'];
if (isset($data['desc'])) $type->desc = (string)$data['desc'];
if (isset($data['sort'])) $type->sort = (int)$data['sort'];
if ((string)$type->title === '') {
return json([
'code' => 400,
'msg' => '分类名称不能为空'
]);
}
$type->update_time = date('Y-m-d H:i:s');
$type->save();
$this->logSuccess('产品分类管理', '更新分类', ['id' => $id]);
return json([
'code' => 200,
'msg' => '更新成功',
'data' => $type->toArray()
]);
} catch (ValidateException $e) {
return json([
'code' => 400,
'msg' => $e->getError()
]);
} catch (\Exception $e) {
$this->logFail('产品分类管理', '更新分类', $e->getMessage());
return json([
'code' => 500,
'msg' => '更新失败:' . $e->getMessage()
]);
}
}
/**
* 删除产品分类(软删除)
* @param int $id
* @return Json
*/
public function deleteProductsTypes(int $id): Json
{
try {
$type = ProductsTypes::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
if (!$type) {
return json([
'code' => 404,
'msg' => '分类不存在'
]);
}
$type->delete();
$this->logSuccess('产品分类管理', '删除分类', ['id' => $id]);
return json([
'code' => 200,
'msg' => '删除成功'
]);
} catch (\Exception $e) {
$this->logFail('产品分类管理', '删除分类', $e->getMessage());
return json([
'code' => 500,
'msg' => '删除失败:' . $e->getMessage()
]);
}
}
}
@@ -0,0 +1,188 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\Cms\Services;
use app\admin\BaseController;
use think\response\Json;
use app\model\Cms\Services;
use think\exception\ValidateException;
/**
* 特色服务管理控制器
*/
class ServicesController extends BaseController
{
/**
* 获取特色服务列表
* @return Json
*/
public function servicesList(): Json
{
try {
$page = (int)$this->request->param('page', 1);
$limit = (int)$this->request->param('limit', 10);
$keyword = $this->request->param('keyword', '');
$query = Services::where('delete_time', null)
->where('tid', $this->getTenantId());
// 关键词搜索
if (!empty($keyword)) {
$query->where('name', 'like', '%' . $keyword . '%');
}
$total = $query->count();
$list = $query->order('sort', 'asc')
->order('id', 'desc')
->page($page, $limit)
->select()
->toArray();
return json([
'code' => 200,
'msg' => 'success',
'data' => [
'list' => $list,
'total' => $total
]
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage(),
'data' => []
]);
}
}
/**
* 添加特色服务
* @return Json
*/
public function addServices(): Json
{
try {
$data = $this->request->param();
$service = new Services();
$service->tid = $this->getTenantId();
$service->title = $data['title'];
$service->url = $data['url'];
$service->thumb = $data['thumb'] ?? '';
$service->desc = $data['desc'] ?? '';
$service->sort = $data['sort'] ?? 0;
$service->create_time = date('Y-m-d H:i:s');
$service->save();
$this->logSuccess('特色服务', '添加服务', ['id' => $service->id]);
return json([
'code' => 200,
'msg' => '添加成功',
'data' => $service->toArray()
]);
} catch (ValidateException $e) {
return json([
'code' => 400,
'msg' => $e->getError()
]);
} catch (\Exception $e) {
$this->logFail('特色服务', '添加服务', $e->getMessage());
return json([
'code' => 500,
'msg' => '添加失败:' . $e->getMessage()
]);
}
}
/**
* 更新特色服务
* @param int $id
* @return Json
*/
public function editServices(int $id): Json
{
try {
$data = $this->request->param();
$service = Services::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
if (!$service) {
return json([
'code' => 404,
'msg' => '服务不存在'
]);
}
if (isset($data['title'])) $service->title = $data['title'];
if (isset($data['url'])) $service->url = $data['url'];
if (isset($data['thumb'])) $service->thumb = $data['thumb'];
if (isset($data['desc'])) $service->desc = $data['desc'];
if (isset($data['sort'])) $service->sort = $data['sort'];
$service->update_time = date('Y-m-d H:i:s');
$service->save();
$this->logSuccess('特色服务', '更新服务', ['id' => $id]);
return json([
'code' => 200,
'msg' => '更新成功',
'data' => $service->toArray()
]);
} catch (ValidateException $e) {
return json([
'code' => 400,
'msg' => $e->getError()
]);
} catch (\Exception $e) {
$this->logFail('特色服务', '更新服务', $e->getMessage());
return json([
'code' => 500,
'msg' => '更新失败:' . $e->getMessage()
]);
}
}
/**
* 删除特色服务
* @param int $id
* @return Json
*/
public function deleteServices(int $id): Json
{
try {
$service = Services::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
if (!$service) {
return json([
'code' => 404,
'msg' => '服务不存在'
]);
}
$service->delete();
$this->logSuccess('特色服务', '删除服务', ['id' => $id]);
return json([
'code' => 200,
'msg' => '删除成功'
]);
} catch (\Exception $e) {
$this->logFail('特色服务', '删除服务', $e->getMessage());
return json([
'code' => 500,
'msg' => '删除失败:' . $e->getMessage()
]);
}
}
}
@@ -0,0 +1,141 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\Cms\Theme;
use app\admin\BaseController;
use app\service\ThemeService;
use think\facade\Request;
use app\model\Cms\TemplateSiteConfig;
/**
* 模板管理控制器
*/
class ThemeController extends BaseController
{
private ThemeService $themeService;
public function __construct(\think\App $app)
{
parent::__construct($app);
$this->themeService = new ThemeService();
}
/**
* 获取模板列表(后台管理)
* @return \think\response\Json
*/
public function index()
{
$tid = $this->getTenantId();
$themes = $this->themeService->getThemeList();
$currentTheme = $this->themeService->getCurrentTheme($tid);
$theme_useing = TemplateSiteConfig::where('tid', $tid)
->where('key', 'current_theme')
->value('value');
return json([
'code' => 200,
'msg' => 'success',
'data' => [
'list' => $themes,
'currentTheme' => $currentTheme
]
]);
}
/**
* 切换模板
* @return \think\response\Json
*/
public function switch()
{
$tid = Request::post('tid', 0, 'int');
$themeKey = Request::post('theme_key', '');
if (empty($tid) || $tid == 0) {
return json([
'code' => 401,
'msg' => '未获取到租户ID,请先选择租户'
]);
}
if (empty($themeKey)) {
return json([
'code' => 400,
'msg' => '模板标识不能为空'
]);
}
$result = $this->themeService->switchTheme($tid, $themeKey);
if ($result) {
return json([
'code' => 200,
'msg' => '切换成功'
]);
}
return json([
'code' => 400,
'msg' => '切换失败,模板不存在'
]);
}
/**
* 获取模板字段数据
* @return \think\response\Json
*/
public function getData()
{
$tid = Request::get('tid', 0, 'int');
$themeKey = Request::get('theme_key', '');
$themeData = $this->themeService->getThemeData($tid, $themeKey ?: null);
return json([
'code' => 200,
'msg' => 'success',
'data' => $themeData
]);
}
/**
* 保存模板字段数据
* @return \think\response\Json
*/
public function saveData()
{
$tid = Request::post('tid', 0, 'int');
$themeKey = Request::post('theme_key', '');
$fieldKey = Request::post('field_key', '');
$fieldValue = Request::post('field_value', '');
if (empty($themeKey) || empty($fieldKey)) {
return json([
'code' => 400,
'msg' => '参数不完整'
]);
}
$result = $this->themeService->saveThemeField($tid, $themeKey, $fieldKey, $fieldValue);
if ($result) {
return json([
'code' => 200,
'msg' => '保存成功'
]);
}
return json([
'code' => 400,
'msg' => '保存失败'
]);
}
}
@@ -0,0 +1,144 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\Erp;
use app\admin\BaseController;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\Erp\Employee;
use app\model\System\AdminUser;
class EmployeeController extends BaseController
{
/**
* 获取员工列表
*/
public function getEmployee()
{
$tid = $this->getTenantId();
if (!$tid) {
return json(['code' => 403, 'msg' => '无法获取租户信息']);
}
$list = Employee::where('delete_time', null)
->where('tid', $tid)
->select()
->toArray();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $list
]);
}
/**
* 获取员工详情
*/
public function getEmployeeDetail($id)
{
$tid = $this->getTenantId();
if (!$tid) {
return json(['code' => 403, 'msg' => '无法获取租户信息']);
}
$detail = Employee::where('id', $id)
->where('delete_time', null)
->where('tid', $tid)
->find()
->toArray();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $detail
]);
}
/**
* 创建员工
*/
public function createEmployee()
{
$tid = $this->getTenantId();
if (!$tid) {
return json(['code' => 403, 'msg' => '无法获取租户信息']);
}
$data = $this->request->post();
$data['tid'] = $tid;
$employee = Employee::create($data);
if ($employee) {
return json([
'code' => 200,
'msg' => '创建成功',
'data' => $employee
]);
} else {
return json([
'code' => 500,
'msg' => '创建失败',
]);
}
}
/**
* 编辑员工
*/
public function editEmployee($id)
{
$tid = $this->getTenantId();
if (!$tid) {
return json(['code' => 403, 'msg' => '无法获取租户信息']);
}
$data = $this->request->post();
unset($data['tid']); // 不允许修改租户ID
$employee = Employee::where('id', $id)
->where('tid', $tid)
->update($data);
if ($employee !== false) {
return json([
'code' => 200,
'msg' => '编辑成功',
'data' => $employee
]);
} else {
return json([
'code' => 500,
'msg' => '编辑失败',
]);
}
}
/**
* 删除员工
*/
public function deleteEmployee($id)
{
$tid = $this->getTenantId();
if (!$tid) {
return json(['code' => 403, 'msg' => '无法获取租户信息']);
}
$employee = Employee::where('id', $id)
->where('tid', $tid)
->update(['delete_time' => date('Y-m-d H:i:s')]);
if ($employee) {
return json([
'code' => 200,
'msg' => '删除成功',
]);
} else {
return json([
'code' => 500,
'msg' => '删除失败',
]);
}
}
}
@@ -0,0 +1,192 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\Erp;
use app\admin\BaseController;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\Erp\Organization;
use app\model\System\AdminUser;
class OrganizationController extends BaseController
{
/**
* 获取组织机构列表
*/
public function getOrganization()
{
$tid = $this->getTenantId();
if (!$tid) {
return json(['code' => 403, 'msg' => '无法获取租户信息']);
}
$list = Organization::where('delete_time', null)
->where('tid', $tid)
->select()
->toArray();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $list
]);
}
/**
* 获取组织机构详情
*/
public function getOrganizationDetail($id)
{
$tid = $this->getTenantId();
if (!$tid) {
return json(['code' => 403, 'msg' => '无法获取租户信息']);
}
$detail = Organization::where('id', $id)
->where('delete_time', null)
->where('tid', $tid)
->find()
->toArray();
$detail['leader_name'] = AdminUser::where('id', $detail['leader_id'])->value('name');
$detail['parent_name'] = Organization::where('id', $detail['parent_id'])->value('org_name');
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $detail
]);
}
/**
* 创建组织机构
*/
public function createOrganization()
{
$tid = $this->getTenantId();
if (!$tid) {
return json(['code' => 403, 'msg' => '无法获取租户信息']);
}
$data = $this->request->post();
$data['tid'] = $tid;
$organization = Organization::create($data);
if ($organization) {
return json([
'code' => 200,
'msg' => '创建成功',
'data' => $organization
]);
} else {
return json([
'code' => 500,
'msg' => '创建失败',
]);
}
}
/**
* 编辑组织机构
*/
public function editOrganization($id)
{
$tid = $this->getTenantId();
if (!$tid) {
return json(['code' => 403, 'msg' => '无法获取租户信息']);
}
$data = $this->request->post();
unset($data['tid']); // 不允许修改租户ID
$organization = Organization::where('id', $id)
->where('tid', $tid)
->update($data);
if ($organization !== false) {
return json([
'code' => 200,
'msg' => '编辑成功',
'data' => $organization
]);
} else {
return json([
'code' => 500,
'msg' => '编辑失败',
]);
}
}
/**
* 删除组织机构
*/
public function deleteOrganization($id)
{
$tid = $this->getTenantId();
if (!$tid) {
return json(['code' => 403, 'msg' => '无法获取租户信息']);
}
$organization = Organization::where('id', $id)
->where('tid', $tid)
->update(['delete_time' => date('Y-m-d H:i:s')]);
if ($organization) {
return json([
'code' => 200,
'msg' => '删除成功',
]);
} else {
return json([
'code' => 500,
'msg' => '删除失败',
]);
}
}
/**
* 获取企业单位
*/
public function getCompanys()
{
$tid = $this->getTenantId();
if (!$tid) {
return json(['code' => 403, 'msg' => '无法获取租户信息']);
}
$where = [['delete_time', '=', null], ['is_company', '=', 1], ['tid', '=', $tid]];
$list = Organization::where($where)->select()->toArray();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $list
]);
}
/**
* 获取部门
*/
public function getDepartments()
{
$tid = $this->getTenantId();
if (!$tid) {
return json(['code' => 403, 'msg' => '无法获取租户信息']);
}
$parentId = input('parent_id/d', 0);
$where = [['delete_time', '=', null], ['is_company', '=', 0], ['tid', '=', $tid]];
if ($parentId > 0) {
$where[] = ['parent_id', '=', $parentId];
}
$list = Organization::where($where)->select()->toArray();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $list
]);
}
}
+1
View File
@@ -4,6 +4,7 @@ declare (strict_types = 1);
namespace app\admin\controller;
use app\admin\BaseController;
class Index extends BaseController
{
public function index()
+692 -55
View File
@@ -9,11 +9,19 @@ use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Cache;
use think\response\Json;
use app\model\AdminUser;
use app\service\JwtService;
use app\model\System\AdminUser;
use app\model\System\AdminUserGroup;
use app\model\System\SystemSiteSettings;
use app\model\Tenant\Tenant;
use app\model\System\OperationLog;
class LoginController extends BaseController
{
private const SMS_CODE_TTL_SECONDS = 300; // 5分钟
private const SMS_CODE_RESEND_SECONDS = 60; // 60秒可重发
private function generateToken($userInfo): string
{
return JwtService::generateToken($userInfo);
@@ -24,67 +32,176 @@ class LoginController extends BaseController
return JwtService::verifyToken($token);
}
/**
* 登录接口
* @return Json
*/
public function login(): Json
private function getSiteSettingValue(string $label): string
{
$data = $this->request->param();
$row = SystemSiteSettings::where('label', $label)
->where('delete_time', null)
->order('id', 'asc')
->find();
return $row ? (string)$row['value'] : '';
}
if (isset($data['email'])) {
$data['account'] = $data['email'];
} elseif (isset($data['phone'])) {
$data['account'] = $data['phone'];
private function smsCodeCacheKey(string $scene, string $tenantName, string $account, string $phone): string
{
return 'sms_code:' . md5($scene . '|' . $tenantName . '|' . $account . '|' . $phone);
}
private function smsCodeThrottleKey(string $scene, string $tenantName, string $account, string $phone): string
{
return 'sms_code_throttle:' . md5($scene . '|' . $tenantName . '|' . $account . '|' . $phone);
}
private function postJson(string $url, array $payload, array $headers): array
{
$body = json_encode($payload, JSON_UNESCAPED_UNICODE);
if ($body === false) {
return ['ok' => false, 'error' => 'json_encode failed'];
}
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => implode("\r\n", $headers),
'content' => $body,
'timeout' => 10,
'ignore_errors' => true,
],
]);
$respBody = @file_get_contents($url, false, $context);
$status = 0;
if (!empty($http_response_header) && is_array($http_response_header)) {
foreach ($http_response_header as $line) {
if (preg_match('#^HTTP/\\S+\\s+(\\d+)#', $line, $m)) {
$status = (int)$m[1];
break;
}
}
}
if ($respBody === false) {
$last = error_get_last();
return ['ok' => false, 'status' => $status, 'error' => (string)($last['message'] ?? 'http request failed')];
}
$decoded = json_decode((string)$respBody, true);
if (!is_array($decoded)) {
$decoded = [];
}
return [
'ok' => $status >= 200 && $status < 300,
'status' => $status,
'body' => (string)$respBody,
'json' => $decoded,
];
}
private function sendSmsCodeInternal(string $scene, string $tenantName, string $account, string $phone): Json
{
$throttleKey = $this->smsCodeThrottleKey($scene, $tenantName, $account, $phone);
if (Cache::has($throttleKey)) {
return json(['code' => 429, 'msg' => '验证码发送过于频繁,请稍后再试']);
}
$backendUrl = $this->getSiteSettingValue('backendUrl');
$apiKey = $this->getSiteSettingValue('apiKey');
if ($backendUrl === '' || $apiKey === '') {
return json(['code' => 500, 'msg' => '短信网关配置缺失,请联系管理员']);
}
$code = (string)random_int(100000, 999999);
$sceneTextMap = [
'register' => '注册',
'reset' => '找回密码',
'login' => '登录',
];
$sceneText = $sceneTextMap[$scene] ?? '验证';
$content = "【云泽网】{$sceneText}验证码:{$code}5分钟内有效。";
$enqueueUrl = rtrim($backendUrl, '/') . '/api/v1/business/outbound-tasks';
$resp = $this->postJson($enqueueUrl, [
'phone' => $phone,
'content' => $content,
], [
'X-Api-Key: ' . $apiKey,
'Content-Type: application/json; charset=utf-8',
'Accept: application/json',
]);
if (empty($resp['ok'])) {
return json([
'code' => 500,
'msg' => '验证码短信发送失败',
'detail' => [
'http_status' => $resp['status'] ?? null,
'body_preview' => isset($resp['body']) ? substr((string)$resp['body'], 0, 200) : '',
]
]);
}
$codeKey = $this->smsCodeCacheKey($scene, $tenantName, $account, $phone);
Cache::set($codeKey, $code, self::SMS_CODE_TTL_SECONDS);
Cache::set($throttleKey, 1, self::SMS_CODE_RESEND_SECONDS);
return json(['code' => 200, 'msg' => '验证码已发送']);
}
private function buildLoginSuccessResponse($user, $tenant): Json
{
$tid = (int)$tenant->id;
try {
$this->validate($data, [
'account|账号' => 'require|length:3,32',
'password|密码' => 'require|length:6,32'
]);
} catch (ValidateException $e) {
$this->logFail('登录管理', '登录', $e->getMessage());
return json([
'code' => 400,
'msg' => $e->getError()
$loginCount = isset($user['login_count']) && $user['login_count'] !== null ? (int)$user['login_count'] : 0;
AdminUser::where('id', $user['id'])->update([
'login_count' => $loginCount + 1,
'last_login_ip' => $this->request->ip(),
'last_login_time' => date('Y-m-d H:i:s')
]);
} catch (\Exception $e) {
error_log('更新登录信息失败: ' . $e->getMessage());
}
$user = AdminUser::where('account', $data['account'])
->where('status', 1)
->where('delete_time', null)
->find();
if (!$user) {
return json([
'code' => 401,
'msg' => '账号不存在或已禁用'
]);
}
if (md5($data['password']) !== $user['password']) {
return json([
'code' => 401,
'msg' => '密码错误'
]);
}
AdminUser::where('id', $user['id'])->update([
'login_count' => $user['login_count'] + 1,
'last_login_ip' => $this->request->ip()
]);
$userInfo = [
'id' => $user['id'],
'account' => $user['account'],
'name' => $user['name'],
'group_id' => $user['group_id']
'group_id' => $user['group_id'],
'tid' => $tid,
'tenant' => $tenant
];
$token = $this->generateToken($userInfo);
if ($user['group_id']) {
$userGroup = AdminUserGroup::where('id', $user['group_id'])->find();
if ($userGroup && $userGroup->rights) {
$userInfo['rights'] = json_decode($userGroup->rights, true);
}
}
try {
$token = $this->generateToken($userInfo);
} catch (\Exception $e) {
$this->logFail('登录管理', '登录', 'Token生成失败: ' . $e->getMessage());
return json(['code' => 500, 'msg' => '登录失败,请稍后重试']);
}
try {
$cacheKey = 'admin_user_' . $user['id'] . '_' . $tid;
\think\facade\Cache::set($cacheKey, $userInfo, 86400 * 7);
} catch (\Exception $e) {
error_log('用户缓存写入失败: ' . $e->getMessage());
}
try {
$this->logSuccess('登录管理', '登录', [
'id' => $user['id'],
'tid' => $tid,
'tenant' => $tenant
], $userInfo);
} catch (\Exception $e) {
error_log('登录日志记录失败: ' . $e->getMessage());
}
$this->logSuccess('登录管理', '登录', ['id' => $user['id']], $userInfo);
return json([
'code' => 200,
'msg' => '登录成功',
@@ -95,6 +212,249 @@ class LoginController extends BaseController
]);
}
/**
* 登录接口
* @return Json
*/
public function login(): Json
{
try {
$data = $this->request->param();
// 1. 先验证租户名称是否传入(新增:必须传租户名称)
try {
$this->validate($data, [
'tenant_name|租户名称' => 'require|length:1,128', // 匹配租户表tenant字段长度
'account|账号' => 'require|length:3,32',
'password|密码' => 'require|length:6,32'
]);
} catch (ValidateException $e) {
$this->logFail('登录管理', '登录', $e->getMessage());
return json([
'code' => 400,
'msg' => $e->getError()
]);
}
// 2. 处理账号兼容(邮箱/手机号转account)
if (isset($data['email'])) {
$data['account'] = $data['email'];
} elseif (isset($data['phone'])) {
$data['account'] = $data['phone'];
}
// 3. 查询租户ID(新增:只查正常状态的租户 status=1)
$tenant = Tenant::where('tenant_name', $data['tenant_name'])
->where('status', 1) // 过滤停用/删除的租户
->field(['id', 'tenant_name']) // 只查需要的字段,提升性能
->find(); // ThinkPHP 使用 find()
// 4. 验证租户是否存在
if (!$tenant) {
$this->logFail('登录管理', '登录', '租户不存在或已禁用,租户名称:' . $data['tenant_name']);
return json([
'code' => 401,
'msg' => '租户不存在或已禁用'
]);
}
$tid = $tenant->id;
$tenant_name = $tenant->tenant_name;
// 5. 查询用户(新增:关联租户ID,确保用户属于该租户)
$user = AdminUser::where('account', $data['account'])
->where('tid', $tid) // 核心:验证用户所属租户
->where('status', 1)
->find();
// 6. 验证用户是否存在
if (!$user) {
$this->logFail('登录管理', '登录', '账号不存在/已禁用,或不属于当前租户,账号:' . $data['account'] . ',租户:' . $tenant_name);
return json([
'code' => 401,
'msg' => '账号不存在或已禁用,或不属于当前租户'
]);
}
// 7. 验证密码
if (md5($data['password']) !== $user['password']) {
$this->logFail('登录管理', '登录', '密码错误,账号:' . $data['account'] . ',租户:' . $tenant_name);
return json([
'code' => 401,
'msg' => '密码错误'
]);
}
// 8. 更新登录次数和IP
try {
$loginCount = isset($user['login_count']) && $user['login_count'] !== null ? (int)$user['login_count'] : 0;
AdminUser::where('id', $user['id'])->update([
'login_count' => $loginCount + 1,
'last_login_ip' => $this->request->ip(),
'last_login_time' => date('Y-m-d H:i:s') // 新增:记录最后登录时间,更实用
]);
} catch (\Exception $e) {
error_log('更新登录信息失败: ' . $e->getMessage());
}
// 9. 组装用户信息(新增:加入租户ID和租户名称和角色权限)
$userInfo = [
'id' => $user['id'],
'account' => $user['account'],
'name' => $user['name'],
'group_id' => $user['group_id'],
'tid' => $tid, // 新增:租户ID
'tenant' => $tenant // 新增:租户名称
];
// 获取角色权限
if ($user['group_id']) {
$userGroup = AdminUserGroup::where('id', $user['group_id'])->find();
if ($userGroup && $userGroup->rights) {
$userInfo['rights'] = json_decode($userGroup->rights, true);
}
}
// 10. 生成Token(Token中已包含租户信息,后续可通过Token解析获取)
try {
$token = $this->generateToken($userInfo);
} catch (\Exception $e) {
$this->logFail('登录管理', '登录', 'Token生成失败: ' . $e->getMessage());
return json([
'code' => 500,
'msg' => '登录失败,请稍后重试'
]);
}
// 11. 写入用户数据缓存(核心:缓存包含租户信息,示例用Redis,可根据你的缓存工具调整)
try {
$cacheKey = 'admin_user_' . $user['id'] . '_' . $tid; // 缓存键加入租户ID,避免多租户冲突
$cacheExpire = 86400 * 7; // 缓存7天,可根据需求调整
// 写入缓存(这里假设你使用thinkphp的Cache类,若用其他工具可替换)
\think\facade\Cache::set($cacheKey, $userInfo, $cacheExpire);
} catch (\Exception $e) {
error_log('用户缓存写入失败: ' . $e->getMessage());
// 缓存写入失败不影响登录,但记录日志
}
// 12. 记录登录成功日志
try {
$this->logSuccess('登录管理', '登录', [
'id' => $user['id'],
'tid' => $tid,
'tenant' => $tenant
], $userInfo);
} catch (\Exception $e) {
error_log('登录日志记录失败: ' . $e->getMessage());
}
// 13. 返回登录结果(包含租户信息)
return json([
'code' => 200,
'msg' => '登录成功',
'data' => [
'token' => $token,
'user' => $userInfo // 前端可直接获取租户ID和名称
]
]);
} catch (\Exception $e) {
$errorMsg = $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine();
error_log('登录失败: ' . $errorMsg);
try {
$this->logFail('登录管理', '登录', $errorMsg);
} catch (\Exception $logError) {
error_log('记录登录失败日志也失败: ' . $logError->getMessage());
}
return json([
'code' => 500,
'msg' => '登录失败:' . $e->getMessage()
]);
}
}
/**
* 发送手机号登录验证码
*/
public function sendLoginCode(): Json
{
try {
$data = $this->request->post();
$this->validate($data, [
'tenant_name|租户名称' => 'require|length:1,128',
'phone|手机号' => 'require|mobile',
]);
$tenant = Tenant::where('tenant_name', (string)$data['tenant_name'])
->where('status', 1)
->find();
if (!$tenant) {
return json(['code' => 400, 'msg' => '租户不存在或已禁用']);
}
$user = AdminUser::where('tid', (int)$tenant['id'])
->where('phone', (string)$data['phone'])
->where('status', 1)
->where('delete_time', null)
->find();
if (!$user) {
return json(['code' => 404, 'msg' => '手机号未绑定可用账号']);
}
return $this->sendSmsCodeInternal('login', (string)$data['tenant_name'], (string)$data['phone'], (string)$data['phone']);
} catch (ValidateException $e) {
return json(['code' => 400, 'msg' => $e->getError()]);
} catch (\Throwable $e) {
return json(['code' => 500, 'msg' => '发送失败:' . $e->getMessage()]);
}
}
/**
* 手机号验证码登录
*/
public function loginBySms(): Json
{
try {
$data = $this->request->post();
$this->validate($data, [
'tenant_name|租户名称' => 'require|length:1,128',
'phone|手机号' => 'require|mobile',
'sms_code|短信验证码' => 'require|length:4,8',
]);
$codeKey = $this->smsCodeCacheKey('login', (string)$data['tenant_name'], (string)$data['phone'], (string)$data['phone']);
$cachedCode = (string)Cache::get($codeKey, '');
if ($cachedCode === '' || $cachedCode !== (string)$data['sms_code']) {
return json(['code' => 400, 'msg' => '短信验证码错误或已过期']);
}
$tenant = Tenant::where('tenant_name', (string)$data['tenant_name'])
->where('status', 1)
->field(['id', 'tenant_name'])
->find();
if (!$tenant) {
return json(['code' => 401, 'msg' => '租户不存在或已禁用']);
}
$user = AdminUser::where('tid', (int)$tenant['id'])
->where('phone', (string)$data['phone'])
->where('status', 1)
->where('delete_time', null)
->find();
if (!$user) {
return json(['code' => 401, 'msg' => '手机号未绑定可用账号']);
}
Cache::delete($codeKey);
return $this->buildLoginSuccessResponse($user, $tenant);
} catch (ValidateException $e) {
return json(['code' => 400, 'msg' => $e->getError()]);
} catch (\Throwable $e) {
return json(['code' => 500, 'msg' => '登录失败:' . $e->getMessage()]);
}
}
/**
* 退出登录
* @return Json
@@ -103,7 +463,7 @@ class LoginController extends BaseController
{
$authHeader = $this->request->header('Authorization', '');
$userInfo = null;
if (preg_match('/Bearer\s+(.+)/i', $authHeader, $matches)) {
$tokenData = $this->verifyToken($matches[1]);
if ($tokenData && isset($tokenData['user'])) {
@@ -114,7 +474,7 @@ class LoginController extends BaseController
if ($userInfo && isset($userInfo['id'])) {
$this->logSuccess('登录管理', '退出登录', ['result' => 'success'], $userInfo);
} else {
\app\model\OperationLog::create([
OperationLog::create([
'user_id' => 0,
'user_account' => '',
'user_name' => '未知用户',
@@ -145,7 +505,7 @@ class LoginController extends BaseController
public function userInfo(): Json
{
$authHeader = $this->request->header('Authorization', '');
if (!preg_match('/Bearer\s+(.+)/i', $authHeader, $matches)) {
return json([
'code' => 401,
@@ -154,7 +514,7 @@ class LoginController extends BaseController
}
$tokenData = $this->verifyToken($matches[1]);
if (!$tokenData || !isset($tokenData['user'])) {
return json([
'code' => 401,
@@ -183,9 +543,286 @@ class LoginController extends BaseController
'data' => $userData->toArray()
]);
}
public function getAdminUserFromToken(): array
{
return JwtService::getUserFromHeader($this->request->header('Authorization', ''));
}
{
return JwtService::getUserFromHeader($this->request->header('Authorization', ''));
}
public function loginInfo()
{
$loginInfo = SystemSiteSettings::select();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $loginInfo
]);
}
/**
* 注册账号(按租户维度)
*/
public function sendRegisterCode(): Json
{
try {
$data = $this->request->post();
$this->validate($data, [
'tenant_name|租户名称' => 'require|length:1,128',
'account|账号' => 'require|length:3,32',
'phone|手机号' => 'require|mobile',
]);
return $this->sendSmsCodeInternal('register', (string)$data['tenant_name'], (string)$data['account'], (string)$data['phone']);
} catch (ValidateException $e) {
return json(['code' => 400, 'msg' => $e->getError()]);
} catch (\Throwable $e) {
return json(['code' => 500, 'msg' => '发送失败:' . $e->getMessage()]);
}
}
public function register(): Json
{
try {
$data = $this->request->post();
$this->validate($data, [
'tenant_name|租户名称' => 'require|length:1,128',
'account|账号' => 'require|length:3,32',
'name|姓名' => 'require|length:2,32',
'password|密码' => 'require|length:6,32',
'confirm_password|确认密码' => 'require',
'phone|手机号' => 'require|mobile',
'sms_code|短信验证码' => 'require|length:4,8',
]);
if ((string)$data['password'] !== (string)$data['confirm_password']) {
return json(['code' => 400, 'msg' => '两次输入的密码不一致']);
}
$codeKey = $this->smsCodeCacheKey('register', (string)$data['tenant_name'], (string)$data['account'], (string)$data['phone']);
$cachedCode = (string)Cache::get($codeKey, '');
if ($cachedCode === '' || $cachedCode !== (string)$data['sms_code']) {
return json(['code' => 400, 'msg' => '短信验证码错误或已过期']);
}
$tenant = Tenant::where('tenant_name', (string)$data['tenant_name'])
->where('status', 1)
->find();
if (!$tenant) {
return json(['code' => 400, 'msg' => '租户不存在或已禁用']);
}
$exists = AdminUser::where('tid', (int)$tenant['id'])
->where('account', (string)$data['account'])
->where('delete_time', null)
->find();
if ($exists) {
return json(['code' => 400, 'msg' => '账号已存在']);
}
$now = date('Y-m-d H:i:s');
$user = new AdminUser();
$user->save([
'tid' => (int)$tenant['id'],
'account' => (string)$data['account'],
'name' => (string)$data['name'],
'phone' => (string)$data['phone'],
'email' => (string)($data['email'] ?? ''),
'password' => md5((string)$data['password']),
'status' => 1,
'group_id' => 0,
'login_count' => 0,
'create_time' => $now,
'update_time' => $now,
]);
Cache::delete($codeKey);
return json(['code' => 200, 'msg' => '注册成功']);
} catch (ValidateException $e) {
return json(['code' => 400, 'msg' => $e->getError()]);
} catch (\Throwable $e) {
return json(['code' => 500, 'msg' => '注册失败:' . $e->getMessage()]);
}
}
/**
* 忘记密码:通过租户 + 账号 + 手机号重置密码
*/
public function sendResetCode(): Json
{
try {
$data = $this->request->post();
$this->validate($data, [
'tenant_name|租户名称' => 'require|length:1,128',
'account|账号' => 'require|length:3,32',
'phone|手机号' => 'require|mobile',
]);
$tenant = Tenant::where('tenant_name', (string)$data['tenant_name'])
->where('status', 1)
->find();
if (!$tenant) {
return json(['code' => 400, 'msg' => '租户不存在或已禁用']);
}
$user = AdminUser::where('tid', (int)$tenant['id'])
->where('account', (string)$data['account'])
->where('phone', (string)$data['phone'])
->where('delete_time', null)
->find();
if (!$user) {
return json(['code' => 404, 'msg' => '账号不存在或手机号不匹配']);
}
return $this->sendSmsCodeInternal('reset', (string)$data['tenant_name'], (string)$data['account'], (string)$data['phone']);
} catch (ValidateException $e) {
return json(['code' => 400, 'msg' => $e->getError()]);
} catch (\Throwable $e) {
return json(['code' => 500, 'msg' => '发送失败:' . $e->getMessage()]);
}
}
public function resetPassword(): Json
{
try {
$data = $this->request->post();
$this->validate($data, [
'tenant_name|租户名称' => 'require|length:1,128',
'account|账号' => 'require|length:3,32',
'phone|手机号' => 'require|mobile',
'new_password|新密码' => 'require|length:6,32',
'confirm_password|确认密码' => 'require',
'sms_code|短信验证码' => 'require|length:4,8',
]);
if ((string)$data['new_password'] !== (string)$data['confirm_password']) {
return json(['code' => 400, 'msg' => '两次输入的密码不一致']);
}
$codeKey = $this->smsCodeCacheKey('reset', (string)$data['tenant_name'], (string)$data['account'], (string)$data['phone']);
$cachedCode = (string)Cache::get($codeKey, '');
if ($cachedCode === '' || $cachedCode !== (string)$data['sms_code']) {
return json(['code' => 400, 'msg' => '短信验证码错误或已过期']);
}
$tenant = Tenant::where('tenant_name', (string)$data['tenant_name'])
->where('status', 1)
->find();
if (!$tenant) {
return json(['code' => 400, 'msg' => '租户不存在或已禁用']);
}
$user = AdminUser::where('tid', (int)$tenant['id'])
->where('account', (string)$data['account'])
->where('phone', (string)$data['phone'])
->where('delete_time', null)
->find();
if (!$user) {
return json(['code' => 404, 'msg' => '账号不存在或手机号不匹配']);
}
AdminUser::where('id', (int)$user['id'])->update([
'password' => md5((string)$data['new_password']),
'update_time' => date('Y-m-d H:i:s'),
]);
Cache::delete($codeKey);
return json(['code' => 200, 'msg' => '密码重置成功']);
} catch (ValidateException $e) {
return json(['code' => 400, 'msg' => $e->getError()]);
} catch (\Throwable $e) {
return json(['code' => 500, 'msg' => '重置失败:' . $e->getMessage()]);
}
}
/**
* 获取极验3.0的id和key
* @return Json
*/
public function getGeetest3Infos()
{
// 定义你需要的 label 列表
$targetLabels = [
'geetest3ID',
'geetest3KEY'
];
$legalInfos = SystemSiteSettings::where('delete_time', null)
->whereIn('label', $targetLabels) // 仅筛选指定的 label
->field('label, value')
->select();
$this->logSuccess('站点设置管理', '查看极验3.0的id和key', [], $this->getAdminUserInfo());
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $legalInfos->toArray()
]);
}
/**
* 获取极验4.0的id和key
* @return Json
*/
public function getGeetest4Infos()
{
// 定义你需要的 label 列表
$targetLabels = [
'geetest4ID',
'geetest4KEY'
];
$legalInfos = SystemSiteSettings::where('delete_time', null)
->whereIn('label', $targetLabels) // 仅筛选指定的 label
->field('label, value')
->select()
->toArray();
// 转换为前端需要的格式
$data = [];
foreach ($legalInfos as $item) {
if ($item['label'] === 'geetest4ID') {
$data['captcha_id'] = $item['value'];
}
if ($item['label'] === 'geetest4KEY') {
$data['captcha_key'] = $item['value'];
}
}
$this->logSuccess('站点设置管理', '查看极验4.0的id和key', [], $this->getAdminUserInfo());
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $data
]);
}
/**
* 判断是否开启验证
* @return Json
*/
public function getOpenVerify()
{
// 定义你需要的 label 列表
$targetLabels = [
'openVerify',
'verifyModel'
];
$legalInfos = SystemSiteSettings::where('delete_time', null)
->whereIn('label', $targetLabels) // 仅筛选指定的 label
->field('label, value')
->select();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $legalInfos->toArray()
]);
}
}
+116 -6
View File
@@ -11,8 +11,8 @@ use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\SystemMenu;
use app\model\AdminUser;
use app\model\AdminUserGroup;
use app\model\System\AdminUser;
use app\model\System\AdminUserGroup;
class MenuController extends BaseController
{
@@ -69,6 +69,10 @@ class MenuController extends BaseController
$userGroup = AdminUserGroup::where('id', $user['group_id'])
->find();
// 调试日志
error_log('getMenus - user group_id: ' . $user['group_id']);
error_log('getMenus - userGroup: ' . json_encode($userGroup));
// var_dump($userGroup);
if (!$userGroup) {
@@ -81,8 +85,9 @@ class MenuController extends BaseController
// 解析权限数组
$menuIds = [];
if (!empty($userGroup['rights'])) {
$menuIds = is_array($userGroup['rights']) ? $userGroup['rights'] : json_decode($userGroup['rights'], true);
if ($userGroup->rights) {
$decoded = json_decode($userGroup->rights, true);
$menuIds = is_array($decoded) ? $decoded : [];
}
// var_dump($menuIds);
@@ -95,11 +100,14 @@ class MenuController extends BaseController
]);
}
// 获取有权限的菜单及其所有父级菜单
$allMenuIds = $this->getMenuIdsWithParents($menuIds);
// 获取有权限的菜单
$menus = SystemMenu::where('delete_time', null)
->where('status', 1)
->whereIn('id', $menuIds)
->field('id,pid,title,path,component_path,icon,sort')
->whereIn('id', $allMenuIds)
->field('id,pid,title,path,component_path,icon,sort,is_visible')
->order('sort', 'asc')
->select();
@@ -253,6 +261,7 @@ class MenuController extends BaseController
'title|菜单名称' => 'require|max:50',
'type|菜单类型' => 'require|in:1,2,3',
'status|菜单状态' => 'require|in:0,1',
'is_visible|是否显示' => 'require|in:0,1',
'sort|排序号' => 'integer',
'path|路由路径' => 'max:200',
'icon|菜单图标' => 'max:100',
@@ -266,6 +275,7 @@ class MenuController extends BaseController
'title' => $data['title'],
'type' => $data['type'],
'status' => $data['status'] ?? 1,
'is_visible' => $data['is_visible'] ?? 1,
'sort' => $data['sort'] ?? 0,
'path' => $data['path'] ?? '',
'component_path' => $data['component_path'] ?? '',
@@ -280,6 +290,9 @@ class MenuController extends BaseController
$result = SystemMenu::insertGetId($menuData);
if ($result) {
// 更新角色菜单权限
$this->updateRoleMenuPermissions((int)$result, 'create');
// 记录操作日志
$this->logSuccess('菜单管理', '创建菜单', ['id' => $result]);
return json([
@@ -318,6 +331,7 @@ class MenuController extends BaseController
'pid|上级菜单ID' => 'integer',
'type|菜单类型' => 'require|in:1,2,3',
'status|菜单状态' => 'require|in:0,1',
'is_visible|是否显示' => 'require|in:0,1',
'sort|排序号' => 'integer',
'path|路由路径' => 'max:200',
'icon|菜单图标' => 'max:100',
@@ -335,6 +349,7 @@ class MenuController extends BaseController
'icon' => $data['icon'] ?? null,
'sort' => $data['sort'] ?? 0,
'status' => $data['status'],
'is_visible' => $data['is_visible'],
'permission' => $data['permission'] ?? null,
'remark' => $data['remark'] ?? null,
'update_time' => date('Y-m-d H:i:s')
@@ -343,6 +358,9 @@ class MenuController extends BaseController
// 执行更新
SystemMenu::where('id', $id)->update($updateData);
// 更新角色菜单权限
$this->updateRoleMenuPermissions($id, 'update');
// 获取更新后的菜单信息
$menu = SystemMenu::where('id', $id)->find();
@@ -405,6 +423,9 @@ class MenuController extends BaseController
]);
}
// 更新角色菜单权限(从所有角色中移除该菜单)
$this->updateRoleMenuPermissions($id, 'delete');
// 记录操作日志
$this->logSuccess('菜单管理', '删除菜单', ['id' => $id]);
return json([
@@ -421,6 +442,95 @@ class MenuController extends BaseController
}
}
/**
* 更新角色菜单权限
* @param int $menuId 菜单ID
* @param string $action 操作类型: create, update, delete
* @return void
*/
private function updateRoleMenuPermissions(int $menuId, string $action = 'update'): void
{
try {
// 获取所有角色
$roles = AdminUserGroup::where('delete_time', null)->select();
foreach ($roles as $role) {
// 解析权限数据,如果rights为空,初始化为空数组
$rights = empty($role->rights) ? [] : json_decode($role->rights, true);
if (!is_array($rights)) {
$rights = [];
}
$updated = false;
switch ($action) {
case 'create':
case 'update':
// 创建或更新菜单时,自动添加到所有角色的权限中
if (!in_array($menuId, $rights)) {
$rights[] = $menuId;
$updated = true;
}
break;
case 'delete':
// 删除菜单时,从所有角色的权限中移除该菜单ID
if (in_array($menuId, $rights)) {
$rights = array_diff($rights, [$menuId]);
$rights = array_values($rights); // 重新索引数组
$updated = true;
}
break;
}
// 如果权限有变化,更新角色
if ($updated) {
$role->rights = !empty($rights) ? json_encode($rights) : null;
$role->update_time = date('Y-m-d H:i:s');
$role->save();
}
}
} catch (\Exception $e) {
// 记录错误日志但不中断主流程
$this->logFail('菜单管理', '更新角色菜单权限', $e->getMessage());
}
}
/**
* 获取菜单ID及其所有父级菜单ID
* @param array $menuIds 菜单ID数组
* @return array 包含所有父级菜单的ID数组
*/
private function getMenuIdsWithParents(array $menuIds): array
{
if (empty($menuIds)) {
return [];
}
$allIds = $menuIds;
$toCheck = $menuIds;
while (!empty($toCheck)) {
// 查询这些菜单的父级
$parents = SystemMenu::whereIn('id', $toCheck)
->where('delete_time', null)
->column('pid');
// 过滤掉已经存在的和为0的父ID
$newParents = [];
foreach ($parents as $pid) {
if ($pid > 0 && !in_array($pid, $allIds)) {
$newParents[] = $pid;
$allIds[] = $pid;
}
}
$toCheck = $newParents;
}
return $allIds;
}
/**
* 构建菜单树形结构
* @param array $menus 菜单列表
@@ -0,0 +1,150 @@
<?php
declare(strict_types=1);
namespace app\admin\controller;
use app\admin\BaseController;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Session;
use think\response\Json;
use app\model\SystemModuleCenter;
use app\model\SystemModuleCategory;
use app\model\System\AdminUser;
class ModuleCenterController extends BaseController
{
/**
* 获取模块中心分类
* @return Json
*/
public function getModuleCategory()
{
$cates = SystemModuleCategory::where('delete_time', null)->field('id, title, status')->select()->toArray();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'list' => $cates,
'total' => count($cates)
]
]);
}
/**
* 获取模块中心列表
* @param int $cid 分类ID0表示全部
* @return Json
*/
public function getModuleList(int $cid = 0)
{
$query = SystemModuleCenter::where('delete_time', null)
->field('id, title, desc, thumb, cid, downloads, author, status, version, create_time');
// 如果cid不为0,则按分类筛选
if ($cid > 0) {
$query->where('cid', $cid);
}
$list = $query->select()->toArray();
// 遍历列表,将cid替换为分类标题
foreach ($list as &$item) {
if ($item['cid']) {
$category = SystemModuleCategory::where('id', $item['cid'])->field('title')->find();
$item['cid'] = $category ? $category['title'] : '';
}
if ($item['author']) {
$author = AdminUser::where('id', $item['author'])->field('name')->find();
$item['author'] = $author ? $author['name'] : '';
}
}
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'list' => $list,
'total' => count($list)
]
]);
}
/**
* 编辑分类(添加/编辑)
* @param int $id 分类ID,为空时新增,有值时编辑
*/
public function editCategory(int $id = 0)
{
$data = request()->param();
// 过滤只保留数据库表需要的字段
$allowedFields = ['id', 'title', 'status'];
$saveData = [];
foreach ($allowedFields as $field) {
if (isset($data[$field])) {
$saveData[$field] = $data[$field];
}
}
if ($id > 0) {
// 编辑模式
$saveData['update_time'] = date('Y-m-d H:i:s');
SystemModuleCategory::where('id', $id)->update($saveData);
$this->logSuccess('模块市场', '编辑模块分类', ['id' => $id, 'data' => $saveData]);
return json([
'code' => 200,
'msg' => '编辑成功'
]);
} else {
// 新增模式
$saveData['create_time'] = date('Y-m-d H:i:s');
$cateId = SystemModuleCategory::insertGetId($saveData);
$this->logSuccess('模块市场', '添加模块分类', ['data' => $saveData]);
return json([
'code' => 200,
'msg' => '添加成功'
]);
}
}
/**
* 编辑模块(添加/编辑)
* @param int $id 为空时新增,有值时编辑
*/
public function editModules(int $id = 0)
{
$data = request()->param();
// 过滤只保留数据库表需要的字段
$allowedFields = ['id', 'title', 'desc', 'thumb', 'cid', 'version', 'tatus'];
$saveData = [];
foreach ($allowedFields as $field) {
if (isset($data[$field])) {
$saveData[$field] = $data[$field];
}
}
if ($id > 0) {
// 编辑模式
$saveData['update_time'] = date('Y-m-d H:i:s');
SystemModuleCenter::where('id', $id)->update($saveData);
$this->logSuccess('模块市场', '编辑模块', ['id' => $id, 'data' => $saveData]);
return json([
'code' => 200,
'msg' => '编辑成功'
]);
} else {
// 新增模式
$saveData['create_time'] = date('Y-m-d H:i:s');
$moduleId = SystemModuleCenter::insertGetId($saveData);
$this->logSuccess('模块市场', '添加模块', ['data' => $saveData]);
return json([
'code' => 200,
'msg' => '添加成功'
]);
}
}
}
+128 -1
View File
@@ -7,6 +7,9 @@ namespace app\admin\controller;
use app\admin\BaseController;
use think\response\Json;
use app\model\AdminModules;
use app\model\System\AdminUserGroup;
use app\model\SystemMenu;
use Symfony\Component\VarDumper\VarDumper;
class ModulesController extends BaseController
{
@@ -43,13 +46,77 @@ class ModulesController extends BaseController
}
}
/**
* 获取租户模块列表
* @return Json
*/
public function getTenantList()
{
try {
$userInfo = $this->getAdminUserInfo();
$groupId = $userInfo['group_id'] ?? null;
$menuIds = [];
if ($groupId) {
$userGroup = AdminUserGroup::where('id', $groupId)->find();
if ($userGroup && $userGroup->rights) {
$rights = $userGroup->rights;
$decoded = json_decode($rights, true);
$menuIds = is_array($decoded) ? $decoded : [];
}
}
if (empty($menuIds)) {
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'list' => [],
'total' => 0
]
]);
}
// 根据子菜单ID查询所有父级模块ID
$moduleIds = $this->getModuleIdsByMenuIds($menuIds);
$modules = AdminModules::where('delete_time', null)
->whereIn('id', $moduleIds)
->where('status', 1)
->where('is_show', 1)
->order('sort', 'asc')
->order('id', 'asc')
->select()
->toArray();
$this->logSuccess('模块管理', '获取模块列表', ['count' => count($modules)]);
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'list' => $modules,
'total' => count($modules)
]
]);
} catch (\Exception $e) {
$this->logFail('模块管理', '获取模块列表', $e->getMessage());
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage(),
'data' => []
]);
}
}
/**
* 获取模块详情
* @return Json
*/
public function getDetail(int $id)
public function getDetail($id)
{
try {
$id = (int) $id;
$module = AdminModules::where('id', $id)
->where('delete_time', null)
->find();
@@ -92,6 +159,7 @@ class ModulesController extends BaseController
'description' => input('post.description', '', 'trim'),
'icon' => input('post.icon', '', 'trim'),
'path' => input('post.path', '', 'trim'),
'type' => input('post.type', '', 'trim'),
'sort' => (int)input('post.sort', 0),
'status' => (int)input('post.status', 1),
'is_show' => (int)input('post.is_show', 1),
@@ -161,6 +229,7 @@ class ModulesController extends BaseController
'description' => input('post.description', '', 'trim'),
'icon' => input('post.icon', '', 'trim'),
'path' => input('post.path', '', 'trim'),
'type' => input('post.type', '', 'trim'),
'sort' => (int)input('post.sort', 0),
'status' => (int)input('post.status', 1),
'is_show' => (int)input('post.is_show', 1),
@@ -323,6 +392,64 @@ class ModulesController extends BaseController
}
}
/**
* 根据菜单ID列表获取对应的模块ID列表(包括父级模块)
* @param array $menuIds 菜单ID列表(子菜单)
* @return array 模块ID列表
*/
private function getModuleIdsByMenuIds(array $menuIds): array
{
if (empty($menuIds)) {
return [];
}
// 1. 获取所有子菜单及其父级菜单ID
$allMenuIds = $this->getMenuIdsWithParents($menuIds);
// 2. 获取所有匹配的模块(mid在allMenuIds中)
$moduleIds = AdminModules::where('delete_time', null)
->whereIn('mid', $allMenuIds)
->column('id');
// 去重并返回
return array_unique($moduleIds);
}
/**
* 获取菜单ID及其所有父级菜单ID
* @param array $menuIds 菜单ID数组
* @return array 包含所有父级菜单的ID数组
*/
private function getMenuIdsWithParents(array $menuIds): array
{
if (empty($menuIds)) {
return [];
}
$allIds = $menuIds;
$toCheck = $menuIds;
while (!empty($toCheck)) {
// 查询这些菜单的父级
$parents = SystemMenu::whereIn('id', $toCheck)
->where('delete_time', null)
->column('pid');
// 过滤掉已经存在的和为0的父ID
$newParents = [];
foreach ($parents as $pid) {
if ($pid > 0 && !in_array($pid, $allIds)) {
$newParents[] = $pid;
$allIds[] = $pid;
}
}
$toCheck = $newParents;
}
return $allIds;
}
/**
* 获取模块选择列表
* @return Json
@@ -7,8 +7,8 @@ namespace app\admin\controller\OperationLog;
use app\admin\BaseController;
use think\facade\Request;
use think\response\Json;
use app\model\OperationLog;
use app\model\AdminUser;
use app\model\System\OperationLog;
use app\model\System\AdminUser;
class OperationLogController extends BaseController
{
@@ -28,7 +28,8 @@ class OperationLogController extends BaseController
$startTime = Request::param('startTime/s', '');
$endTime = Request::param('endTime/s', '');
$query = OperationLog::where('delete_time', null);
$query = OperationLog::where('delete_time', null)
->where('tid', $this->getTenantId());
// 关键词搜索(用户姓名、URL
if ($keyword) {
@@ -129,6 +130,7 @@ class OperationLogController extends BaseController
{
try {
$log = OperationLog::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
@@ -164,6 +166,7 @@ class OperationLogController extends BaseController
{
try {
$log = OperationLog::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
@@ -206,6 +209,7 @@ class OperationLogController extends BaseController
}
OperationLog::whereIn('id', $ids)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->delete();
@@ -230,11 +234,13 @@ class OperationLogController extends BaseController
try {
// 获取模块列表
$modules = OperationLog::where('delete_time', null)
->where('tid', $this->getTenantId())
->group('module')
->column('module');
// 获取动作列表
$actions = OperationLog::where('delete_time', null)
->where('tid', $this->getTenantId())
->group('action')
->column('action');
@@ -2,7 +2,7 @@
namespace app\admin\controller\OperationLog;
use app\model\OperationLog;
use app\model\System\OperationLog;
use think\facade\Request;
use think\facade\Session;
@@ -37,6 +37,7 @@ class OperationLogHelper
$userId = $userInfo['id'] ?? 0;
$userAccount = $userInfo['account'] ?? '';
$userName = $userInfo['name'] ?? '';
$tid = $userInfo['tid'] ?? ($userInfo['tid'] ?? 0);
// 获取请求信息
$method = Request::method();
@@ -52,6 +53,7 @@ class OperationLogHelper
'user_id' => $userId,
'user_account' => $userAccount,
'user_name' => $userName,
'tid' => $tid,
'module' => $module,
'action' => $action,
'method' => $method,
@@ -3,7 +3,7 @@
namespace app\admin\controller\OperationLog;
use think\facade\Request;
use app\model\OperationLog;
use app\model\System\OperationLog;
use app\service\JwtService;
/**
@@ -33,6 +33,7 @@ class OperationLogger
$userId = $userInfo['id'] ?? 0;
$userAccount = $userInfo['account'] ?? '';
$userName = $userInfo['name'] ?? '';
$tid = $userInfo['tid'] ?? ($userInfo['tid'] ?? 0);
if (empty($requestData)) {
$requestData = Request::param();
@@ -49,6 +50,7 @@ class OperationLogger
'user_id' => $userId,
'user_account' => $userAccount,
'user_name' => $userName,
'tid' => $tid,
'module' => $module,
'action' => $action,
'method' => $method,
+312 -88
View File
@@ -8,24 +8,59 @@ use app\admin\BaseController;
use think\exception\ValidateException;
use think\facade\Db;
use think\response\Json;
use app\model\SystemSiteSettings;
use app\model\System\SystemSiteSetting;
use app\model\System\SystemSiteSettings;
use app\model\Tenant\Tenant;
class SiteSettingsController extends BaseController
{
/**
* 获取所有站点设置列表
* 获取指定站点设置列表
* @return Json
*/
public function getNormalInfos()
{
$siteSettings = SystemSiteSettings::where('delete_time', null)
->field('id, label, value, sort')
->order('id', 'asc')
->select();
// 获取当前租户ID
$tid = $this->request->param('tid', 0, 'int');
// 查询站点设置
$siteSetting = SystemSiteSetting::where('delete_time', null)
->where('tid', $tid)
->find();
// 如果没有数据,返回空值结构
if (!$siteSetting) {
$data = [
'sitename' => '',
'logo' => '',
'logow' => '',
'ico' => '',
'companyintroduction' => '',
'description' => '',
'copyright' => '',
'companyname' => '',
'icp' => ''
];
} else {
$data = [
'sitename' => $siteSetting->sitename ?? '',
'logo' => $siteSetting->logo ?? '',
'logow' => $siteSetting->logow ?? '',
'ico' => $siteSetting->ico ?? '',
'companyintroduction' => $siteSetting->companyintroduction ?? '',
'description' => $siteSetting->description ?? '',
'copyright' => $siteSetting->copyright ?? '',
'companyname' => $siteSetting->companyname ?? '',
'icp' => $siteSetting->icp ?? ''
];
}
$this->logSuccess('站点设置管理', '查看基本信息配置', [], $this->getAdminUserInfo());
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $siteSettings->toArray()
'data' => $data
]);
}
@@ -36,65 +71,137 @@ class SiteSettingsController extends BaseController
public function saveNormalInfos()
{
$params = $this->request->param();
$rawData = isset($params['data']) ? json_decode($params['data'], true) : $params;
$tid = $params['tid'] ?? 0;
if (!isset($params['data']) || empty($params['data'])) {
return json(['code' => 400, 'msg' => '数据不能为空', 'data' => null]);
// 查找或创建记录
$siteSetting = SystemSiteSetting::where('delete_time', null)
->where('tid', $tid)
->find();
if (!$siteSetting) {
$siteSetting = new SystemSiteSetting();
$siteSetting->tid = $tid;
$siteSetting->create_time = date('Y-m-d H:i:s');
}
$data = json_decode($params['data'], true);
if (!is_array($data)) {
return json(['code' => 400, 'msg' => '数据格式错误', 'data' => null]);
// 更新字段
if (isset($rawData['sitename'])) {
$siteSetting->sitename = (string)$rawData['sitename'];
}
if (isset($rawData['companyintroduction'])) {
$siteSetting->companyintroduction = (string)$rawData['companyintroduction'];
}
if (isset($rawData['logo'])) {
$siteSetting->logo = (string)$rawData['logo'];
}
if (isset($rawData['logow'])) {
$siteSetting->logow = (string)$rawData['logow'];
}
if (isset($rawData['ico'])) {
$siteSetting->ico = (string)$rawData['ico'];
}
if (isset($rawData['description'])) {
$siteSetting->description = (string)$rawData['description'];
}
if (isset($rawData['copyright'])) {
$siteSetting->copyright = (string)$rawData['copyright'];
}
if (isset($rawData['companyname'])) {
$siteSetting->companyname = (string)$rawData['companyname'];
}
if (isset($rawData['icp'])) {
$siteSetting->icp = (string)$rawData['icp'];
}
foreach ($data as $item) {
if (!isset($item['label']) || !isset($item['value'])) {
continue;
}
$siteSetting->update_time = date('Y-m-d H:i:s');
$siteSetting->save();
$label = $item['label'];
$value = $item['value'] ?? '';
$this->logSuccess('站点设置管理', '保存基本信息', ['tid' => $tid], $this->getAdminUserInfo());
$setting = SystemSiteSettings::where('label', $label)->find();
if ($setting) {
$setting->value = $value;
$setting->save();
} else {
SystemSiteSettings::create([
'label' => $label,
'value' => $value,
'create_time' => date('Y-m-d H:i:s')
]);
}
}
return json(['code' => 200, 'msg' => '基本信息保存成功']);
}
/**
* 获取登录验证数据
* @return Json
*/
public function getVerifyInfos()
{
// 定义你需要的 label 列表
$targetLabels = [
'openVerify',
'verifyModel',
'geetest3ID',
'geetest3KEY',
'geetest4ID',
'geetest4KEY'
];
$legalInfos = SystemSiteSettings::where('delete_time', null)
->whereIn('label', $targetLabels) // 仅筛选指定的 label
->field('label, value')
->select();
$this->logSuccess('站点设置管理', '查看登录验证数据', [], $this->getAdminUserInfo());
$allSettings = SystemSiteSettings::column('value', 'label');
$allSettings['type'] = 'normal';
$labels = array_column($data, 'label');
$userInfo = $this->getAdminUserInfo();
$this->logSuccess('站点设置管理', '保存基本信息', ['labels' => $labels], $userInfo);
return json([
'code' => 200,
'msg' => '保存成功',
'data' => $allSettings
'msg' => '获取成功',
'data' => $legalInfos->toArray()
]);
}
/**
* 获取所有站点设置列表
* 保存登录验证数据
* @return Json
*/
public function saveVerifyInfos()
{
$params = $this->request->param();
$allowedLabels = ['openVerify', 'verifyModel', 'geetest3ID', 'geetest3KEY', 'geetest4ID', 'geetest4KEY'];
foreach ($allowedLabels as $label) {
if (isset($params[$label])) {
$val = $params[$label];
if (is_bool($val)) {
$val = $val ? '1' : '0';
}
SystemSiteSettings::where('label', $label)->update([
'value' => (string)$val
]);
}
}
$this->logSuccess('站点设置管理', '保存登录验证配置', $params, $this->getAdminUserInfo());
return json(['code' => 200, 'msg' => '保存成功']);
}
/**
* 获取法律声明和隐私条款
* @return Json
*/
public function getLegalInfos()
{
$siteSettings = SystemSiteSettings::where('delete_time', null)
->field('id, label, value, sort')
->order('id', 'asc')
// 定义你需要的 label 列表
$targetLabels = [
'legalNotice',
'privacyTerms'
];
$legalInfos = SystemSiteSettings::where('delete_time', null)
->whereIn('label', $targetLabels) // 仅筛选指定的 label
->field('label, value')
->select();
$this->logSuccess('站点设置管理', '查看法律声明和隐私条款', [], $this->getAdminUserInfo());
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $siteSettings->toArray()
'data' => $legalInfos->toArray()
]);
}
@@ -105,58 +212,175 @@ class SiteSettingsController extends BaseController
public function saveLegalInfos()
{
$params = $this->request->param();
$rawData = isset($params['data']) ? json_decode($params['data'], true) : $params;
$allowedLabels = ['legalNotice', 'privacyTerms'];
if (!isset($params['data']) || empty($params['data'])) {
return json(['code' => 400, 'msg' => '数据不能为空', 'data' => null]);
}
$data = json_decode($params['data'], true);
if (!is_array($data)) {
return json(['code' => 400, 'msg' => '数据格式错误', 'data' => null]);
}
foreach ($data as $item) {
if (!isset($item['label']) || !isset($item['value'])) {
continue;
}
$label = $item['label'];
$value = $item['value'] ?? '';
$setting = SystemSiteSettings::where('label', $label)->find();
if ($setting) {
$setting->value = $value;
$setting->save();
} else {
SystemSiteSettings::create([
'label' => $label,
'value' => $value,
'create_time' => date('Y-m-d H:i:s')
]);
foreach ($allowedLabels as $label) {
if (isset($rawData[$label])) {
SystemSiteSettings::update(
['value' => (string)$rawData[$label]],
['label' => $label]
);
}
}
$this->logSuccess('站点设置管理', '保存法律声明和隐私条款', ['labels' => $allowedLabels], $this->getAdminUserInfo());
return json(['code' => 200, 'msg' => '法律声明和隐私条款保存成功']);
}
/**
* 获取企业信息
* @return Json
*/
public function getCompanyInfos()
{
// 获取当前租户ID
$tid = $this->request->param('tid', 0, 'int');
// 查询租户信息
$tenant = Tenant::where('delete_time', null)
->where('id', $tid)
->find();
// 如果没有数据,返回空值结构
if (!$tenant) {
$data = [
'contact_phone' => '',
'contact_email' => '',
'address' => '',
'worktime' => ''
];
} else {
$data = [
'contact_phone' => $tenant->contact_phone ?? '',
'contact_email' => $tenant->contact_email ?? '',
'address' => $tenant->address ?? '',
'worktime' => $tenant->worktime ?? ''
];
}
$this->logSuccess('站点设置管理', '查看企业信息', [], $this->getAdminUserInfo());
$allSettings = SystemSiteSettings::column('value', 'label');
$allSettings['type'] = 'legal_notice';
$labels = array_column($data, 'label');
$userId = Session::get('user_id', 0);
$userFromSession = Session::get('user', []);
$userFromCache = Cache::get('userInfo_' . $userId, []);
$account = Session::get('account', '');
$name = Session::get('name', '');
\think\facade\Log::record('SiteSettings Debug - user_id: ' . $userId . ', userFromSession: ' . json_encode($userFromSession) . ', userFromCache: ' . json_encode($userFromCache) . ', account: ' . $account . ', name: ' . $name);
$this->logSuccess('站点设置管理', '保存法律声明和隐私条款', ['labels' => $labels]);
return json([
'code' => 200,
'msg' => '保存成功',
'data' => $allSettings
'msg' => '获取成功',
'data' => $data
]);
}
/**
* 保存企业信息
* @return Json
*/
public function saveCompanyInfos()
{
$params = $this->request->param();
$tid = $params['tid'] ?? 0;
// 查找租户记录
$tenant = Tenant::where('delete_time', null)
->where('id', $tid)
->find();
if (!$tenant) {
return json([
'code' => 404,
'msg' => '租户不存在'
]);
}
// 更新字段
if (isset($params['contact_phone'])) {
$tenant->contact_phone = (string)$params['contact_phone'];
}
if (isset($params['contact_email'])) {
$tenant->contact_email = (string)$params['contact_email'];
}
if (isset($params['address'])) {
$tenant->address = (string)$params['address'];
}
if (isset($params['worktime'])) {
$tenant->worktime = (string)$params['worktime'];
}
$tenant->save();
$this->logSuccess('站点设置管理', '保存企业信息', ['tid' => $tid], $this->getAdminUserInfo());
return json(['code' => 200, 'msg' => '企业信息保存成功']);
}
/**
* 获取企业SEO
* @return Json
*/
public function getCompanySeo()
{
// 获取当前租户ID
$tid = $this->getTenantId();
// 直接根据 ID 查询租户记录
$tenant = Tenant::where('delete_time', null)
->where('id', $tid)
->field('seoTitle, seoKeywords, seoDescription')
->find();
// 如果没有数据,返回默认空值
if (!$tenant) {
$data = [
'seoTitle' => '',
'seoKeywords' => '',
'seoDescription' => ''
];
} else {
// 直接读取模型对象的属性
$data = [
'seoTitle' => $tenant->seoTitle ?? '',
'seoKeywords' => $tenant->seoKeywords ?? '',
'seoDescription' => $tenant->seoDescription ?? ''
];
}
$this->logSuccess('站点设置管理', '查看企业SEO', ['tid' => $tid], $this->getAdminUserInfo());
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $data
]);
}
/**
* 保存企业SEO
* @return Json
*/
public function saveCompanySeo()
{
$params = $this->request->param();
$id = $this->getTenantId();
$tenant = Tenant::where('delete_time', null)
->where('id', $id)
->find();
if (!$tenant) {
return json(['code' => 404, 'msg' => '租户不存在']);
}
// 构建更新数据
$updateData = [];
if (isset($params['seoTitle'])) $updateData['seoTitle'] = (string)$params['seoTitle'];
if (isset($params['seoKeywords'])) $updateData['seoKeywords'] = (string)$params['seoKeywords'];
if (isset($params['seoDescription'])) $updateData['seoDescription'] = (string)$params['seoDescription'];
if (!empty($updateData)) {
$tenant->save($updateData);
}
$this->logSuccess('站点设置管理', '保存企业SEO', ['tid' => $id], $this->getAdminUserInfo());
return json(['code' => 200, 'msg' => '企业SEO保存成功']);
}
}
@@ -0,0 +1,232 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\System;
use app\admin\BaseController;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\System\SystemEmail;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception as MailException;
class EmailController extends BaseController
{
/**
* 获取邮箱信息
* @return Json
*/
public function getEmailInfo()
{
try {
$email = SystemEmail::order('id', 'asc')
->field('id, from_address, from_name, host, port, password, encryption, timeout, create_time, update_time')
->select()
->toArray();
// 记录操作日志
$this->logSuccess('邮箱管理', '获取邮箱信息', ['data' => $email]);
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $email
]);
} catch (DbException $e) {
// 记录失败日志
$this->logFail('邮箱管理', '获取邮箱信息', $e->getMessage());
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage(),
'data' => []
]);
}
}
/**
* 编辑邮箱信息
* @return Json
*/
public function editEmailInfo()
{
try {
// 获取原始请求参数(驼峰命名)
$raw = $this->request->post();
// 手动映射为下划线命名,便于与数据库字段、验证规则对应
$data = [
'from_address' => $raw['fromAddress'] ?? '',
'from_name' => $raw['fromName'] ?? '',
'host' => $raw['host'] ?? '',
'port' => $raw['port'] ?? '',
'password' => $raw['password'] ?? '',
'encryption' => $raw['encryption'] ?? '',
'timeout' => $raw['timeout'] ?? '',
];
// 验证参数
$this->validate($data, [
'from_address|发件人邮箱' => 'require|max:255',
'from_name|发件人名称' => 'require|max:255',
'host|SMTP主机' => 'require|max:255',
'port|SMTP端口' => 'require|integer',
'password|密码' => 'require|max:255',
'encryption|加密方式' => 'require|max:255',
'timeout|超时时间' => 'require|integer',
]);
// 先查一条配置(本表设计为单条配置)
$email = SystemEmail::order('id', 'asc')->find();
// 公共字段
$saveData = [
'from_address' => $data['from_address'],
'from_name' => $data['from_name'],
'host' => $data['host'],
'port' => $data['port'],
'password' => $data['password'],
'encryption' => $data['encryption'],
'timeout' => $data['timeout'],
];
if ($email) {
// 已有一条记录,按主键更新
$saveData['update_time'] = date('Y-m-d H:i:s');
SystemEmail::update($saveData, ['id' => $email['id']]);
} else {
// 没有记录,插入新记录
$saveData['create_time'] = date('Y-m-d H:i:s');
$saveData['update_time'] = $saveData['create_time'];
$model = new SystemEmail();
$model->save($saveData);
}
// 获取最新的邮箱信息
$updatedEmail = SystemEmail::order('id', 'asc')->find();
// 记录操作日志
$this->logSuccess('邮箱管理', '更新邮箱信息', ['data' => $updatedEmail]);
return json([
'code' => 200,
'msg' => '更新成功',
'data' => $updatedEmail
]);
} catch (ValidateException $e) {
// 记录失败日志
$this->logFail('邮箱管理', '更新邮箱信息', $e->getMessage());
return json([
'code' => 400,
'msg' => $e->getError()
]);
} catch (DbException $e) {
// 记录失败日志
$this->logFail('邮箱管理', '更新邮箱信息', $e->getMessage());
return json([
'code' => 500,
'msg' => '更新失败:' . $e->getMessage()
]);
}
}
/**
* 发送测试邮件
* @return Json
*/
public function sendTestEmail()
{
try {
// 获取前端传来的表单数据(驼峰命名)
$raw = $this->request->post();
// 映射为下划线命名,便于验证和使用
$data = [
'from_address' => $raw['fromAddress'] ?? '',
'from_name' => $raw['fromName'] ?? '',
'host' => $raw['host'] ?? '',
'port' => $raw['port'] ?? '',
'password' => $raw['password'] ?? '',
'encryption' => $raw['encryption'] ?? '',
'timeout' => $raw['timeout'] ?? 30,
'test_email' => $raw['testEmail'] ?? '',
];
// 参数验证
$this->validate($data, [
'from_address|发件人邮箱' => 'require|email|max:255',
'from_name|发件人名称' => 'max:255',
'host|SMTP主机' => 'require|max:255',
'port|SMTP端口' => 'require|integer',
'password|密码' => 'require|max:255',
'encryption|加密方式' => 'require|max:10',
'timeout|超时时间' => 'integer',
'test_email|测试收件邮箱' => 'require|email|max:255',
]);
// 使用 PHPMailer 发送测试邮件
$mail = new PHPMailer(true);
// 服务器设置
$mail->CharSet = 'UTF-8';
$mail->isSMTP();
$mail->Host = $data['host'];
$mail->SMTPAuth = true;
// 发件人邮箱即为登录用户名
$mail->Username = $data['from_address'];
$mail->Password = $data['password'];
$mail->Port = (int)$data['port'];
// 加密方式
if (in_array(strtolower((string)$data['encryption']), ['ssl', 'tls'], true)) {
$mail->SMTPSecure = strtolower((string)$data['encryption']);
}
// 超时时间(秒)
if (!empty($data['timeout'])) {
$mail->Timeout = (int)$data['timeout'];
}
// 收发件配置
$fromName = $data['from_name'] ?: $data['from_address'];
$mail->setFrom($data['from_address'], $fromName);
$mail->addAddress($data['test_email']);
// 内容
$mail->isHTML(false);
$mail->Subject = '测试邮件 - 网站邮箱配置';
$mail->Body = '这是一封测试邮件,用于验证网站邮箱配置是否正确。发送时间:' . date('Y-m-d H:i:s');
// 发送
$mail->send();
// 记录操作日志
$this->logSuccess('邮箱管理', '发送测试邮件', [
'to' => $data['test_email'],
'host' => $data['host'],
]);
return json([
'code' => 200,
'msg' => '测试邮件发送成功',
]);
} catch (ValidateException $e) {
$this->logFail('邮箱管理', '发送测试邮件', $e->getMessage());
return json([
'code' => 400,
'msg' => $e->getError()
]);
} catch (MailException $e) {
$this->logFail('邮箱管理', '发送测试邮件', $e->getMessage());
return json([
'code' => 500,
'msg' => '发送失败:' . $e->getMessage()
]);
} catch (\Exception $e) {
$this->logFail('邮箱管理', '发送测试邮件', $e->getMessage());
return json([
'code' => 500,
'msg' => '发送失败:' . $e->getMessage()
]);
}
}
}
@@ -1,6 +1,6 @@
<?php
namespace app\admin\controller;
namespace app\admin\controller\System;
use app\admin\BaseController;
use think\facade\Filesystem;
@@ -8,8 +8,8 @@ use think\facade\Request;
use think\facade\Db;
use think\Response;
use app\model\FilesCategory;
use app\model\Files;
use app\model\System\FilesCategory;
use app\model\System\Files;
class FileController extends BaseController
{
@@ -38,7 +38,8 @@ class FileController extends BaseController
$cate = Request::param('cate/d', 0);
$keyword = Request::param('keyword/s', '');
$query = Files::where('delete_time', null);
$query = Files::where('delete_time', null)
->where('tid', $this->getTenantId());
if ($cate) {
$query->where('cate', $cate);
@@ -75,11 +76,20 @@ class FileController extends BaseController
public function getUserCate()
{
try {
$cate = FilesCategory::where('delete_time', null)->field('id,name')->select();
$tid = $this->getTenantId();
// 只根据 tid 筛选分类
$cate = FilesCategory::where('delete_time', null)
->where('tid', $tid)
->field('id,name')
->select();
// 获取每个分类下的文件数量
foreach ($cate as &$c) {
$c['total'] = Files::where('cate', $c['id'])->where('delete_time', null)->count();
$c['total'] = Files::where('cate', $c['id'])
->where('tid', $tid)
->where('delete_time', null)
->count();
}
return json([
@@ -101,6 +111,7 @@ class FileController extends BaseController
try {
$data = Request::param();
$data['create_time'] = date('Y-m-d H:i:s');
$data['tid'] = $this->getTenantId();
$id = FilesCategory::insertGetId($data);
// 记录操作日志
@@ -129,7 +140,9 @@ class FileController extends BaseController
{
try {
$data = Request::param();
$result = FilesCategory::where('id', $id)->update(['name' => $data['name']]);
$result = FilesCategory::where('id', $id)
->where('tid', $this->getTenantId())
->update(['name' => $data['name']]);
// 记录操作日志
$this->logSuccess('文件管理', '重命名文件分组', ['id' => $id]);
return json([
@@ -150,7 +163,21 @@ class FileController extends BaseController
public function deleteFileCate($id)
{
try {
$result = FilesCategory::where('id', $id)->update(['delete_time' => date('Y-m-d H:i:s')]);
// 检查分组里是否有关联文件
$fileCount = Files::where('cate', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->count();
if ($fileCount > 0) {
return json([
'code' => 400,
'msg' => '该分组下还有 ' . $fileCount . ' 个文件,请先删除分组内文件!'
]);
}
$result = FilesCategory::where('id', $id)
->where('tid', $this->getTenantId())
->update(['delete_time' => date('Y-m-d H:i:s')]);
// 记录操作日志
$this->logSuccess('文件管理', '删除文件分组', ['id' => $id]);
return json([
@@ -175,12 +202,16 @@ class FileController extends BaseController
$pageSize = Request::param('pageSize/d', 10);
$keyword = Request::param('keyword/s', '');
$query = Files::where('cate', $id)->where('delete_time', null);
$query = Files::where('cate', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null);
if ($keyword) {
$query->whereLike('name', "%{$keyword}%");
}
// 获取总条数
$total = $query->count();
$list = $query->page($page, $pageSize)
->order('create_time', 'desc')
->select();
@@ -196,6 +227,7 @@ class FileController extends BaseController
'msg' => 'success',
'data' => [
'list' => $list,
'total' => $total,
'page' => $page,
'pageSize' => $pageSize,
'categoryId' => $id
@@ -209,6 +241,7 @@ class FileController extends BaseController
}
}
// 文件上传
public function uploadFile()
{
try {
@@ -229,7 +262,10 @@ class FileController extends BaseController
$fileMd5 = md5_file($file->getRealPath());
// 检查是否已存在相同文件
$existFile = Files::where('md5', $fileMd5)->where('delete_time', null)->find();
$existFile = Files::where('md5', $fileMd5)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
if ($existFile) {
return json([
@@ -271,6 +307,7 @@ class FileController extends BaseController
'src' => $fileUrl,
'md5' => $fileMd5,
'uploader' => $userId,
'tid' => $this->getTenantId(),
'create_time' => date('Y-m-d H:i:s'),
];
@@ -308,7 +345,10 @@ class FileController extends BaseController
$data['update_time'] = date('Y-m-d H:i:s');
$result = Files::where('id', $id)->where('delete_time', null)->update($data);
$result = Files::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->update($data);
if ($result) {
// 记录操作日志
@@ -328,7 +368,17 @@ class FileController extends BaseController
public function deleteFile($id)
{
try {
$result = Files::where('id', $id)->update(['delete_time' => date('Y-m-d H:i:s')]);
// 先获取文件信息
$file = Files::where('id', $id)
->where('tid', $this->getTenantId())
->find();
if (!$file) {
return json(['code' => 404, 'msg' => '文件不存在']);
}
$result = Files::where('id', $id)
->where('tid', $this->getTenantId())
->update(['delete_time' => date('Y-m-d H:i:s')]);
if ($result) {
// 记录操作日志
@@ -344,11 +394,49 @@ class FileController extends BaseController
}
}
//永久删除文件
public function deleteFilePermanently($id)
{
try {
// 先获取文件信息
$file = Files::where('id', $id)
->where('tid', $this->getTenantId())
->find();
if (!$file) {
return json(['code' => 404, 'msg' => '文件不存在']);
}
// 删除服务器上的文件
if ($file['src']) {
$this->deleteFileFromServer($file['src']);
}
$result = Files::where('id', $id)
->where('tid', $this->getTenantId())
->update(['delete_time' => date('Y-m-d H:i:s')]);
if ($result) {
// 记录操作日志
$this->logSuccess('文件管理', '永久删除文件', ['id' => $id]);
return json(['code' => 200, 'msg' => '永久删除成功']);
}
return json(['code' => 404, 'msg' => '文件不存在']);
} catch (\Exception $e) {
// 记录失败日志
$this->logFail('文件管理', '永久删除文件', $e->getMessage());
return json(['code' => 500, 'msg' => '永久删除失败: ' . $e->getMessage()]);
}
}
// 下载文件
public function download($id)
{
try {
$file = Files::where('id', $id)->where('delete_time', null)->find();
$file = Files::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
if (!$file) {
return json(['code' => 404, 'msg' => '文件不存在']);
@@ -373,7 +461,9 @@ class FileController extends BaseController
{
try {
$cate = Request::param('cate/d', 0);
$result = Files::where('id', $id)->update(['cate' => $cate]);
$result = Files::where('id', $id)
->where('tid', $this->getTenantId())
->update(['cate' => $cate]);
if ($result) {
// 记录操作日志
$this->logSuccess('文件管理', '移动文件', ['id' => $id]);
@@ -387,4 +477,121 @@ class FileController extends BaseController
return json(['code' => 500, 'msg' => '移动失败: ' . $e->getMessage()]);
}
}
//删除服务器上文件
private function deleteFileFromServer($fileUrl)
{
// 去掉开头的 /storage/,得到相对路径
$relativePath = ltrim($fileUrl, '/');
if (strpos($relativePath, 'storage/') === 0) {
$relativePath = substr($relativePath, 8); // 去掉 'storage/' 部分
}
// 获取实际服务器路径
$fullPath = root_path() . 'public/storage/' . $relativePath;
if (file_exists($fullPath)) {
unlink($fullPath);
}
}
// 批量删除文件
public function batchDeleteFiles()
{
try {
$ids = Request::param('ids/a', []);
if (empty($ids)) {
return json(['code' => 400, 'msg' => '请选择要删除的文件']);
}
foreach ($ids as $id) {
$file = Files::where('id', $id)
->where('tid', $this->getTenantId())
->find();
if ($file && $file['src']) {
$this->deleteFileFromServer($file['src']);
}
}
$result = Files::where('id', 'in', $ids)
->where('tid', $this->getTenantId())
->update(['delete_time' => date('Y-m-d H:i:s')]);
if ($result) {
$this->logSuccess('文件管理', '批量删除文件', ['ids' => $ids]);
return json(['code' => 200, 'msg' => '批量删除成功']);
}
return json(['code' => 404, 'msg' => '文件不存在']);
} catch (\Exception $e) {
$this->logFail('文件管理', '批量删除文件', $e->getMessage());
return json(['code' => 500, 'msg' => '批量删除失败: ' . $e->getMessage()]);
}
}
// 批量彻底删除文件
public function batchDeleteFilesPermanently()
{
try {
$ids = Request::param('ids/a', []);
if (empty($ids)) {
return json(['code' => 400, 'msg' => '请选择要彻底删除的文件']);
}
foreach ($ids as $id) {
$file = Files::where('id', $id)
->where('tid', $this->getTenantId())
->find();
if ($file && $file['src']) {
$this->deleteFileFromServer($file['src']);
}
}
$result = Files::where('id', 'in', $ids)
->where('tid', $this->getTenantId())
->force(true)
->update(['delete_time' => date('Y-m-d H:i:s')])
->delete();
if ($result) {
$this->logSuccess('文件管理', '批量彻底删除文件', ['ids' => $ids]);
return json(['code' => 200, 'msg' => '批量彻底删除成功']);
}
return json(['code' => 404, 'msg' => '文件不存在']);
} catch (\Exception $e) {
$this->logFail('文件管理', '批量彻底删除文件', $e->getMessage());
return json(['code' => 500, 'msg' => '批量彻底删除失败: ' . $e->getMessage()]);
}
}
// 批量移动文件
public function batchMoveFiles()
{
try {
$ids = Request::param('ids/a', []);
$cate = Request::param('cate/d', 0);
if (empty($ids)) {
return json(['code' => 400, 'msg' => '请选择要移动的文件']);
}
$result = Files::where('id', 'in', $ids)
->where('tid', $this->getTenantId())
->update(['cate' => $cate]);
if ($result) {
$this->logSuccess('文件管理', '批量移动文件', ['ids' => $ids, 'cate' => $cate]);
return json(['code' => 200, 'msg' => '批量移动成功']);
}
return json(['code' => 404, 'msg' => '文件不存在']);
} catch (\Exception $e) {
$this->logFail('文件管理', '批量移动文件', $e->getMessage());
return json(['code' => 500, 'msg' => '批量移动失败: ' . $e->getMessage()]);
}
}
}
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace app\admin\controller;
namespace app\admin\controller\System;
use app\admin\BaseController;
use think\exception\ValidateException;
@@ -10,8 +10,8 @@ use think\facade\Db;
use think\facade\Session;
use think\response\Json;
use app\model\AdminUser;
use app\model\AdminUserGroup;
use app\model\System\AdminUser;
use app\model\System\AdminUserGroup;
class RoleController extends BaseController
{
@@ -21,7 +21,11 @@ class RoleController extends BaseController
*/
public function getAllRoles()
{
$tid = $this->getTenantId();
$roles = AdminUserGroup::where('delete_time', null)
->where(function ($query) use ($tid) {
$query->where('tid', $tid)->whereOr('tid', 0);
})
->order('id', 'asc')
->select();
return json([
@@ -39,6 +43,9 @@ class RoleController extends BaseController
public function getRoleById(int $id)
{
$role = AdminUserGroup::where('id', $id)
->where(function ($query) {
$query->where('tid', $this->getTenantId())->whereOr('tid', 0);
})
->where('delete_time', null)
->find();
if (!$role) {
@@ -73,6 +80,7 @@ class RoleController extends BaseController
// 检查角色名称是否已存在
$exists = AdminUserGroup::where('name', $data['name'])
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
if ($exists) {
@@ -85,6 +93,7 @@ class RoleController extends BaseController
// 准备数据
$roleData = [
'name' => $data['name'],
'tid' => $this->getTenantId(),
'status' => $data['status'] ?? 1,
'rights' => !empty($data['rights']) ? json_encode($data['rights']) : null,
'create_time' => date('Y-m-d H:i:s'),
@@ -136,8 +145,11 @@ class RoleController extends BaseController
'rights|权限' => 'array'
]);
// 查找角色
// 查找角色(验证tid,tid为0的系统角色也可编辑)
$role = AdminUserGroup::where('id', $id)
->where(function ($query) {
$query->where('tid', $this->getTenantId())->whereOr('tid', 0);
})
->where('delete_time', null)
->find();
if (!$role) {
@@ -147,9 +159,12 @@ class RoleController extends BaseController
]);
}
// 检查角色名称是否已被其他角色使用
// 检查角色名称是否已被其他角色使用tid为0或当前用户tid的角色)
$exists = AdminUserGroup::where('name', $data['name'])
->where('id', '<>', $id)
->where(function ($query) {
$query->where('tid', $this->getTenantId())->whereOr('tid', 0);
})
->where('delete_time', null)
->find();
if ($exists) {
@@ -160,18 +175,23 @@ class RoleController extends BaseController
}
// 更新数据
$role->name = $data['name'];
$role->status = $data['status'] ?? 1;
$role->rights = !empty($data['rights']) ? json_encode($data['rights']) : null;
$role->update_time = date('Y-m-d H:i:s');
$role->save();
$updateData = [
'name' => $data['name'],
'status' => $data['status'] ?? 1,
'rights' => !empty($data['rights']) ? json_encode($data['rights']) : null,
'update_time' => date('Y-m-d H:i:s')
];
AdminUserGroup::where('id', $id)->update($updateData);
// 重新查询获取最新数据
$updatedRole = AdminUserGroup::where('id', $id)->find();
// 记录操作日志
$this->logSuccess('角色管理', '更新角色', ['id' => $id]);
return json([
'code' => 200,
'msg' => '更新成功',
'data' => $role->toArray()
'data' => $updatedRole->toArray()
]);
} catch (ValidateException $e) {
// 记录失败日志
@@ -206,8 +226,9 @@ class RoleController extends BaseController
]);
}
// 查找角色
// 查找角色(验证tid
$role = AdminUserGroup::where('id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->find();
if (!$role) {
@@ -219,6 +240,7 @@ class RoleController extends BaseController
// 检查是否有用户正在使用该角色
$userCount = AdminUser::where('group_id', $id)
->where('tid', $this->getTenantId())
->where('delete_time', null)
->count();
if ($userCount > 0) {
@@ -0,0 +1,676 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\System;
use app\admin\BaseController;
use app\model\System\SystemSiteSettings;
use think\facade\Db;
use think\facade\Log;
use think\db\exception\DbException;
use think\exception\ValidateException;
use think\facade\Request;
class SmsController extends BaseController
{
private function getSiteSettingValue(string $label): string
{
$row = SystemSiteSettings::where('label', $label)->order('id', 'asc')->find();
if (!$row) {
return '';
}
return (string)$row['value'];
}
private function upsertSiteSetting(string $label, string $value): void
{
$row = SystemSiteSettings::where('label', $label)->order('id', 'asc')->find();
if ($row) {
SystemSiteSettings::update(['value' => $value], ['id' => $row['id']]);
return;
}
$model = new SystemSiteSettings();
$model->label = $label;
$model->value = $value;
$model->sort = 0;
$model->save();
}
/**
* 获取短信网关配置
*/
public function getSmsInfo()
{
try {
$backendUrl = $this->getSiteSettingValue('backendUrl');
$apiKey = $this->getSiteSettingValue('apiKey');
// 前端约定返回 data[0],字段名兼容 backend_url/api_key 与 backendUrl/apiKey
$data = [[
'backend_url' => $backendUrl,
'api_key' => $apiKey,
'backendUrl' => $backendUrl,
'apiKey' => $apiKey,
]];
$this->logSuccess('短信管理', '获取短信网关配置', ['data' => $data]);
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $data,
]);
} catch (DbException $e) {
$this->logFail('短信管理', '获取短信网关配置', $e->getMessage());
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage(),
'data' => [],
]);
}
}
/**
* 编辑短信网关配置(本表设计为单条配置)
*/
public function editSmsInfo()
{
try {
$raw = $this->request->post();
$data = [
'backend_url' => $raw['backendUrl'] ?? $raw['backend_url'] ?? '',
'api_key' => $raw['apiKey'] ?? $raw['api_key'] ?? '',
];
$this->validate($data, [
'backend_url|短信网关地址' => 'require|max:255',
'api_key|短信网关 API KEY' => 'require|max:255',
]);
$this->upsertSiteSetting('backendUrl', (string)$data['backend_url']);
$this->upsertSiteSetting('apiKey', (string)$data['api_key']);
$updated = [[
'backend_url' => $this->getSiteSettingValue('backendUrl'),
'api_key' => $this->getSiteSettingValue('apiKey'),
]];
$this->logSuccess('短信管理', '更新短信网关配置', ['data' => $updated]);
return json([
'code' => 200,
'msg' => '更新成功',
'data' => $updated,
]);
} catch (ValidateException $e) {
$this->logFail('短信管理', '更新短信网关配置', $e->getMessage());
return json([
'code' => 400,
'msg' => $e->getError(),
]);
} catch (DbException $e) {
$this->logFail('短信管理', '更新短信网关配置', $e->getMessage());
return json([
'code' => 500,
'msg' => '更新失败:' . $e->getMessage(),
]);
}
}
/**
* 后台发起“短信测试”:入队一条任务,等待 Android 网关轮询取走并发送
*/
public function sendTestSms()
{
try {
$raw = $this->request->post();
$phone = trim((string)($raw['phone'] ?? $raw['testPhone'] ?? ''));
$content = trim((string)($raw['content'] ?? ''));
if (empty($phone)) {
return json(['code' => 400, 'msg' => '缺少测试手机号']);
}
// Android 网关文档:国际格式 +8613712345678
if (!preg_match('/^\+\d{6,15}$/', $phone)) {
return json(['code' => 400, 'msg' => '请使用国际格式手机号(以 + 开头,后为数字)']);
}
// 获取当前网关配置(来自 mete_system_site_settings label/value
$apiKey = $this->getSiteSettingValue('apiKey');
$backendUrl = $this->getSiteSettingValue('backendUrl');
if (empty($apiKey)) {
return json(['code' => 400, 'msg' => '请先配置短信网关 API KEY']);
}
if (empty($backendUrl)) {
return json(['code' => 400, 'msg' => '请先配置短信网关地址 backendUrl']);
}
$code = (string)random_int(100000, 999999);
if ($content === '') {
$content = '短信测试验证码:' . $code;
}
// 兼容:调用 Uniapp/sms 后端入队发送任务
$enqueueUrl = rtrim($backendUrl, '/') . '/api/v1/business/outbound-tasks';
$enqueuePayload = [
'phone' => $phone,
'content' => $content,
];
$enqueueHeaders = [
'X-Api-Key: ' . (string)$apiKey,
'Content-Type: application/json; charset=utf-8',
'Accept: application/json',
];
Log::write('[sms_enqueue] url=' . $enqueueUrl . ' phone=' . $phone);
$enqueueResp = $this->postJson($enqueueUrl, $enqueuePayload, $enqueueHeaders);
if (empty($enqueueResp['ok'])) {
Log::write('[sms_enqueue] failed http_status=' . ($enqueueResp['status'] ?? '') . ' body=' . ($enqueueResp['body'] ?? ''));
return json([
'code' => 500,
'msg' => '短信网关入队失败:' . ($enqueueResp['error'] ?? 'unknown'),
]);
}
$smsGatewayTaskId = (string)($enqueueResp['json']['taskId'] ?? '');
Log::write('[sms_enqueue] success taskId=' . $smsGatewayTaskId);
$enqueueDebug = [
'enqueueOk' => (bool)($enqueueResp['ok'] ?? false),
'enqueueStatus' => $enqueueResp['status'] ?? null,
'enqueueJsonKeys' => array_keys((array)($enqueueResp['json'] ?? [])),
'gatewayTaskId' => $smsGatewayTaskId,
];
// 同步一条本地记录(用于前端列表展示/对账)
$now = date('Y-m-d H:i:s');
$taskId = Db::name('mete_system_sms_tasks')->insertGetId([
'api_key' => (string)$apiKey,
'tid' => $this->getTenantId(),
'phone' => $phone,
'content' => $content,
'status' => 0, // pending(本地不自动同步网关状态)
'code' => $code, // 预期验证码(用于对账/展示)
'create_time' => $now,
'update_time' => $now,
]);
$this->logSuccess('短信管理', '发送测试短信(调用网关入队)', [
'to' => $phone,
'local_task_id' => $taskId ?? null,
'gateway_task_id' => $smsGatewayTaskId,
]);
return json([
'code' => 200,
'msg' => '测试短信任务已入队,等待网关发送',
'data' => [
'taskId' => $smsGatewayTaskId ?: ($taskId ?? null),
'code' => $code,
],
'debug' => empty($smsGatewayTaskId) ? $enqueueDebug : null,
]);
} catch (ValidateException $e) {
$this->logFail('短信管理', '发送测试短信', $e->getMessage());
return json(['code' => 400, 'msg' => $e->getError()]);
} catch (\Throwable $e) {
$this->logFail('短信管理', '发送测试短信', $e->getMessage());
return json(['code' => 500, 'msg' => '发送失败:' . $e->getMessage()]);
}
}
/**
* 简单 JSON POST(无 curl 扩展时使用)
* @return array{ok:bool,status?:int,body?:string,error?:string,json?:array}
*/
private function postJson(string $url, array $payload, array $headers): array
{
$body = json_encode($payload, JSON_UNESCAPED_UNICODE);
if ($body === false) {
return ['ok' => false, 'error' => 'json_encode failed'];
}
$headerStr = implode("\r\n", $headers);
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => $headerStr,
'content' => $body,
'timeout' => 10,
],
]);
$respBody = @file_get_contents($url, false, $context);
$status = 0;
if (!empty($http_response_header) && is_array($http_response_header)) {
foreach ($http_response_header as $line) {
if (preg_match('#^HTTP/\\S+\\s+(\\d+)#', $line, $m)) {
$status = (int)$m[1];
break;
}
}
}
if ($respBody === false) {
$last = error_get_last();
$errMsg = $last['message'] ?? 'http request failed';
return ['ok' => false, 'status' => $status, 'error' => (string)$errMsg];
}
$decoded = null;
$decoded = json_decode((string)$respBody, true);
if (!is_array($decoded)) {
$decoded = [];
}
$ok = $status >= 200 && $status < 300;
return [
'ok' => $ok,
'status' => $status,
'body' => (string)$respBody,
'json' => $decoded,
];
}
/**
* 简单 JSON GET(无 curl 扩展时使用)
* @return array{ok:bool,status?:int,body?:string,error?:string,json?:array}
*/
private function httpGetJson(string $url, array $headers): array
{
$headerStr = implode("\r\n", $headers);
$context = stream_context_create([
'http' => [
'method' => 'GET',
'header' => $headerStr,
'timeout' => 10,
// 对 4xx/5xx 也返回响应体,便于排障
'ignore_errors' => true,
],
]);
$respBody = @file_get_contents($url, false, $context);
$status = 0;
if (!empty($http_response_header) && is_array($http_response_header)) {
foreach ($http_response_header as $line) {
if (preg_match('#^HTTP/\\S+\\s+(\\d+)#', $line, $m)) {
$status = (int)$m[1];
break;
}
}
}
if ($respBody === false) {
$last = error_get_last();
$errMsg = $last['message'] ?? 'http request failed';
return ['ok' => false, 'status' => $status, 'error' => (string)$errMsg];
}
$decoded = json_decode((string)$respBody, true);
if (!is_array($decoded)) {
$decoded = [];
}
$ok = $status >= 200 && $status < 300;
return [
'ok' => $ok,
'status' => $status,
'body' => (string)$respBody,
'json' => $decoded,
];
}
/**
* Android 网关轮询:获取待发送任务
*
* 约定:
* - apiKey 通过 header `X-API-KEY` / `X-SMS-GATEWAY-API-KEY` body/query `apiKey`
*/
public function gatewayPoll()
{
$apiKey = $this->getApiKeyFromRequest();
if (empty($apiKey)) {
Log::write('[sms_gateway_poll] missing apiKey');
return json(['code' => 401, 'msg' => '缺少 apiKey']);
}
// 不强制要求 mete_system_site_settings(apiKey) 与网关入参完全一致;
// 直接按任务表的 api_key 匹配,提高兼容性与排障速度。
$apiKeySetting = $this->getSiteSettingValue('apiKey');
if (empty($apiKeySetting) || (string)$apiKeySetting !== (string)$apiKey) {
Log::write('[sms_gateway_poll] apiKey differs from setting; setting=' . $this->maskKey($apiKeySetting) . ' req=' . $this->maskKey($apiKey));
}
$task = Db::name('mete_system_sms_tasks')
->where('api_key', (string)$apiKey)
->where('status', 0)
->where('delete_time', null)
->order('id', 'asc')
->find();
// 没有任务
if (!$task) {
Log::write('[sms_gateway_poll] no task for apiKey=' . $this->maskKey($apiKey));
return json([
'code' => 200,
'msg' => 'no task',
'data' => null,
]);
}
// 标记为处理中,避免被重复领取
Db::name('mete_system_sms_tasks')->where('id', (int)$task['id'])->update([
'status' => 1,
'update_time' => date('Y-m-d H:i:s'),
]);
Log::write('[sms_gateway_poll] send task id=' . (int)$task['id'] . ' phone=' . (string)$task['phone']);
return json([
'code' => 200,
'msg' => 'success',
'data' => [
'taskId' => $task['id'],
'task_id' => $task['id'],
'phone' => $task['phone'],
'content' => $task['content'],
'smsContent' => $task['content'],
],
]);
}
/**
* Android 网关上报:上传实际收到的短信内容并解析验证码
*/
public function gatewayReport()
{
try {
$raw = $this->request->post();
$apiKey = $this->getApiKeyFromRequest();
$taskId = $raw['taskId'] ?? $raw['task_id'] ?? $raw['id'] ?? null;
$smsContent = trim((string)($raw['smsContent'] ?? $raw['sms_content'] ?? $raw['content'] ?? $raw['message'] ?? $raw['sms'] ?? ''));
$status = $raw['status'] ?? $raw['result'] ?? $raw['state'] ?? null; // success/failed/int
if (empty($apiKey)) {
return json(['code' => 401, 'msg' => '缺少 apiKey']);
}
if (empty($taskId)) {
return json(['code' => 400, 'msg' => '缺少 taskId']);
}
if ($smsContent === '') {
return json(['code' => 400, 'msg' => '缺少 smsContent']);
}
$task = Db::name('mete_system_sms_tasks')
->where('id', (int)$taskId)
->where('api_key', (string)$apiKey)
->where('delete_time', null)
->find();
if (!$task) {
Log::write('[sms_gateway_report] task not found; taskId=' . (int)$taskId . ' apiKey=' . $this->maskKey($apiKey));
return json(['code' => 404, 'msg' => '任务不存在或 apiKey 不匹配']);
}
Log::write('[sms_gateway_report] received taskId=' . (int)$taskId . ' status=' . (string)($status ?? ''));
// 从短信内容里抓取验证码(通用策略:4-6 位数字)
$parsedCode = '';
if (preg_match('/(\d{4,6})/', $smsContent, $m)) {
$parsedCode = (string)$m[1];
}
$update = [
'report_raw' => $smsContent,
'code' => $parsedCode !== '' ? $parsedCode : (string)$task['code'],
'update_time' => date('Y-m-d H:i:s'),
];
if ($status === 'failed' || $status === 'error' || $status === 2) {
$update['status'] = 2;
} elseif ($status === 'success' || $status === 'reported' || $status === 3 || $status === 1) {
// success => reported/done
$update['status'] = 3;
} else {
// 默认当做已上报
$update['status'] = 3;
}
Db::name('mete_system_sms_tasks')->where('id', (int)$task['id'])->update($update);
$this->logSuccess('短信管理', '网关上报短信(解析验证码)', [
'task_id' => $task['id'],
'parsed_code' => $update['code'],
]);
return json(['code' => 200, 'msg' => '上报成功']);
} catch (DbException $e) {
$this->logFail('短信管理', '网关上报短信', $e->getMessage());
return json(['code' => 500, 'msg' => '服务器错误:' . $e->getMessage()]);
} catch (\Throwable $e) {
$this->logFail('短信管理', '网关上报短信', $e->getMessage());
return json(['code' => 500, 'msg' => '服务器错误:' . $e->getMessage()]);
}
}
/**
* 管理员:获取短信任务列表(租户隔离)
*
* 支持参数(可选):
* - status: 任务状态
* - phone: 手机号关键词
*/
public function getSmsTaskList()
{
$backendUrl = $this->getSiteSettingValue('backendUrl');
$apiKey = $this->getSiteSettingValue('apiKey');
if (empty($backendUrl) || empty($apiKey)) {
return json([
'code' => 400,
'msg' => '短信网关配置未完成(backendUrl/apiKey 缺失)',
'list' => [],
]);
}
$status = (string)Request::param('status', '');
$phoneKeyword = trim((string)Request::param('phone', ''));
$limit = (int)Request::param('limit', 50, 'int');
$limit = $limit > 0 ? min($limit, 200) : 50;
// 前端 status: 0/1/2/3 -> uniapp backend status: pending/sending/failed/success
$statusMap = [
'0' => 'pending',
'1' => 'sending',
'2' => 'failed',
'3' => 'success',
];
$uniStatus = $status !== '' && isset($statusMap[$status]) ? $statusMap[$status] : '';
$url = rtrim($backendUrl, '/') . '/api/v1/business/outbound-tasks?limit=' . $limit;
if ($uniStatus !== '') {
$url .= '&status=' . urlencode($uniStatus);
}
if ($phoneKeyword !== '') {
$url .= '&phone=' . urlencode($phoneKeyword);
}
$headers = [
'X-Api-Key: ' . (string)$apiKey,
'Accept: application/json',
];
$resp = $this->httpGetJson($url, $headers);
if (empty($resp['ok'])) {
// 兼容:如果 uniapp 后端还未部署新增查询接口,则 fallback 到本地队列表(只能看到“已入队”,无法看到发送成功/失败)
$fallback = [];
try {
$fWhere = [
['api_key', '=', (string)$apiKey],
['delete_time', '=', null],
];
if ($phoneKeyword !== '') {
$fWhere[] = ['phone', 'like', '%' . $phoneKeyword . '%'];
}
// 前端 status=0/1/2/3,局部队列表字段同样使用数字状态
if ($status !== '') {
$fWhere[] = ['status', '=', (int)$status];
}
$fallback = Db::name('mete_system_sms_tasks')
->where($fWhere)
->order('id', 'desc')
->limit($limit)
->select()
->toArray();
} catch (\Throwable $e) {
$fallback = [];
}
return json([
'code' => 200,
'msg' => 'uniapp 查询任务接口不可用,已使用本地队列表(仅展示入队状态)',
'detail' => [
'http_status' => $resp['status'] ?? null,
'body_preview' => isset($resp['body']) ? substr((string)$resp['body'], 0, 300) : '',
],
'list' => $fallback,
]);
}
$tasks = (array)($resp['json']['tasks'] ?? []);
$statusNumMap = [
'pending' => 0,
'sending' => 1,
'failed' => 2,
'success' => 3,
];
$list = array_map(function ($t) use ($statusNumMap) {
$content = (string)($t['content'] ?? '');
$parsedCode = '';
if (preg_match('/(\d{4,6})/', $content, $m)) {
$parsedCode = (string)$m[1];
}
$statusStr = (string)($t['status'] ?? '');
return [
'id' => $t['taskId'] ?? '',
'phone' => $t['phone'] ?? '',
'content' => $content,
'code' => $parsedCode,
'status' => $statusNumMap[$statusStr] ?? 0,
'create_time' => isset($t['createdAt']) ? date('Y-m-d H:i:s', (int)($t['createdAt'] / 1000)) : '',
'update_time' => isset($t['updatedAt']) ? date('Y-m-d H:i:s', (int)($t['updatedAt'] / 1000)) : '',
];
}, $tasks);
return json([
'code' => 200,
'msg' => 'success',
'list' => $list,
]);
}
/**
* 管理员:编辑短信任务(租户隔离)
*
* body:
* - status
* - code
* - report_raw
*/
public function editSmsTask($id = null)
{
try {
$tid = $this->getTenantId();
$id = $this->request->param('id', $id);
$data = Request::only(['status', 'code', 'report_raw']);
if (empty($id)) {
return json(['code' => 400, 'msg' => '任务ID不能为空']);
}
if (!isset($data['status'])) {
return json(['code' => 400, 'msg' => '状态不能为空']);
}
$this->validate($data, [
'status|状态' => 'integer',
'code|验证码' => 'max:20',
'report_raw|上报内容' => 'max:100000',
]);
$task = Db::name('mete_system_sms_tasks')
->where('id', (int)$id)
->where('tid', (int)$tid)
->find();
if (!$task) {
return json(['code' => 404, 'msg' => '任务不存在或无权限']);
}
$update = [
'status' => (int)$data['status'],
'code' => (string)($data['code'] ?? ''),
'report_raw' => (string)($data['report_raw'] ?? ''),
'update_time' => date('Y-m-d H:i:s'),
];
Db::name('mete_system_sms_tasks')
->where('id', (int)$id)
->where('tid', (int)$tid)
->update($update);
return json(['code' => 200, 'msg' => '编辑成功']);
} catch (ValidateException $e) {
return json(['code' => 400, 'msg' => $e->getError()]);
} catch (\Throwable $e) {
return json(['code' => 500, 'msg' => '编辑失败:' . $e->getMessage()]);
}
}
private function getApiKeyFromRequest(): string
{
$req = Request::instance();
$apiKey = (string)$req->header('X-SMS-GATEWAY-API-KEY', '');
if ($apiKey !== '') {
return $apiKey;
}
$apiKey = (string)$req->header('X-API-KEY', '');
if ($apiKey !== '') {
return $apiKey;
}
$apiKey = (string)$req->header('X-SMS-API-KEY', '');
if ($apiKey !== '') {
return $apiKey;
}
$apiKey = (string)$req->header('Authorization', '');
if ($apiKey !== '' && preg_match('/Bearer\\s+(.+)/i', $apiKey, $m)) {
return (string)$m[1];
}
// body/query
$apiKey = (string)($req->post('apiKey') ?? $req->post('api_key') ?? $req->param('apiKey', '') ?? $req->param('api_key', ''));
return $apiKey;
}
private function maskKey(string $key): string
{
$key = (string)$key;
if ($key === '') return '';
if (strlen($key) <= 6) return '***';
return substr($key, 0, 3) . '***' . substr($key, -3);
}
}
@@ -0,0 +1,169 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\Tenant;
use app\admin\BaseController;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use think\Request;
use app\model\Tenant\Tenant;
use app\model\System\AdminUser;
use app\model\Template\TemplateSiteConfig;
class TenantController extends BaseController
{
/**
* 获取租户列表
*/
public function getTenant(Request $request)
{
// 获取参数
$page = $request->param('page', 1, 'int');
$pageSize = $request->param('pageSize', 10, 'int');
$tenantName = $request->param('tenant_name', '');
$tenantCode = $request->param('tenant_code', '');
$contactPerson = $request->param('contact_person', '');
$contactPhone = $request->param('contact_phone', '');
$where = [['delete_time', '=', null]];
// 动态增加模糊搜索条件
if ($tenantName) $where[] = ['tenant_name', 'like', "%$tenantName%"];
if ($tenantCode) $where[] = ['tenant_code', 'like', "%$tenantCode%"];
if ($contactPerson) $where[] = ['contact_person', 'like', "%$contactPerson%"];
if ($contactPhone) $where[] = ['contact_phone', 'like', "%$contactPhone%"];
$list = Tenant::where($where)->page($page, $pageSize)->select()->toArray();
$total = Tenant::where($where)->count();
return json(['code' => 200, 'data' => ['list' => $list, 'total' => $total]]);
}
/**
* 获取租户详情
*/
public function getTenantDetail($id)
{
$detail = Tenant::where('id', $id)->where('delete_time', null)->find()->toArray();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $detail
]);
}
/**
* 创建租户
*/
public function createTenant()
{
$data = $this->request->post();
$tenant = Tenant::create($data);
if ($tenant) {
// 创建租户默认数据
$this->createTenantDefaultData($tenant->id);
return json([
'code' => 200,
'msg' => '创建成功',
'data' => $tenant
]);
} else {
return json([
'code' => 500,
'msg' => '创建失败',
]);
}
}
/**
* 创建租户默认数据
* @param int $tenantId 租户ID
*/
private function createTenantDefaultData(int $tenantId)
{
$now = date('Y-m-d H:i:s');
// 创建租户默认模板配置
TemplateSiteConfig::create([
'tid' => $tenantId,
'key' => 'current_theme',
'value' => 'default',
'create_time' => $now,
'update_time' => $now
]);
}
/**
* 编辑租户
*/
public function editTenant($id)
{
$data = $this->request->post();
$tenant = Tenant::where('id', $id)->update($data);
if ($tenant) {
return json([
'code' => 200,
'msg' => '编辑成功',
'data' => $tenant
]);
} else {
return json([
'code' => 500,
'msg' => '编辑失败',
]);
}
}
/**
* 删除租户
*/
public function deleteTenant($id)
{
$tenant = Tenant::where('id', $id)->update(['delete_time' => date('Y-m-d H:i:s')]);
if ($tenant) {
return json([
'code' => 200,
'msg' => '删除成功',
]);
} else {
return json([
'code' => 500,
'msg' => '删除失败',
]);
}
}
/**
* 查询租户编码重复(不筛选删除数据)
*/
public function findTenantCode(Request $request)
{
$tenantCode = $request->param('tenant_code', '');
if (!$tenantCode) {
return json(['code' => 400, 'msg' => '缺少编码参数']);
}
// 直接去表里查,只要有一条一样的就返回重复
$hasOne = Tenant::where('tenant_code', $tenantCode)->find();
if ($hasOne) {
return json([
'code' => 201,
'msg' => '编码已存在',
'data' => $hasOne
]);
}
return json([
'code' => 200,
'msg' => '编码可用'
]);
}
}
+142 -24
View File
@@ -10,25 +10,85 @@ use think\facade\Db;
use think\facade\Session;
use think\response\Json;
use app\model\AdminUser;
use app\model\System\AdminUser;
class UserController extends BaseController
{
private function getCurrentTenantIdOrFail(): int
{
$tid = $this->getTenantId();
if ($tid <= 0) {
throw new \RuntimeException('未获取到有效租户信息');
}
return $tid;
}
/**
* 获取所有用户信息
* @return Json
*/
public function getAllUsers()
{
$users = AdminUser::where('delete_time', null)->field('id, account, name, phone,email, qq, sex, group_id, status, last_login_ip, login_count, create_time, update_time')->select()->toArray();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'list' => $users,
'total' => count($users)
]
]);
try {
$tid = $this->getCurrentTenantIdOrFail();
$users = AdminUser::where('delete_time', null)
->where('tid', $tid)
->field('id, tid, account, name, phone, birth, email, qq, sex, group_id, status, last_login_ip, login_count, create_time, update_time')
->select()
->toArray();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'list' => $users,
'total' => count($users)
]
]);
} catch (\Throwable $e) {
return json([
'code' => 401,
'msg' => '获取用户失败:' . $e->getMessage(),
'data' => [
'list' => [],
'total' => 0
]
]);
}
}
/**
* 获取租户用户
* @return Json
*/
public function getTenantUsers(int $tenantId)
{
try {
$tid = $this->getCurrentTenantIdOrFail();
if ($tenantId !== $tid) {
return json([
'code' => 403,
'msg' => '禁止跨租户查看用户'
]);
}
$users = AdminUser::where('delete_time', null)
->where('tid', $tid)
->field('id, tid, account, name, phone, birth, email, qq, sex, group_id, status, last_login_ip, login_count, create_time, update_time')
->select()
->toArray();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'list' => $users,
'total' => count($users)
]
]);
} catch (\Throwable $e) {
return json([
'code' => 401,
'msg' => '获取用户失败:' . $e->getMessage()
]);
}
}
/**
@@ -37,13 +97,22 @@ class UserController extends BaseController
*/
public function getUserInfo(int $id)
{
$tid = $this->getTenantId();
$user = AdminUser::where('id', $id)
->field('id, account, name, phone,email, qq, sex, group_id, status')
->find();
->where('tid', $tid)
->where('delete_time', null)
->field('id, account, name, phone, email, birth, qq, sex, group_id, status, create_time, last_login_ip')
->find();
if (!$user) {
return json([
'code' => 404,
'msg' => '用户不存在或无权限访问'
]);
}
// 记录操作日志
$this->logSuccess('用户管理', '获取用户信息', ['id' => $id]);
return json([
'code' => 200,
'msg' => '获取成功',
@@ -58,10 +127,20 @@ class UserController extends BaseController
public function changePassword(int $id, string $password)
{
try {
AdminUser::where('id', $id)->update([
$tid = $this->getCurrentTenantIdOrFail();
$affected = AdminUser::where('id', $id)
->where('tid', $tid)
->where('delete_time', null)
->update([
'password' => md5($password),
'update_time' => date('Y-m-d H:i:s')
]);
if (!$affected) {
return json([
'code' => 404,
'msg' => '用户不存在或无权限修改'
]);
}
// 记录操作日志
$this->logSuccess('用户管理', '修改密码', ['id' => $id]);
return json([
@@ -85,12 +164,21 @@ class UserController extends BaseController
public function addUser()
{
$data = request()->param();
$tid = $this->getTenantId();
if ($tid <= 0) {
return json([
'code' => 401,
'msg' => '未获取到有效租户信息'
]);
}
$data['password'] = md5($data['password']);
$data['create_time'] = date('Y-m-d H:i:s');
$data['update_time'] = $data['create_time'];
$data['group_id'] = 2;
$data['tid'] = $tid;
$id = AdminUser::insertGetId($data);
// 记录操作日志
$this->logSuccess('用户管理', '添加用户', ['data' => $data]);
return json([
'code' => 200,
@@ -107,8 +195,25 @@ class UserController extends BaseController
{
$data = request()->param();
unset($data['_t'], $data['id']);
unset($data['tid']);
$tid = $this->getTenantId();
if ($tid <= 0) {
return json([
'code' => 401,
'msg' => '未获取到有效租户信息'
]);
}
$data['update_time'] = date('Y-m-d H:i:s');
AdminUser::where('id', $id)->update($data);
$affected = AdminUser::where('id', $id)
->where('tid', $tid)
->where('delete_time', null)
->update($data);
if (!$affected) {
return json([
'code' => 404,
'msg' => '用户不存在或无权限编辑'
]);
}
$this->logSuccess('用户管理', '编辑用户', ['id' => $id]);
return json([
'code' => 200,
@@ -122,19 +227,32 @@ class UserController extends BaseController
*/
public function deleteUser(int $id)
{
$user = AdminUser::where('id', $id)->where('delete_time', null)->find();
if (!$user) {
$tid = $this->getTenantId();
if ($tid <= 0) {
return json([
'code' => 404,
'msg' => '用户不存在或已删除'
'code' => 401,
'msg' => '未获取到有效租户信息'
]);
}
AdminUser::where('id', $id)->update([
$user = AdminUser::where('id', $id)
->where('tid', $tid)
->where('delete_time', null)
->find();
if (!$user) {
return json([
'code' => 404,
'msg' => '用户不存在、已删除或无权限操作'
]);
}
AdminUser::where('id', $id)
->where('tid', $tid)
->update([
'delete_time' => date('Y-m-d H:i:s')
]);
$this->logSuccess('用户管理', '删除用户', ['id' => $id]);
return json([
'code' => 200,
+2
View File
@@ -5,6 +5,8 @@ return [
// \think\middleware\CheckRequestCache::class,
// 多语言加载
// \think\middleware\LoadLangPack::class,
// 跨域支持
\app\common\middleware\AllowCrossDomain::class,
// Session初始化
\think\middleware\SessionInit::class,
];
-65
View File
@@ -1,65 +0,0 @@
<?php
namespace app\admin\middleware;
use think\Response;
use think\Request;
/**
* 自定义跨域中间件
* 解决 AJAX 请求 Cookie 传递问题
*/
class CustomCors
{
public function handle(Request $request, \Closure $next): Response
{
$origin = $request->header('origin', '');
// 允许的域名列表(根据实际情况修改)
$allowedDomains = [
'http://localhost:3000',
'http://localhost:8000',
'http://127.0.0.1:3000',
'http://127.0.0.1:8000',
'http://backapi.yunzer.cn',
'http://localhost:5173',
'http://www.yunzer.cn',
'https://www.yunzer.cn',
'http://yunzer.cn',
'https://yunzer.cn',
'http://back.yunzer.cn',
'https://back.yunzer.cn',
'http://backend.yunzer.cn',
'https://backend.yunzer.cn',
];
// 检查是否为允许的域名
if (in_array($origin, $allowedDomains)) {
$header['Access-Control-Allow-Origin'] = $origin;
} else {
// 对于同源请求,允许当前域名
if (!empty($origin)) {
$header['Access-Control-Allow-Origin'] = $origin;
} else {
$header['Access-Control-Allow-Origin'] = '*';
}
}
// 处理 OPTIONS 预检请求,直接返回
if ($request->method() === 'OPTIONS') {
$header['Access-Control-Allow-Credentials'] = 'true';
$header['Access-Control-Max-Age'] = 1800;
$header['Access-Control-Allow-Methods'] = 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
$header['Access-Control-Allow-Headers'] = 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With';
\think\facade\Log::record('CORS OPTIONS preflight for: ' . $origin);
return response('', 200, $header);
}
$header['Access-Control-Allow-Credentials'] = 'true';
$header['Access-Control-Max-Age'] = 1800;
$header['Access-Control-Allow-Methods'] = 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
$header['Access-Control-Allow-Headers'] = 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With';
return $next($request)->header($header);
}
}
+16 -108
View File
@@ -1,114 +1,22 @@
<?php
use think\facade\Route;
// 首页路由
Route::get('/', 'app\\admin\\controller\\Index@index');
Route::get('index/index', 'app\\admin\\controller\\Index@index');
/**
* 自动加载子路由文件
* 路由文件存放在 routes/ 目录下
*/
$routesPath = __DIR__ . '/routes';
// 检查路由目录是否存在
if (is_dir($routesPath)) {
// 获取所有 .php 文件
$routeFiles = glob($routesPath . '/*.php');
// 静态文件路由 - 必须在其他路由之前定义,使用通配符匹配所有storage路径
Route::get('storage/:path', 'app\\admin\\controller\\StorageController@index')->pattern(['path' => '.*']);
// 按名称排序,确保加载顺序
sort($routeFiles);
// 登录路由
Route::post('login', 'app\\admin\\controller\\LoginController@login');
Route::post('logout', 'app\\admin\\controller\\LoginController@logout');
Route::get('user/info', 'app\\admin\\controller\\LoginController@userInfo');
// 菜单路由
Route::get('allmenu', 'app\\admin\\controller\\MenuController@getAllMenus');
Route::get('menu/:id', 'app\\admin\\controller\\MenuController@getMenus');
Route::post('createMenu', 'app\\admin\\controller\\MenuController@createMenu');
Route::put('updateMenu/:id', 'app\\admin\\controller\\MenuController@updateMenu');
Route::delete('deleteMenu/:id', 'app\\admin\\controller\\MenuController@deleteMenu');
Route::patch('menu/status/:id', 'app\\admin\\controller\\MenuController@updateMenuStatus');
// 前端导航路由
Route::get('frontmenus', 'app\\admin\\controller\\FrontMenuController@getFrontMenus');
Route::post('createfrontmenu', 'app\\admin\\controller\\FrontMenuController@createFrontMenu');
Route::post('editfrontmenu/:id', 'app\\admin\\controller\\FrontMenuController@editFrontMenu');
Route::delete('deletefrontmenu/:id', 'app\\admin\\controller\\FrontMenuController@deleteFrontMenu');
// Banner路由
Route::get('allbanners', 'app\\admin\\controller\\BannerController@getAllBanners');
Route::post('createbanner', 'app\\admin\\controller\\BannerController@createBanner');
Route::post('editbanner/:id', 'app\\admin\\controller\\BannerController@editBanner');
Route::delete('deletebanner/:id', 'app\\admin\\controller\\BannerController@deleteBanner');
// 单页路由
Route::get('allonepages', 'app\\admin\\controller\\OnePageController@getAllOnePages');
Route::post('createonepage', 'app\\admin\\controller\\OnePageController@createOnePage');
Route::post('editonepage/:id', 'app\\admin\\controller\\OnePageController@editOnePage');
Route::delete('deleteonepage/:id', 'app\\admin\\controller\\OnePageController@deleteOnePage');
// 文章路由
Route::get('articlesList', 'app\\admin\\controller\\Article\\ArticleController@getArticlesList');
Route::get('allarticles', 'app\\admin\\controller\\Article\\ArticleController@getAllArticles');
Route::get('articles/:id', 'app\\admin\\controller\\Article\\ArticleController@getArticle');
Route::post('createarticle', 'app\\admin\\controller\\Article\\ArticleController@createArticle');
Route::post('editarticle/:id', 'app\\admin\\controller\\Article\\ArticleController@editArticle');
Route::delete('deletearticle/:id', 'app\\admin\\controller\\Article\\ArticleController@deleteArticle');
Route::post('publisharticle/:id', 'app\\admin\\controller\\Article\\ArticleController@publishArticle');
Route::post('unPublisharticle/:id', 'app\\admin\\controller\\Article\\ArticleController@unPublishArticle');
Route::post('articleRecommend/:id', 'app\\admin\\controller\\Article\\ArticleController@articleRecommend');
Route::post('unArticleRecommend/:id', 'app\\admin\\controller\\Article\\ArticleController@unArticleRecommend');
Route::post('articleTop/:id', 'app\\admin\\controller\\Article\\ArticleController@articleTop');
Route::post('unArticleTop/:id', 'app\\admin\\controller\\Article\\ArticleController@unArticleTop');
// 文章分类路由
Route::get('allcategories', 'app\\admin\\controller\\Article\\ArticleCategoryController@getAllArticleCategories');
Route::get('categories', 'app\\admin\\controller\\Article\\ArticleCategoryController@getArticleCategories');
Route::delete('categories/:id', 'app\\admin\\controller\\Article\\ArticleCategoryController@deleteCategory');
Route::post('createCategory', 'app\\admin\\controller\\Article\\ArticleCategoryController@createCategory');
Route::post('editCategory/:id', 'app\\admin\\controller\\Article\\ArticleCategoryController@editCategory');
// 文件路由
Route::get('usercate', 'app\\admin\\controller\\FileController@getUserCate');
Route::get('allfiles', 'app\\admin\\controller\\FileController@getAllFiles');
Route::get('catefiles/:id', 'app\\admin\\controller\\FileController@getCateFiles');
Route::post('uploadfile', 'app\\admin\\controller\\FileController@uploadFile');
Route::post('updatefile/:id', 'app\\admin\\controller\\FileController@updateFile');
Route::delete('deletefile/:id', 'app\\admin\\controller\\FileController@deleteFile');
Route::get('movefile/:id', 'app\\admin\\controller\\FileController@moveFile');
Route::post('createfilecate', 'app\\admin\\controller\\FileController@createFileCate');
Route::post('renamefilecate/:id', 'app\\admin\\controller\\FileController@renameFileCate');
Route::delete('deletefilecate/:id', 'app\\admin\\controller\\FileController@deleteFileCate');
// 用户路由
Route::get('getAllUsers', 'app\\admin\\controller\\UserController@getAllUsers');
Route::get('getUserInfo/:id', 'app\\admin\\controller\\UserController@getUserInfo');
Route::post('addUser', 'app\\admin\\controller\\UserController@addUser');
Route::post('editUser/:id', 'app\\admin\\controller\\UserController@editUser');
Route::delete('deleteUser/:id', 'app\\admin\\controller\\UserController@deleteUser');
Route::post('changePassword', 'app\\admin\\controller\\UserController@changePassword');
// 角色路由
Route::get('allRoles', 'app\\admin\\controller\\RoleController@getAllRoles');
Route::get('roles/:id', 'app\\admin\\controller\\RoleController@getRoleById');
Route::post('roles', 'app\\admin\\controller\\RoleController@createRole');
Route::put('roles/:id', 'app\\admin\\controller\\RoleController@updateRole');
Route::delete('roles/:id', 'app\\admin\\controller\\RoleController@deleteRole');
// 操作日志路由(注意:具体路径要在动态路径之前)
Route::get('operationLogs/statistics', 'app\\admin\\controller\\OperationLog\\OperationLogController@getOperationStatistics');
Route::get('operationLogs/:id', 'app\\admin\\controller\\OperationLog\\OperationLogController@getOperationLogDetail');
Route::get('operationLogs', 'app\\admin\\controller\\OperationLog\\OperationLogController@getOperationLogs');
// 站点设置路由
Route::get('normalInfos', 'app\\admin\\controller\\SiteSettingsController@getNormalInfos');
Route::post('saveNormalInfos', 'app\\admin\\controller\\SiteSettingsController@saveNormalInfos');
Route::get('legalInfos', 'app\\admin\\controller\\SiteSettingsController@getLegalInfos');
Route::post('saveLegalInfos', 'app\\admin\\controller\\SiteSettingsController@saveLegalInfos');
// 数据统计路由
Route::get('contentstats', 'app\\admin\\controller\\AnalyticsController@getContentStats');
Route::get('usersstats', 'app\\admin\\controller\\AnalyticsController@getUserStats');
// 模块管理路由
Route::get('modules/list', 'app\\admin\\controller\\ModulesController@getList');
Route::get('modules/:id', 'app\\admin\\controller\\ModulesController@getDetail');
Route::post('modules', 'app\\admin\\controller\\ModulesController@add');
Route::put('modules/:id', 'app\\admin\\controller\\ModulesController@edit');
Route::delete('modules/:id', 'app\\admin\\controller\\ModulesController@delete');
Route::post('modules/batchDelete', 'app\\admin\\controller\\ModulesController@batchDelete');
Route::post('modules/status', 'app\\admin\\controller\\ModulesController@changeStatus');
Route::get('modules/select/list', 'app\\admin\\controller\\ModulesController@getSelectList');
// 加载每个路由文件
foreach ($routeFiles as $file) {
require_once $file;
}
}
+6
View File
@@ -0,0 +1,6 @@
<?php
use think\facade\Route;
// 数据统计路由
Route::get('contentstats', 'app\\admin\\controller\\Cms\\Analytics\\AnalyticsController@getContentStats');
Route::get('usersstats', 'app\\admin\\controller\\Cms\\Analytics\\AnalyticsController@getUserStats');
+23
View File
@@ -0,0 +1,23 @@
<?php
use think\facade\Route;
// 文章路由
Route::get('articlesList', 'app\\admin\\controller\\Cms\\Article\\ArticleController@getArticlesList');
Route::get('allarticles', 'app\\admin\\controller\\Cms\\Article\\ArticleController@getAllArticles');
Route::get('articles/:id', 'app\\admin\\controller\\Cms\\Article\\ArticleController@getArticle');
Route::post('createarticle', 'app\\admin\\controller\\Cms\\Article\\ArticleController@createArticle');
Route::post('editarticle/:id', 'app\\admin\\controller\\Cms\\Article\\ArticleController@editArticle');
Route::delete('deletearticle/:id', 'app\\admin\\controller\\Cms\\Article\\ArticleController@deleteArticle');
Route::post('publisharticle/:id', 'app\\admin\\controller\\Cms\\Article\\ArticleController@publishArticle');
Route::post('unPublisharticle/:id', 'app\\admin\\controller\\Cms\\Article\\ArticleController@unPublishArticle');
Route::post('articleRecommend/:id', 'app\\admin\\controller\\Cms\\Article\\ArticleController@articleRecommend');
Route::post('unArticleRecommend/:id', 'app\\admin\\controller\\Cms\\Article\\ArticleController@unArticleRecommend');
Route::post('articleTop/:id', 'app\\admin\\controller\\Cms\\Article\\ArticleController@articleTop');
Route::post('unArticleTop/:id', 'app\\admin\\controller\\Cms\\Article\\ArticleController@unArticleTop');
// 文章分类路由
Route::get('allcategories', 'app\\admin\\controller\\Cms\\Article\\ArticleCategoryController@getAllArticleCategories');
Route::get('categories', 'app\\admin\\controller\\Cms\\Article\\ArticleCategoryController@getArticleCategories');
Route::delete('categories/:id', 'app\\admin\\controller\\Cms\\Article\\ArticleCategoryController@deleteCategory');
Route::post('createCategory', 'app\\admin\\controller\\Cms\\Article\\ArticleCategoryController@createCategory');
Route::post('editCategory/:id', 'app\\admin\\controller\\Cms\\Article\\ArticleCategoryController@editCategory');
+34
View File
@@ -0,0 +1,34 @@
<?php
use think\facade\Route;
// 宝贝管理路由
Route::group('babys', function() {
// 获取宝贝列表
Route::get('list', 'app\admin\controller\BabyHealth\BabysController/getBabyList');
// 获取宝贝详情
Route::get(':id', 'app\admin\controller\BabyHealth\BabysController/getBabyDetail');
// 创建宝贝
Route::post('create', 'app\admin\controller\BabyHealth\BabysController/createBaby');
// 更新宝贝
Route::post('update/:id', 'app\admin\controller\BabyHealth\BabysController/editBaby');
// 删除宝贝
Route::delete('delete/:id', 'app\admin\controller\BabyHealth\BabysController/deleteBaby');
// 获取父母信息
Route::get('parents/:id', 'app\admin\controller\BabyHealth\BabysController/getParents');
// 绑定父母
Route::post('bind-parents/:id', 'app\admin\controller\BabyHealth\BabysController/bindParent');
});
// 用户管理路由
Route::group('babyhealthUser', function() {
Route::get('list', 'app\admin\controller\BabyHealth\UserController/getUserList');
Route::get('detail/:id', 'app\admin\controller\BabyHealth\UserController/getUserDetail');
Route::post('create', 'app\admin\controller\BabyHealth\UserController/createUser');
Route::post('update/:id', 'app\admin\controller\BabyHealth\UserController/updateUser');
Route::delete('delete/:id', 'app\admin\controller\BabyHealth\UserController/deleteUser');
});
// 统计路由
Route::group('babyhealthDashborad', function() {
Route::get('dashborad', 'app\admin\controller\BabyHealth\DashboradController/getDashborad');
});
+8
View File
@@ -0,0 +1,8 @@
<?php
use think\facade\Route;
// Banner路由
Route::get('allbanners', 'app\\admin\\controller\\Cms\\Banner\\BannerController@getAllBanners');
Route::post('createbanner', 'app\\admin\\controller\\Cms\\Banner\\BannerController@createBanner');
Route::post('editbanner/:id', 'app\\admin\\controller\\Cms\\Banner\\BannerController@editBanner');
Route::delete('deletebanner/:id', 'app\\admin\\controller\\Cms\\Banner\\BannerController@deleteBanner');
+27
View File
@@ -0,0 +1,27 @@
<?php
use think\facade\Route;
// 首页路由
Route::get('/', 'app\\admin\\controller\\Index@index');
Route::get('index/index', 'app\\admin\\controller\\Index@index');
// 静态文件路由 - 必须在其他路由之前定义,使用通配符匹配所有storage路径
Route::get('storage/:path', 'app\\admin\\controller\\StorageController@index')->pattern(['path' => '.*']);
// 登录路由
Route::post('loginInfo', 'app\\admin\\controller\\LoginController@loginInfo');
Route::post('login', 'app\\admin\\controller\\LoginController@login');
Route::post('sendLoginCode', 'app\\admin\\controller\\LoginController@sendLoginCode');
Route::post('loginBySms', 'app\\admin\\controller\\LoginController@loginBySms');
Route::post('sendRegisterCode', 'app\\admin\\controller\\LoginController@sendRegisterCode');
Route::post('sendResetCode', 'app\\admin\\controller\\LoginController@sendResetCode');
Route::post('register', 'app\\admin\\controller\\LoginController@register');
Route::post('resetPassword', 'app\\admin\\controller\\LoginController@resetPassword');
Route::post('logout', 'app\\admin\\controller\\LoginController@logout');
Route::get('user/info', 'app\\admin\\controller\\LoginController@userInfo');
// 极验路由
Route::get('login/getGeetest3Infos', 'app\\admin\\controller\\LoginController@getGeetest3Infos');
Route::get('login/getGeetest4Infos', 'app\\admin\\controller\\LoginController@getGeetest4Infos');
Route::get('login/getOpenVerify', 'app\\admin\\controller\\LoginController@getOpenVerify');
+8
View File
@@ -0,0 +1,8 @@
<?php
use think\facade\Route;
// 需求路由
Route::get('demandList', 'app\\admin\\controller\\Cms\\Demand\\DemandController@getDemandList');
Route::post('addDemand', 'app\\admin\\controller\\Cms\\Demand\\DemandController@addDemand');
Route::post('editDemand/:id', 'app\\admin\\controller\\Cms\\Demand\\DemandController@editDemand');
Route::post('deleteDemand/:id', 'app\\admin\\controller\\Cms\\Demand\\DemandController@deleteDemand');
+22
View File
@@ -0,0 +1,22 @@
<?php
use think\facade\Route;
// 主域名池管理路由
Route::group('domain/pool', function () {
Route::get('index', 'app\admin\controller\Cms\Domain\DomainPoolController/index');
Route::get('getEnabledDomains', 'app\admin\controller\Cms\Domain\DomainPoolController/getEnabledDomains');
Route::post('create', 'app\admin\controller\Cms\Domain\DomainPoolController/create');
Route::post('update', 'app\admin\controller\Cms\Domain\DomainPoolController/update');
Route::delete('delete/:id', 'app\admin\controller\Cms\Domain\DomainPoolController/delete');
Route::post('toggleStatus', 'app\admin\controller\Cms\Domain\DomainPoolController/toggleStatus');
});
// 租户域名绑定路由
Route::group('domain/tenant', function () {
Route::get('index', 'app\admin\controller\Cms\Domain\TenantDomainController/index');
Route::get('myDomains', 'app\admin\controller\Cms\Domain\TenantDomainController/myDomains');
Route::post('apply', 'app\admin\controller\Cms\Domain\TenantDomainController/apply');
Route::post('audit', 'app\admin\controller\Cms\Domain\TenantDomainController/audit');
Route::post('toggleStatus', 'app\admin\controller\Cms\Domain\TenantDomainController/toggleStatus');
Route::delete('delete/:id', 'app\admin\controller\Cms\Domain\TenantDomainController/delete');
});
+12
View File
@@ -0,0 +1,12 @@
<?php
use think\facade\Route;
// 邮箱管理路由
Route::group('email', function () {
// 获取邮箱信息
Route::get('info', 'app\admin\controller\System\EmailController/getEmailInfo');
// 编辑邮箱信息
Route::post('editinfo', 'app\admin\controller\System\EmailController/editEmailInfo');
// 发送测试邮件
Route::post('sendtestemail', 'app\admin\controller\System\EmailController/sendTestEmail');
});
+22
View File
@@ -0,0 +1,22 @@
<?php
use think\facade\Route;
// 组织机构管理路由
Route::group('erp', function() {
Route::get('getOrganization', 'app\admin\controller\Erp\OrganizationController/getOrganization');
Route::get('getOrganizationDetail/:id', 'app\admin\controller\Erp\OrganizationController/getOrganizationDetail');
Route::post('createOrganization', 'app\admin\controller\Erp\OrganizationController/createOrganization');
Route::post('editOrganization/:id', 'app\admin\controller\Erp\OrganizationController/editOrganization');
Route::delete('deleteOrganization/:id', 'app\admin\controller\Erp\OrganizationController/deleteOrganization');
Route::get('getCompanys', 'app\admin\controller\Erp\OrganizationController/getCompanys');
Route::get('getDepartments', 'app\admin\controller\Erp\OrganizationController/getDepartments');
});
// 员工管理路由
Route::group('erp', function() {
Route::get('getEmployee', 'app\admin\controller\Erp\EmployeeController/getEmployee');
Route::get('getEmployeeDetail/:id', 'app\admin\controller\Erp\EmployeeController/getEmployeeDetail');
Route::post('createEmployee', 'app\admin\controller\Erp\EmployeeController/createEmployee');
Route::post('editEmployee/:id', 'app\admin\controller\Erp\EmployeeController/editEmployee');
Route::delete('deleteEmployee/:id', 'app\admin\controller\Erp\EmployeeController/deleteEmployee');
});
+22
View File
@@ -0,0 +1,22 @@
<?php
use think\facade\Route;
// 文件路由
Route::get('usercate', 'app\\admin\\controller\\System\\FileController@getUserCate');
Route::get('allfiles', 'app\\admin\\controller\\System\\FileController@getAllFiles');
Route::get('catefiles/:id', 'app\\admin\\controller\\System\\FileController@getCateFiles');
Route::post('uploadfile', 'app\\admin\\controller\\System\\FileController@uploadFile');
Route::post('uploadfiles', 'app\\admin\\controller\\System\\FileController@uploadFile');
Route::post('updatefile/:id', 'app\\admin\\controller\\System\\FileController@updateFile');
Route::delete('deletefile/:id', 'app\\admin\\controller\\System\\FileController@deleteFile');
Route::get('movefile/:id', 'app\\admin\\controller\\System\\FileController@moveFile');
Route::post('createfilecate', 'app\\admin\\controller\\System\\FileController@createFileCate');
Route::post('renamefilecate/:id', 'app\\admin\\controller\\System\\FileController@renameFileCate');
Route::delete('deletefilecate/:id', 'app\\admin\\controller\\System\\FileController@deleteFileCate');
Route::post('uploadavatar', 'app\\admin\\controller\\System\\FileController@uploadAvatar');
Route::post('uploadavatar/:id', 'app\\admin\\controller\\System\\FileController@updateAvatar');
Route::post('batchdeletefiles', 'app\\admin\\controller\\System\\FileController@batchDeleteFiles');
Route::post('batchDeleteFilesPermanently', 'app\\admin\\controller\\System\\FileController@batchDeleteFilesPermanently');
Route::post('batchMoveFiles', 'app\\admin\\controller\\System\\FileController@batchMoveFiles');
+10
View File
@@ -0,0 +1,10 @@
<?php
use think\facade\Route;
// 友情链接路由
Route::get('friendlinks', 'app\admin\controller\Cms\Friendlink\FriendlinkController@getList');
Route::get('friendlinks/all', 'app\admin\controller\Cms\Friendlink\FriendlinkController@getAll');
Route::post('friendlinks', 'app\admin\controller\Cms\Friendlink\FriendlinkController@add');
Route::put('friendlinks/:id', 'app\admin\controller\Cms\Friendlink\FriendlinkController@update');
Route::delete('friendlinks/:id', 'app\admin\controller\Cms\Friendlink\FriendlinkController@delete');
Route::post('friendlinks/batchdelete', 'app\admin\controller\Cms\Friendlink\FriendlinkController@batchDelete');
+8
View File
@@ -0,0 +1,8 @@
<?php
use think\facade\Route;
// 前端导航路由
Route::get('frontmenus', 'app\\admin\\controller\\Cms\\FrontMenu\\FrontMenuController@getFrontMenus');
Route::post('createfrontmenu', 'app\\admin\\controller\\Cms\\FrontMenu\\FrontMenuController@createFrontMenu');
Route::post('editfrontmenu/:id', 'app\\admin\\controller\\Cms\\FrontMenu\\FrontMenuController@editFrontMenu');
Route::delete('deletefrontmenu/:id', 'app\\admin\\controller\\Cms\\FrontMenu\\FrontMenuController@deleteFrontMenu');
+10
View File
@@ -0,0 +1,10 @@
<?php
use think\facade\Route;
// 菜单路由
Route::get('allmenu', 'app\\admin\\controller\\MenuController@getAllMenus');
Route::get('menu/:id', 'app\\admin\\controller\\MenuController@getMenus');
Route::post('createMenu', 'app\\admin\\controller\\MenuController@createMenu');
Route::put('updateMenu/:id', 'app\\admin\\controller\\MenuController@updateMenu');
Route::delete('deleteMenu/:id', 'app\\admin\\controller\\MenuController@deleteMenu');
Route::patch('menu/status/:id', 'app\\admin\\controller\\MenuController@updateMenuStatus');
+8
View File
@@ -0,0 +1,8 @@
<?php
use think\facade\Route;
// 模块中心路由
Route::get('moduleCategory', 'app\\admin\\controller\\ModuleCenterController@getModuleCategory');
Route::get('moduleCenter/modules', 'app\\admin\\controller\\ModuleCenterController@getModuleList');
Route::post('moduleCenter/editCategory', 'app\\admin\\controller\\ModuleCenterController@editCategory');
Route::post('moduleCenter/editModules', 'app\\admin\\controller\\ModuleCenterController@editModules');
+13
View File
@@ -0,0 +1,13 @@
<?php
use think\facade\Route;
// 模块管理路由
Route::get('modules/list', 'app\\admin\\controller\\ModulesController@getList');
Route::get('modules/getTenantList', 'app\\admin\\controller\\ModulesController@getTenantList');
Route::get('modules/:id', 'app\\admin\\controller\\ModulesController@getDetail');
Route::post('modules', 'app\\admin\\controller\\ModulesController@add');
Route::put('modules/:id', 'app\\admin\\controller\\ModulesController@edit');
Route::delete('modules/:id', 'app\\admin\\controller\\ModulesController@delete');
Route::post('modules/batchDelete', 'app\\admin\\controller\\ModulesController@batchDelete');
Route::post('modules/status', 'app\\admin\\controller\\ModulesController@changeStatus');
Route::get('modules/select/list', 'app\\admin\\controller\\ModulesController@getSelectList');
+8
View File
@@ -0,0 +1,8 @@
<?php
use think\facade\Route;
// 单页路由
Route::get('allonepages', 'app\\admin\\controller\\Cms\\OnePage\\OnePageController@getAllOnePages');
Route::post('createonepage', 'app\\admin\\controller\\Cms\\OnePage\\OnePageController@createOnePage');
Route::post('editonepage/:id', 'app\\admin\\controller\\Cms\\OnePage\\OnePageController@editOnePage');
Route::delete('deleteonepage/:id', 'app\\admin\\controller\\Cms\\OnePage\\OnePageController@deleteOnePage');
+7
View File
@@ -0,0 +1,7 @@
<?php
use think\facade\Route;
// 操作日志路由(注意:具体路径要在动态路径之前)
Route::get('operationLogs/statistics', 'app\\admin\\controller\\OperationLog\\OperationLogController@getOperationStatistics');
Route::get('operationLogs/:id', 'app\\admin\\controller\\OperationLog\\OperationLogController@getOperationLogDetail');
Route::get('operationLogs', 'app\\admin\\controller\\OperationLog\\OperationLogController@getOperationLogs');
+14
View File
@@ -0,0 +1,14 @@
<?php
use think\facade\Route;
// 特色产品路由
Route::get('productsList', 'app\admin\controller\Cms\Products\ProductsController@productsList');
Route::post('addProducts', 'app\admin\controller\Cms\Products\ProductsController@addProducts');
Route::put('editProducts/:id', 'app\admin\controller\Cms\Products\ProductsController@editProducts');
Route::delete('deleteProducts/:id', 'app\admin\controller\Cms\Products\ProductsController@deleteProducts');
// 产品分类
Route::get('productsTypesList', 'app\admin\controller\Cms\Products\ProductsTypesController@productsTypesList');
Route::post('addProductsTypes', 'app\admin\controller\Cms\Products\ProductsTypesController@addProductsTypes');
Route::put('editProductsTypes/:id', 'app\admin\controller\Cms\Products\ProductsTypesController@editProductsTypes');
Route::delete('deleteProductsTypes/:id', 'app\admin\controller\Cms\Products\ProductsTypesController@deleteProductsTypes');
+9
View File
@@ -0,0 +1,9 @@
<?php
use think\facade\Route;
// 角色路由
Route::get('allRoles', 'app\\admin\\controller\\System\\RoleController@getAllRoles');
Route::get('roles/:id', 'app\\admin\\controller\\System\\RoleController@getRoleById');
Route::post('roles', 'app\\admin\\controller\\System\\RoleController@createRole');
Route::put('roles/:id', 'app\\admin\\controller\\System\\RoleController@updateRole');
Route::delete('roles/:id', 'app\\admin\\controller\\System\\RoleController@deleteRole');
+8
View File
@@ -0,0 +1,8 @@
<?php
use think\facade\Route;
// 特色服务路由
Route::get('servicesList', 'app\admin\controller\Cms\Services\ServicesController@servicesList');
Route::post('addServices', 'app\admin\controller\Cms\Services\ServicesController@addServices');
Route::put('editServices/:id', 'app\admin\controller\Cms\Services\ServicesController@editServices');
Route::delete('deleteServices/:id', 'app\admin\controller\Cms\Services\ServicesController@deleteServices');
+14
View File
@@ -0,0 +1,14 @@
<?php
use think\facade\Route;
// 站点设置路由
Route::get('normalInfos', 'app\\admin\\controller\\SiteSettingsController@getNormalInfos');
Route::post('saveNormalInfos', 'app\\admin\\controller\\SiteSettingsController@saveNormalInfos');
Route::get('loginVerifyInfos', 'app\\admin\\controller\\SiteSettingsController@getVerifyInfos');
Route::post('saveloginVerifyInfos', 'app\\admin\\controller\\SiteSettingsController@saveVerifyInfos');
Route::get('legalInfos', 'app\\admin\\controller\\SiteSettingsController@getLegalInfos');
Route::post('saveLegalInfos', 'app\\admin\\controller\\SiteSettingsController@saveLegalInfos');
Route::get('companyInfos', 'app\\admin\\controller\\SiteSettingsController@getCompanyInfos');
Route::post('saveCompanyInfos', 'app\\admin\\controller\\SiteSettingsController@saveCompanyInfos');
Route::get('companySeo', 'app\\admin\\controller\\SiteSettingsController@getCompanySeo');
Route::post('saveCompanySeo', 'app\\admin\\controller\\SiteSettingsController@saveCompanySeo');
+33
View File
@@ -0,0 +1,33 @@
<?php
use think\facade\Route;
// 短信管理路由
Route::group('sms', function () {
// 获取短信网关配置
Route::get('info', 'app\\admin\\controller\\System\\SmsController@getSmsInfo');
// 编辑短信网关配置
Route::post('editinfo', 'app\\admin\\controller\\System\\SmsController@editSmsInfo');
// 发送测试短信(入队任务)
Route::post('sendtest', 'app\\admin\\controller\\System\\SmsController@sendTestSms');
// Android 网关轮询待发送任务
Route::post('gateway/poll', 'app\\admin\\controller\\System\\SmsController@gatewayPoll');
// 兼容:如果网关端只请求 poll(不带 gateway
Route::post('poll', 'app\\admin\\controller\\System\\SmsController@gatewayPoll');
Route::get('poll', 'app\\admin\\controller\\System\\SmsController@gatewayPoll');
// Android 网关上报短信内容(解析验证码)
Route::post('gateway/report', 'app\\admin\\controller\\System\\SmsController@gatewayReport');
// 兼容:如果网关端只请求 report(不带 gateway
Route::post('report', 'app\\admin\\controller\\System\\SmsController@gatewayReport');
// 管理员:短信任务列表(租户隔离)
Route::get('taskList', 'app\\admin\\controller\\System\\SmsController@getSmsTaskList');
// 管理员:编辑短信任务(租户隔离)
Route::post('taskEdit/:id', 'app\\admin\\controller\\System\\SmsController@editSmsTask');
});
+13
View File
@@ -0,0 +1,13 @@
<?php
use think\facade\Route;
// 租户管理路由
Route::group('tenant', function() {
Route::get('getTenant', 'app\admin\controller\Tenant\TenantController/getTenant');
Route::get('getTenantDetail/:id', 'app\admin\controller\Tenant\TenantController/getTenantDetail');
Route::post('createTenant', 'app\admin\controller\Tenant\TenantController/createTenant');
Route::post('editTenant/:id', 'app\admin\controller\Tenant\TenantController/editTenant');
Route::delete('deleteTenant/:id', 'app\admin\controller\Tenant\TenantController/deleteTenant');
Route::get('findTenantCode', 'app\admin\controller\Tenant\TenantController/findTenantCode');
});
+8
View File
@@ -0,0 +1,8 @@
<?php
use think\facade\Route;
// 模板管理路由
Route::get('theme', 'app\admin\controller\Cms\Theme\ThemeController@index');
Route::post('theme/switch', 'app\admin\controller\Cms\Theme\ThemeController@switch');
Route::get('theme/data', 'app\admin\controller\Cms\Theme\ThemeController@getData');
Route::post('theme/data', 'app\admin\controller\Cms\Theme\ThemeController@saveData');
+11
View File
@@ -0,0 +1,11 @@
<?php
use think\facade\Route;
// 用户路由
Route::get('getAllUsers', 'app\\admin\\controller\\UserController@getAllUsers');
Route::get('getTenantUsers/:tenantId', 'app\\admin\\controller\\UserController@getTenantUsers');
Route::get('getUserInfo/:id', 'app\\admin\\controller\\UserController@getUserInfo');
Route::post('addUser', 'app\\admin\\controller\\UserController@addUser');
Route::post('editUser/:id', 'app\\admin\\controller\\UserController@editUser');
Route::delete('deleteUser/:id', 'app\\admin\\controller\\UserController@deleteUser');
Route::post('changePassword', 'app\\admin\\controller\\UserController@changePassword');
+102
View File
@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
// 关键修正:命名空间对应控制器目录
namespace app\api;
use think\App;
use think\exception\ValidateException;
use think\Validate;
use think\Request;
// 在控制器方法中添加
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
/**
* 控制器基础类
*/
abstract class BaseController
{
/**
* Request实例
* @var Request
*/
protected $request;
/**
* 应用实例
* @var App
*/
protected $app;
/**
* 是否批量验证
* @var bool
*/
protected $batchValidate = false;
/**
* 控制器中间件
* @var array
*/
protected $middleware = [];
/**
* 构造方法
* @access public
* @param App $app 应用对象
*/
public function __construct(App $app)
{
$this->app = $app;
$this->request = $this->app->request;
// 控制器初始化
$this->initialize();
}
// 初始化
protected function initialize()
{
}
/**
* 验证数据
* @access protected
* @param array $data 数据
* @param string|array $validate 验证器名或者验证规则数组
* @param array $message 提示信息
* @param bool $batch 是否批量验证
* @return array|string|true
* @throws ValidateException
*/
protected function validate(array $data, string|array $validate, array $message = [], bool $batch = false)
{
if (is_array($validate)) {
$v = new Validate();
$v->rule($validate);
} else {
if (strpos($validate, '.')) {
// 支持场景
[$validate, $scene] = explode('.', $validate);
}
$class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
$v = new $class();
if (!empty($scene)) {
$v->scene($scene);
}
}
$v->message($message);
// 是否批量验证
if ($batch || $this->batchValidate) {
$v->batch(true);
}
return $v->failException(true)->check($data);
}
}
+2
View File
@@ -0,0 +1,2 @@
<?php
// 这是系统自动生成的公共文件
+19
View File
@@ -0,0 +1,19 @@
<?php
declare (strict_types = 1);
namespace app\api\controller;
use app\api\BaseController;
class Index extends BaseController
{
public function index()
{
return '后端运行中...';
}
public function hello($name = 'ThinkPHP8')
{
return 'hello,' . $name;
}
}
+177
View File
@@ -0,0 +1,177 @@
<?php
declare(strict_types=1);
namespace app\api\controller;
use app\api\BaseController;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Cache;
use think\response\Json;
use app\service\JwtService;
use app\model\System\AdminUser;
use app\model\System\SystemSiteSettings;
class LoginController extends BaseController
{
private function generateToken($userInfo): string
{
return JwtService::generateToken($userInfo);
}
private function verifyToken($token): ?array
{
return JwtService::verifyToken($token);
}
/**
* 登录接口
* @return Json
*/
public function login(): Json
{
$data = $this->request->param();
if (isset($data['email'])) {
$data['account'] = $data['email'];
} elseif (isset($data['phone'])) {
$data['account'] = $data['phone'];
}
try {
$this->validate($data, [
'account|账号' => 'require|length:3,32',
'password|密码' => 'require|length:6,32'
]);
} catch (ValidateException $e) {
return json([
'code' => 400,
'msg' => $e->getError()
]);
}
$user = AdminUser::where('account', $data['account'])
->where('status', 1)
->where('delete_time', null)
->find();
if (!$user) {
return json([
'code' => 401,
'msg' => '账号不存在或已禁用'
]);
}
if (md5($data['password']) !== $user['password']) {
return json([
'code' => 401,
'msg' => '密码错误'
]);
}
AdminUser::where('id', $user['id'])->update([
'login_count' => $user['login_count'] + 1,
'last_login_ip' => $this->request->ip()
]);
$userInfo = [
'id' => $user['id'],
'account' => $user['account'],
'name' => $user['name'],
'tid' => $user['tid'],
'group_id' => $user['group_id']
];
$token = $this->generateToken($userInfo);
return json([
'code' => 200,
'msg' => '登录成功',
'data' => [
'token' => $token,
'user' => $userInfo
]
]);
}
/**
* 退出登录
* @return Json
*/
public function logout(): Json
{
$authHeader = $this->request->header('Authorization', '');
$userInfo = null;
return json([
'code' => 200,
'msg' => '退出成功'
]);
}
/**
* 获取当前登录用户信息
* @return Json
*/
public function userInfo(): Json
{
$authHeader = $this->request->header('Authorization', '');
if (!preg_match('/Bearer\s+(.+)/i', $authHeader, $matches)) {
return json([
'code' => 401,
'msg' => '未登录'
]);
}
$tokenData = $this->verifyToken($matches[1]);
if (!$tokenData || !isset($tokenData['user'])) {
return json([
'code' => 401,
'msg' => 'Token无效'
]);
}
$user = (array)$tokenData['user'];
$user_id = $user['id'];
$userData = AdminUser::where('id', $user_id)
->where('delete_time', null)
->field('id, account, name, phone, qq, sex, group_id, status, create_time, update_time')
->find();
if (!$userData) {
return json([
'code' => 404,
'msg' => '用户不存在'
]);
}
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $userData->toArray()
]);
}
public function getAdminUserFromToken(): array
{
return JwtService::getUserFromHeader($this->request->header('Authorization', ''));
}
public function loginInfo()
{
$loginInfo = SystemSiteSettings::select();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $loginInfo
]);
}
}
+144
View File
@@ -0,0 +1,144 @@
<?php
declare(strict_types=1);
namespace app\api\controller;
use app\api\BaseController;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Session;
use think\response\Json;
use app\model\System\AdminUser;
class UserController extends BaseController
{
/**
* 获取所有用户信息
* @return Json
*/
public function getAllUsers()
{
$users = AdminUser::where('delete_time', null)->field('id, account, name, phone, birth, email, qq, sex, group_id, status, last_login_ip, login_count, create_time, update_time')->select()->toArray();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'list' => $users,
'total' => count($users)
]
]);
}
/**
* 获取用户信息
* @return Json
*/
public function getUserInfo(int $id)
{
$user = AdminUser::where('id', $id)
->field('id, account, name, phone, email, birth, qq, sex, group_id, status, create_time, last_login_ip')
->find();
// 记录操作日志
$this->logSuccess('用户管理', '获取用户信息', ['id' => $id]);
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $user
]);
}
/**
* 修改密码
* @return Json
*/
public function changePassword(int $id, string $password)
{
try {
AdminUser::where('id', $id)->update([
'password' => md5($password),
'update_time' => date('Y-m-d H:i:s')
]);
// 记录操作日志
$this->logSuccess('用户管理', '修改密码', ['id' => $id]);
return json([
'code' => 200,
'msg' => '修改成功'
]);
} catch (\Exception $e) {
// 记录失败日志
$this->logFail('用户管理', '修改密码', $e->getMessage());
return json([
'code' => 500,
'msg' => '修改失败'
]);
}
}
/**
* 添加用户
* @return Json
*/
public function addUser()
{
$data = request()->param();
$data['password'] = md5($data['password']);
$data['create_time'] = date('Y-m-d H:i:s');
$data['update_time'] = $data['create_time'];
$data['group_id'] = 2;
$id = AdminUser::insertGetId($data);
// 记录操作日志
$this->logSuccess('用户管理', '添加用户', ['data' => $data]);
return json([
'code' => 200,
'msg' => '添加成功',
'data' => ['id' => $id]
]);
}
/**
* 编辑用户
* @return Json
*/
public function editUser(int $id)
{
$data = request()->param();
unset($data['_t'], $data['id']);
$data['update_time'] = date('Y-m-d H:i:s');
AdminUser::where('id', $id)->update($data);
$this->logSuccess('用户管理', '编辑用户', ['id' => $id]);
return json([
'code' => 200,
'msg' => '编辑成功'
]);
}
/**
* 删除用户
* @return Json
*/
public function deleteUser(int $id)
{
$user = AdminUser::where('id', $id)->where('delete_time', null)->find();
if (!$user) {
return json([
'code' => 404,
'msg' => '用户不存在或已删除'
]);
}
AdminUser::where('id', $id)->update([
'delete_time' => date('Y-m-d H:i:s')
]);
$this->logSuccess('用户管理', '删除用户', ['id' => $id]);
return json([
'code' => 200,
'msg' => '删除成功'
]);
}
}
+5
View File
@@ -0,0 +1,5 @@
<?php
// 这是系统自动生成的event定义文件
return [
];
+12
View File
@@ -0,0 +1,12 @@
<?php
// 全局中间件定义文件
return [
// 全局请求缓存
// \think\middleware\CheckRequestCache::class,
// 多语言加载
// \think\middleware\LoadLangPack::class,
// Session初始化
\app\common\middleware\AllowCrossDomain::class,
\think\middleware\SessionInit::class,
];
+22
View File
@@ -0,0 +1,22 @@
<?php
use think\facade\Route;
/**
* 自动加载子路由文件
* 路由文件存放在 routes/ 目录下
*/
$routesPath = __DIR__ . '/routes';
// 检查路由目录是否存在
if (is_dir($routesPath)) {
// 获取所有 .php 文件
$routeFiles = glob($routesPath . '/*.php');
// 按名称排序,确保加载顺序
sort($routeFiles);
// 加载每个路由文件
foreach ($routeFiles as $file) {
require_once $file;
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
use think\facade\Route;
// 登录相关路由(统一加 /api 前缀,和前端保持一致)
Route::post('loginInfo', 'app\\api\\controller\\LoginController@loginInfo');
Route::post('login', 'app\\api\\controller\\LoginController@login');
Route::post('logout', 'app\\api\\controller\\LoginController@logout');
Route::get('user/info', 'app\\api\\controller\\LoginController@userInfo');
+8
View File
@@ -0,0 +1,8 @@
<?php
use think\facade\Route;
// 模板相关路由
Route::get('theme', 'app\admin\controller\ThemeController@index');
Route::post('theme/switch', 'app\admin\controller\ThemeController@switch');
Route::get('theme/data', 'app\admin\controller\ThemeController@getData');
Route::post('theme/data', 'app\admin\controller\ThemeController@saveData');
+10
View File
@@ -0,0 +1,10 @@
<?php
use think\facade\Route;
// 用户路由
Route::get('getAllUsers', 'app\\api\\controller\\UserController@getAllUsers');
Route::get('getUserInfo/:id', 'app\\api\\controller\\UserController@getUserInfo');
Route::post('addUser', 'app\\api\\controller\\UserController@addUser');
Route::post('editUser/:id', 'app\\api\\controller\\UserController@editUser');
Route::delete('deleteUser/:id', 'app\\api\\controller\\UserController@deleteUser');
Route::post('changePassword', 'app\\api\\controller\\UserController@changePassword');
@@ -0,0 +1,36 @@
<?php
namespace app\common\middleware;
use think\Response;
use think\Request;
/**
* 公共跨域中间件
* 供所有应用共用
*/
class AllowCrossDomain
{
public function handle(Request $request, \Closure $next): Response
{
$origin = $request->header('origin', '');
// 允许所有来源(生产环境建议限制具体域名)
$header['Access-Control-Allow-Origin'] = $origin ?: '*';
if ($request->method() === 'OPTIONS') {
$header['Access-Control-Allow-Credentials'] = 'true';
$header['Access-Control-Max-Age'] = 1800;
$header['Access-Control-Allow-Methods'] = 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
$header['Access-Control-Allow-Headers'] = 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With, X-Current-Domain';
return response('', 200, $header);
}
$header['Access-Control-Allow-Credentials'] = 'true';
$header['Access-Control-Max-Age'] = 1800;
$header['Access-Control-Allow-Methods'] = 'GET, POST, PATCH, PUT, DELETE, OPTIONS';
$header['Access-Control-Allow-Headers'] = 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With, X-Current-Domain';
return $next($request)->header($header);
}
}
+69
View File
@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace app\common\middleware;
use think\facade\Db;
use think\Request;
/**
* 域名解析中间件
* 通过访问域名自动识别租户
*/
class DomainParse
{
public function handle(Request $request, \Closure $next)
{
$host = $request->host(true); // 获取完整域名,不带端口
// 你的后台/平台域名(排除,不走租户逻辑)
$adminDomains = ['back.yunzer.cn', 'api.yunzer.cn'];
$platformDomains = ['www.yunzer.cn', 'yunzer.cn'];
// 后台/平台/API域名,直接放行
if (in_array($host, $adminDomains) || in_array($host, $platformDomains)) {
return $next($request);
}
// 解析域名(兼容 yunzer.com.cn/dh2.fun
$domainParts = explode('.', $host);
$allowMainDomains = ['yunzer.com.cn', 'dh2.fun'];
$mainDomain = '';
foreach ($allowMainDomains as $domain) {
$domainLen = count(explode('.', $domain));
$currentLen = count($domainParts);
if ($currentLen > $domainLen) {
$matchMain = implode('.', array_slice($domainParts, -$domainLen));
if ($matchMain === $domain) {
$mainDomain = $matchMain;
break;
}
}
}
// 非租户主域名,放行(走API默认界面,可改为跳转到平台)
if (empty($mainDomain)) {
return $next($request);
}
// 查询租户域名绑定记录
$tenantDomain = Db::name('mete_tenant_domain')
->where('full_domain', $host)
->where('status', 1)
->find();
if ($tenantDomain) {
// 1. 写入租户ID到请求(核心,后续所有逻辑都能获取)
$request->tenantId = $tenantDomain['tid'];
$request->header('X-Tenant-Id', (string)$tenantDomain['tid']);
// 2. 额外:写入租户域名信息,方便模板使用
$request->tenantDomain = $host;
$request->tenantMainDomain = $mainDomain;
}
return $next($request);
}
}
+66 -1
View File
@@ -9,6 +9,7 @@ use think\App;
use think\exception\ValidateException;
use think\Validate;
use think\Request;
use think\facade\Db;
// 在控制器方法中添加
header('Access-Control-Allow-Origin: *');
@@ -44,6 +45,12 @@ abstract class BaseController
*/
protected $middleware = [];
/**
* 当前租户ID
* @var int
*/
protected $tenantId = 0;
/**
* 构造方法
* @access public
@@ -59,7 +66,65 @@ abstract class BaseController
}
// 初始化
protected function initialize() {}
protected function initialize()
{
// 自动检测并设置租户ID
$this->initTenantId();
}
/**
* 初始化租户ID
* 优先从 Referer 获取来源域名,否则使用当前访问域名
* mete_tenant_domain 表查询对应的 tid
*/
protected function initTenantId(): void
{
// 优先从 Referer 获取来源域名(跨域调用场景)
$referer = $this->request->header('Referer');
if ($referer) {
$host = parse_url($referer, PHP_URL_HOST);
} else {
// 否则使用当前访问域名
$host = $this->request->host(true);
}
// 查询域名对应的租户ID
$tenantDomain = Db::name('mete_tenant_domain')
->where('full_domain', $host)
->where('status', 1)
->whereNull('delete_time')
->find();
if ($tenantDomain) {
$this->tenantId = (int)$tenantDomain['tid'];
}
}
/**
* 获取当前租户ID
* @return int
*/
protected function getTenantId(): int
{
return $this->tenantId;
}
/**
* 根据域名获取租户ID(静态方法,方便全局调用)
* @return int
*/
public static function getTenantIdByDomain(string $baseUrl): int
{
// 查询域名对应的租户ID
$tenantDomain = Db::name('mete_tenant_domain')
->where('full_domain', $baseUrl)
->where('status', 1)
->whereNull('delete_time')
->find();
return $tenantDomain ? (int)$tenantDomain['tid'] : 0;
}
/**
* 验证数据
@@ -1,32 +1,33 @@
<?php
declare (strict_types = 1);
declare(strict_types=1);
namespace app\index\controller\Article;
use app\index\BaseController;
use app\model\Articles;
use app\model\ArticlesCategory;
use Symfony\Component\VarDumper\VarDumper;
use think\exception\ValidateException;
use think\facade\Request;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\Cms\Articles;
use app\model\Cms\ArticlesCategory;
class ArticleController extends BaseController
{
/**
* 获取新闻中心顶部4篇文章
* 获取技术中心顶部4篇文章
* @return Json
*/
public function getNewsCenterTop4(): Json
{
try {
// 1. 查询新闻中心主分类
$newsCenterCategory = ArticlesCategory::where('name', '新闻中心')
// 1. 查询技术中心主分类
$newsCenterCategory = ArticlesCategory::where('name', '技术中心')
->where('delete_time', null)
->find();
if (!$newsCenterCategory) {
return json([
'code' => 200,
@@ -34,18 +35,18 @@ class ArticleController extends BaseController
'list' => [],
]);
}
// 2. 获取新闻中心及其子分类的ID列表
// 2. 获取技术中心及其子分类的ID列表
$mainCategoryId = $newsCenterCategory['id'];
// 查询子分类
$subCategories = ArticlesCategory::where('cid', $mainCategoryId)
->where('delete_time', null)
->column('id');
// 合并主分类和子分类ID
$categoryIds = array_merge([$mainCategoryId], $subCategories);
if (empty($categoryIds)) {
return json([
'code' => 200,
@@ -53,7 +54,7 @@ class ArticleController extends BaseController
'list' => [],
]);
}
// 3. 查询符合条件的文章,按指定优先级排序,取最新4篇
$articles = Articles::published()
->whereIn('cate', $categoryIds)
@@ -75,7 +76,7 @@ class ArticleController extends BaseController
])
->limit(4)
->select();
// 4. 处理文章数据,去除HTML标签并生成desc字段
foreach ($articles as &$article) {
// 使用PHP的strip_tags函数去除HTML标签,然后截取前100个字符
@@ -83,7 +84,7 @@ class ArticleController extends BaseController
// 移除原始content字段,减少返回数据大小
unset($article['content']);
}
return json([
'code' => 200,
'msg' => 'success',
@@ -92,15 +93,53 @@ class ArticleController extends BaseController
} catch (\Exception $e) {
// 打印完整错误信息到日志
$errorMsg = '错误信息:' . $e->getMessage() . ' | 错误行号:' . $e->getLine() . ' | 执行SQL' . Articles::getLastSql();
trace('新闻中心文章查询失败: ' . $errorMsg, 'error');
trace('技术中心文章查询失败: ' . $errorMsg, 'error');
return json([
'code' => 500,
'msg' => '新闻中心文章查询失败,请稍后重试',
'msg' => '技术中心文章查询失败,请稍后重试',
'list' => []
]);
}
}
/**
* 获取文章分类列表(mete_articles_category:当前租户 tid、启用状态)
* 支持 baseUrl=租户前台域名,与 getCenterNews 一致,便于 api 域名跨域调用时解析 tid
*
* @return Json
*/
public function getArticleCategories(): Json
{
$baseUrl = $this->request->get('baseUrl', '');
if (!empty($baseUrl)) {
$this->tenantId = BaseController::getTenantIdByDomain($baseUrl);
}
$tid = $this->getTenantId();
if (empty($tid)) {
return json([
'code' => 400,
'msg' => '无法识别租户信息',
'list' => [],
]);
}
$articleCategories = ArticlesCategory::where('delete_time', null)
->where('tid', $tid)
->where('status', 1)
->order('sort', 'asc')
->order('id', 'asc')
->select()
->toArray();
return json([
'code' => 200,
'msg' => 'success',
'list' => $articleCategories,
]);
}
/**
* 游客文章阅读量函数
* @return Json
@@ -118,7 +157,7 @@ class ArticleController extends BaseController
try {
$article = Articles::where('id', $id)->where('delete_time', null)->find();
if (!$article) {
return json([
'code' => 404,
@@ -164,7 +203,7 @@ class ArticleController extends BaseController
try {
$article = Articles::where('id', $id)->where('delete_time', null)->find();
if (!$article) {
return json([
'code' => 404,
@@ -210,15 +249,15 @@ class ArticleController extends BaseController
try {
$article = Articles::where('id', $id)->where('delete_time', null)->find();
if (!$article) {
return json([
'code' => 404,
'msg' => '文章不存在',
'list' => []
]);
}
}
if ($article->likes <= 0) {
return json([
'code' => 400,
@@ -226,10 +265,10 @@ class ArticleController extends BaseController
'list' => []
]);
}
$article->dec('likes');
$article->save();
return json([
'code' => 200,
'msg' => '取消点赞成功',
@@ -246,4 +285,4 @@ class ArticleController extends BaseController
]);
}
}
}
}
@@ -0,0 +1,282 @@
<?php
declare(strict_types=1);
namespace app\index\controller\Article;
use app\index\BaseController;
use Symfony\Component\VarDumper\VarDumper;
use think\exception\ValidateException;
use think\facade\Request;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\Cms\Articles;
use app\model\Cms\ArticlesCategory;
use tidy;
class NewsCenterController extends BaseController
{
/**
* 根据域名获取新闻数据
*
* 返回字段:list 列表;total 当前租户下已发布新闻总数;count 当前筛选(全部或 cate)下的条数,翻页按 count page_size。
*
* @return Json
*/
public function getCenterNews(): Json
{
$baseUrl = (string) $this->request->param('baseUrl', '');
if ($baseUrl !== '') {
$this->tenantId = BaseController::getTenantIdByDomain($baseUrl);
}
$tid = $this->getTenantId();
if (empty($tid)) {
return json([
'code' => 400,
'msg' => '无法识别租户信息',
'list' => [],
]);
}
// param 合并 GET,避免个别环境下仅 get 取不到查询串
$cateId = (int) $this->request->param('cate', 0);
$listPage = (int) $this->request->param('page', 0);
$pageSizeReq = (int) $this->request->param('page_size', 0);
// 点击左侧分类:文章 cate 可能挂在子分类上,需包含「该分类 + 其下所有子分类」id
$categoryIdsForFilter = [];
if ($cateId > 0) {
$categoryIdsForFilter = $this->resolveCategoryBranchIds($cateId, $tid);
if ($categoryIdsForFilter === []) {
$categoryIdsForFilter = [$cateId];
}
}
$baseQuery = static function () use ($tid, $cateId, $categoryIdsForFilter) {
$q = Articles::published()
->where('tid', $tid)
->where('delete_time', null);
if ($cateId > 0 && $categoryIdsForFilter !== []) {
$q->whereIn('cate', $categoryIdsForFilter);
}
return $q;
};
// 当前租户下「全部」已发布新闻数(不受 cate 筛选影响)
$totalTenantNews = (int) Articles::published()
->where('tid', $tid)
->where('delete_time', null)
->count();
// 当前列表条件下的总数:tid +(可选)cate,用于翻页总页数 = ceil(count / page_size)
$countFiltered = (int) $baseQuery()->count();
$useListPagination = $listPage > 0 && $pageSizeReq > 0;
if ($useListPagination) {
// 新闻中心列表页:服务端分页;不要用查询参数名 page,避免与路由占位符冲突
$pageSizeReq = max(1, min($pageSizeReq, 50));
$listPage = max(1, $listPage);
$articles = $baseQuery()
->order('publish_date', 'desc')
->page($listPage, $pageSizeReq)
->select();
} else {
// 首页等:按 limit 取前 N 条(默认 8)
$limit = (int) $this->request->param('limit', 8);
if ($limit < 1) {
$limit = 8;
}
if ($limit > 200) {
$limit = 200;
}
$articles = $baseQuery()
->order('publish_date', 'desc')
->limit($limit)
->select();
}
// 处理图片:如果文章 image 为空,则取分类的 image
foreach ($articles as &$article) {
if (empty($article['image']) && !empty($article['cate'])) {
$category = ArticlesCategory::where('id', $article['cate'])
->where('delete_time', null)
->find();
if ($category && !empty($category['image'])) {
$article['image'] = $category['image'];
}
}
}
unset($article);
return json([
'code' => 200,
'msg' => 'success',
'list' => $articles,
// 当前租户下新闻总条数(全部状态为已发布)
'total' => $totalTenantNews,
// 当前筛选(全部或某分类)下的条数,列表翻页按 count 与 page_size 计算
'count' => $countFiltered,
]);
}
/**
* 解析分类筛选用的 id 列表:自身 + mete_articles_category cid= id 的所有子孙(同 tid、未删除)
*/
private function resolveCategoryBranchIds(int $rootId, int $tid): array
{
if ($rootId <= 0 || $tid <= 0) {
return [];
}
$root = ArticlesCategory::where('id', $rootId)
->where('tid', $tid)
->where('delete_time', null)
->find();
if (!$root) {
return [$rootId];
}
$ids = [$rootId];
$queue = [$rootId];
while ($queue !== []) {
$pid = array_shift($queue);
$children = ArticlesCategory::where('cid', $pid)
->where('tid', $tid)
->where('delete_time', null)
->column('id');
foreach ($children as $id) {
$id = (int) $id;
if ($id > 0 && !in_array($id, $ids, true)) {
$ids[] = $id;
$queue[] = $id;
}
}
}
return $ids;
}
/**
* 获取新闻详情
* @param int $id 文章ID
* @return Json
*/
public function getNewsDetail(int $id): Json
{
try {
$baseUrl = $this->request->get('baseUrl', '');
if (!empty($baseUrl)) {
$this->tenantId = BaseController::getTenantIdByDomain($baseUrl);
}
$tid = $this->getTenantId();
$query = Articles::published()->where('id', $id);
if ($tid > 0) {
$query->where('tid', $tid);
}
$article = $query->find();
if (!$article) {
return json([
'code' => 404,
'msg' => '文章不存在',
'list' => [],
]);
}
return json([
'code' => 200,
'msg' => 'success',
'list' => $article,
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '获取新闻详情失败:' . $e->getMessage(),
'list' => [],
]);
}
}
/**
* 获取上一篇下一篇
* @param int $id 文章ID
* @param int $cate 分类ID
* @return array
*/
private function getNextPreviousArticles(int $id, int $cate): array
{
$nextArticle = Articles::where('id', '<', $id)
->where('cate', $cate)
->where('delete_time', null)
->where('status', 2)
->field('id,title')
->find();
$previousArticle = Articles::where('id', '>', $id)
->where('cate', $cate)
->where('delete_time', null)
->where('status', 2)
->field('id,title')
->find();
return [
'code' => 200,
'msg' => 'success',
'next' => $nextArticle,
'previous' => $previousArticle,
];
}
/**
* 通过分类 ID 获取分类名称
* @param int $cateId 分类ID
* @return string
*/
private function getCategoryName(int $id): string
{
$categoryInfo = ArticlesCategory::where('id', $id)
->where('delete_time', null)
->find();
if (!$categoryInfo) {
return '未分类';
}
return $categoryInfo['name'];
}
/**
* 获取相关文章
*/
private function getRelatedArticles(int $id, int $cate): array
{
$articles = Articles::where('id', '<>', $id)
->where('cate', $cate)
->where('delete_time', null)
->where('status', 2)
->order('top', 'desc')
->order('recommend', 'desc')
->order('sort', 'desc')
->order('id', 'desc')
->limit(5)
->select();
foreach ($articles as &$article) {
$article['cate'] = $this->getCategoryName($article['cate']);
}
return [
'code' => 200,
'msg' => 'success',
'list' => $articles,
];
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace app\index\controller;
use app\index\BaseController;
use think\response\Json;
use think\facade\Db;
/**
* Banner 控制器
*/
class BannerController extends BaseController
{
/**
* 获取 Banner 列表
* @return Json
*/
public function getBanners()
{
$baseUrl = $this->request->get('baseUrl', '');
if (!empty($baseUrl)) {
$this->tenantId = BaseController::getTenantIdByDomain($baseUrl);
}
$tid = $this->getTenantId();
if (empty($tid)) {
return json([
'code' => 400,
'msg' => '无法识别租户信息',
'list' => [],
]);
}
// 查询该租户下的 Banner
$banners = Db::name('mete_apps_cms_banner')
->where('tid', $tid)
->whereNull('delete_time')
->order('sort', 'asc')
->order('id', 'desc')
->select()
->toArray();
// 处理图片路径
foreach ($banners as &$banner) {
if (!empty($banner['image'])) {
// 如果图片路径已经是完整 URL,直接返回
if (!preg_match('/^https?:\/\//', $banner['image'])) {
// 拼接完整 URL
$banner['image'] = $this->request->scheme() . '://' . $this->request->host() .
(strpos($banner['image'], '/') === 0 ? '' : '/') . $banner['image'];
}
}
}
return json([
'code' => 200,
'msg' => 'success',
'list' => $banners,
]);
}
}
+482 -6
View File
@@ -6,15 +6,409 @@ namespace app\index\controller;
use app\model\Banner;
use app\index\BaseController;
use app\model\FrontMenu;
use app\model\OnePage;
use app\model\Cms\FrontMenu;
use app\model\Cms\OnePage;
use app\model\System\SystemSiteSetting;
use app\service\ThemeService;
use think\db\exception\DbException;
use think\facade\Env;
use think\facade\Request;
use app\model\Cms\TemplateSiteConfig;
use app\model\Cms\Friendlink;
use app\model\Cms\Services;
use app\model\Cms\Products;
use app\model\Tenant\Tenant;
use app\model\Cms\Articles;
use app\model\Cms\ArticlesCategory;
class Index extends BaseController
{
private ThemeService $themeService;
public function __construct()
{
$this->themeService = new ThemeService();
}
public function index()
{
return '您好!这是一个[index]示例应用';
// 获取请求对象
$request = $this->request ?? Request::instance();
// 优先从请求对象获取中间件设置的租户ID(通过域名解析)
$tid = $request->tenantId ?? 0;
// 兼容:从header获取
if ($tid == 0) {
$headerTid = $request->header('X-Tenant-Id');
$tid = $headerTid ? (int) $headerTid : 0;
}
// 调试:检查tid
if ($tid === 0) {
die('tenantId未获取到,请检查域名解析');
}
// 获取租户选择的模板
$themeKey = 'default';
if ($tid > 0) {
// 不使用软删除过滤
$config = TemplateSiteConfig::where('tid', $tid)
->where('key', 'current_theme')
->withoutField('delete_time')
->find();
$themeKey = $config['value'] ?? 'default';
}
// 模板路径:/themes/{theme_key}/,优先使用index.php,其次index.html
$themeBasePath = root_path() . 'public' . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $themeKey;
$themeUrlPath = '/themes/' . $themeKey . '/';
if (is_file($themeBasePath . DIRECTORY_SEPARATOR . 'index.php')) {
// 渲染PHP模板(直接include
return $this->renderPhpTemplate($themeBasePath . DIRECTORY_SEPARATOR . 'index.php', $themeUrlPath);
} elseif (is_file($themeBasePath . DIRECTORY_SEPARATOR . 'index.html')) {
// 读取HTML模板内容
$content = file_get_contents($themeBasePath . DIRECTORY_SEPARATOR . 'index.html');
// 修复资源路径:将相对路径转换为绝对路径
$content = $this->fixTemplateAssets($content, $themeUrlPath);
return response($content, 200, ['Content-Type' => 'text/html; charset=utf-8']);
}
// 模板不存在,返回默认
return view('index/index');
}
/**
* 通用页面渲染
* 根据页面名称(如 about, blog, portfolio 等)渲染对应模板
* @param string $page 页面名称
* @return \think\response|\think\response\View
*/
public function page(string $page = 'index')
{
$request = $this->request ?? Request::instance();
// 获取租户ID
$tid = $request->tenantId ?? 0;
if ($tid == 0) {
$headerTid = $request->header('X-Tenant-Id');
$tid = $headerTid ? (int) $headerTid : 0;
}
if ($tid === 0) {
die('tenantId未获取到,请检查域名解析');
}
// 获取租户选择的模板
$themeKey = 'default';
if ($tid > 0) {
$config = TemplateSiteConfig::where('tid', $tid)
->where('key', 'current_theme')
->withoutField('delete_time')
->find();
$themeKey = $config['value'] ?? 'default';
}
// 模板路径
$themeBasePath = root_path() . 'public' . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $themeKey;
$themeUrlPath = '/themes/' . $themeKey . '/';
// 安全检查:只允许字母、数字、中划线
$page = preg_replace('/[^a-zA-Z0-9\-]/', '', $page);
if (empty($page)) {
$page = 'index';
}
// 查找 PHP 模板(优先)或 HTML 模板
$templateFile = $themeBasePath . DIRECTORY_SEPARATOR . $page . '.php';
$templateHtmlFile = $themeBasePath . DIRECTORY_SEPARATOR . $page . '.html';
if (is_file($templateFile)) {
return $this->renderPhpTemplate($templateFile, $themeUrlPath);
} elseif (is_file($templateHtmlFile)) {
$content = file_get_contents($templateHtmlFile);
$content = $this->fixTemplateAssets($content, $themeUrlPath);
return response($content, 200, ['Content-Type' => 'text/html; charset=utf-8']);
}
// 模板不存在
return response('页面不存在', 404, ['Content-Type' => 'text/html; charset=utf-8']);
}
/**
* 文章详情页
* @param int $id 文章ID
* @return \think\response
*/
public function articleDetail(int $id = 0)
{
$request = $this->request ?? Request::instance();
// 获取租户ID
$tid = $request->tenantId ?? 0;
if ($tid == 0) {
$headerTid = $request->header('X-Tenant-Id');
$tid = $headerTid ? (int) $headerTid : 0;
}
if ($tid === 0) {
die('tenantId未获取到,请检查域名解析');
}
// 与 NewsCenterController::getNewsDetail 一致:已发布 + 当前租户(网页直出,非 JSON)
$templateVars = $this->buildArticleDetailTemplateVars($id, $tid);
if ($templateVars === null) {
return response('文章不存在', 404, ['Content-Type' => 'text/html; charset=utf-8']);
}
// 获取租户选择的模板
$themeKey = 'default';
if ($tid > 0) {
$config = TemplateSiteConfig::where('tid', $tid)
->where('key', 'current_theme')
->withoutField('delete_time')
->find();
$themeKey = $config['value'] ?? 'default';
}
// 模板路径
$themeBasePath = root_path() . 'public' . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $themeKey;
$themeUrlPath = '/themes/' . $themeKey . '/';
// 查找文章详情模板
$templateFile = $themeBasePath . DIRECTORY_SEPARATOR . 'article_detail.php';
$templateHtmlFile = $themeBasePath . DIRECTORY_SEPARATOR . 'article_detail.html';
$blogDetailsFile = $themeBasePath . DIRECTORY_SEPARATOR . 'blog-details.php';
$blogDetailsHtmlFile = $themeBasePath . DIRECTORY_SEPARATOR . 'blog-details.html';
// 优先使用 article_detail 模板
if (is_file($templateFile)) {
return $this->renderPhpTemplate($templateFile, $themeUrlPath, $templateVars);
} elseif (is_file($templateHtmlFile)) {
$content = file_get_contents($templateHtmlFile);
$content = $this->fixTemplateAssets($content, $themeUrlPath);
return response($content, 200, ['Content-Type' => 'text/html; charset=utf-8']);
} elseif (is_file($blogDetailsFile)) {
return $this->renderPhpTemplate($blogDetailsFile, $themeUrlPath, $templateVars);
} elseif (is_file($blogDetailsHtmlFile)) {
$content = file_get_contents($blogDetailsHtmlFile);
$content = $this->fixTemplateAssets($content, $themeUrlPath);
return response($content, 200, ['Content-Type' => 'text/html; charset=utf-8']);
}
return response('文章不存在', 404, ['Content-Type' => 'text/html; charset=utf-8']);
}
/**
* 组装文章详情页模板变量(数据源与 getNewsDetail 一致,并限定租户 + 已发布)
*
* @return array<string, mixed>|null
*/
private function buildArticleDetailTemplateVars(int $id, int $tid): ?array
{
$article = Articles::published()
->where('id', $id)
->where('tid', $tid)
->find();
if (!$article) {
return null;
}
$row = $article->toArray();
$categoryName = '未分类';
if (!empty($row['cate'])) {
$cat = ArticlesCategory::where('id', $row['cate'])
->where('delete_time', null)
->find();
if ($cat) {
$categoryName = (string) $cat['name'];
}
}
$coverPath = $row['image'] ?? '';
if ($coverPath === '' && !empty($row['cate'])) {
$category = ArticlesCategory::where('id', $row['cate'])
->where('delete_time', null)
->find();
if ($category && !empty($category['image'])) {
$coverPath = (string) $category['image'];
}
}
$apiUrl = rtrim((string) (Env::get('app.api_url', '') ?: 'https://api.yunzer.cn'), '/');
$articleCoverUrl = $this->resolveArticleMediaUrl($coverPath, $apiUrl);
$descPlain = strip_tags((string) ($row['desc'] ?? ''));
$title = (string) ($row['title'] ?? '详情');
$descShort = $descPlain !== ''
? (function_exists('mb_substr') ? mb_substr($descPlain, 0, 160) : substr($descPlain, 0, 160))
: $title;
return [
'article' => $row,
'articleCoverUrl' => $articleCoverUrl,
'articleCategoryName' => $categoryName,
'pageTitle' => $title . ' - 新闻详情',
'pageDescription' => $descShort,
'pageKeywords' => $title,
];
}
/**
* 文章封面等资源 URL(与列表页逻辑一致:相对路径拼 API 域名)
*/
private function resolveArticleMediaUrl(string $path, string $apiUrl): string
{
if ($path === '') {
return '';
}
if (strpos($path, 'http://') === 0 || strpos($path, 'https://') === 0) {
return $path;
}
if (strpos($path, '/') === 0) {
return $apiUrl . $path;
}
return $apiUrl . '/' . $path;
}
/**
* 修复模板中的资源路径
* 将相对路径 (assets/, css/, js/, images/) 转换为绝对路径 (/themes/xxx/)
*/
private function fixTemplateAssets(string $content, string $themeUrlPath): string
{
// 需要修复的资源目录
$assetsDirs = ['assets', 'css', 'js', 'images', 'img', 'fonts', 'plugins', 'libs'];
foreach ($assetsDirs as $dir) {
// 匹配 src="assets/..." 或 href="css/..." 等相对路径
// 不匹配已以 / 开头的绝对路径
$content = preg_replace(
'/(src|href)=["\'](' . $dir . '\/[^"\']*)["\']/',
'$1="' . $themeUrlPath . '$2"',
$content
);
// 匹配 src='assets/...'
$content = preg_replace(
'/(src|href)=[\']([^\/][^\'\"]*\/[^\'\"]*)[\']/',
'$1="' . $themeUrlPath . '$2"',
$content
);
}
return $content;
}
/**
* 渲染PHP模板文件
*/
private function renderPhpTemplate(string $templateFile, string $themeUrlPath = '', array $templateVars = [])
{
// 定义模板基础URL常量,供模板使用
if (!defined('THEME_URL')) {
define('THEME_URL', $themeUrlPath);
}
ob_start();
// 获取URL参数
$getParams = Request::get();
extract($getParams, EXTR_OVERWRITE);
extract($templateVars, EXTR_OVERWRITE);
include $templateFile;
$content = ob_get_clean();
// 修复PHP模板输出中的资源路径
$content = $this->fixTemplateAssets($content, $themeUrlPath);
return response($content, 200, ['Content-Type' => 'text/html; charset=utf-8']);
}
/**
* 前端初始化接口 - 返回当前模板和填充数据
* @return \think\response\Json
*/
public function init()
{
// 优先从请求对象获取中间件设置的租户ID(通过域名解析)
$tid = $this->request->tenantId ?? 0;
// 兼容:URL参数传递的tid作为备用
$tid = $tid > 0 ? $tid : Request::param('tid', 0, 'int');
// 从TemplateSiteConfig获取配置(根据租户ID
$config = null;
if ($tid > 0) {
$config = TemplateSiteConfig::where('tid', $tid)
->where('key', 'current_theme')
->find();
}
$themeKey = $config['value'] ?? 'default';
// 模板路径:/themes/{theme_key}/,优先使用index.php,其次index.html
$themeBasePath = root_path() . 'public' . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $themeKey;
if (is_file($themeBasePath . DIRECTORY_SEPARATOR . 'index.php')) {
$themePath = '/themes/' . $themeKey . '/index.php';
} else {
$themePath = '/themes/' . $themeKey . '/index.html';
}
return json([
'code' => 200,
'msg' => 'success',
'data' => [
'theme_key' => $themeKey,
'theme_path' => $themePath
]
]);
}
/**
* 获取日志列表
*/
public function getLogs()
{
$logPath = Env::get('runtime_path') . 'log/';
$logs = [];
// 读取最近的日志文件
$files = glob($logPath . '*.log');
if ($files) {
// 按修改时间排序,最新的在前
usort($files, function ($a, $b) {
return filemtime($b) - filemtime($a);
});
// 读取最新的日志文件
$latestFile = reset($files);
if (file_exists($latestFile)) {
$content = file_get_contents($latestFile);
$lines = explode("\n", $content);
// 倒序遍历,只取最近的100条
$count = 0;
for ($i = count($lines) - 1; $i >= 0 && $count < 100; $i--) {
if (!empty(trim($lines[$i]))) {
$logs[] = $lines[$i];
$count++;
}
}
// 再倒序回来,使最新的日志在最后
$logs = array_reverse($logs);
}
}
return json([
'code' => 200,
'msg' => 'success',
'data' => $logs
]);
}
/**
@@ -94,15 +488,15 @@ class Index extends BaseController
$requestPath = $this->request->pathinfo();
$path = str_replace('onepage/', '', $requestPath);
}
// 确保路径以 / 开头
if (empty($path) || $path[0] !== '/') {
$path = '/' . $path;
}
// 解码路径
$path = urldecode($path);
// 查找对应路径的单页
$onePage = OnePage::where('path', $path)
->where('status', 1)
@@ -130,4 +524,86 @@ class Index extends BaseController
]);
}
}
/**
* 获取首页综合数据
* @return \think\response\Json
*/
public function getHomeData()
{
$baseUrl = Request::param('baseUrl', '');
if (empty($baseUrl)) {
return json(['code' => 400, 'msg' => '缺少 baseUrl 参数']);
}
try {
// 通过域名获取租户ID
$tid = BaseController::getTenantIdByDomain($baseUrl);
if (empty($tid)) {
return json([
'code' => 400,
'msg' => '无法识别租户信息',
'data' => []
]);
}
// 获取站点基础信息 (normalinfos)
$normalInfos = SystemSiteSetting::where('tid', $tid)
->field('sitename,logo,logow,ico,description,copyright,companyname,icp,companyintroduction')
->find();
// 获取特色服务列表
$servicesList = Services::where('delete_time', null)
->where('tid', $tid)
->order('sort', 'asc')
->field('id,title,desc,thumb,url')
->select()
->toArray();
// 获取企业产品列表
$productsList = Products::where('delete_time', null)
->where('tid', $tid)
->order('sort', 'asc')
->field('id,title,desc,content,thumb,url')
->select()
->toArray();
// 获取友情链接列表
$friendlinkList = Friendlink::where('delete_time', null)
->where('tid', $tid)
->where('status', 1)
->order('sort', 'asc')
->field('id,link_name,link_url,link_logo')
->select()
->toArray();
// 获取联系方式
$contact = Tenant::where('id', $tid)
->where('status', 1)
->where('delete_time', null)
->field('contact_phone,contact_email,address,worktime')
->find();
// 合并返回
return json([
'code' => 200,
'msg' => 'success',
'data' => [
'normal' => $normalInfos ?: (object) [],
'contact' => $contact ?: (object) [],
'services' => $servicesList,
'products' => $productsList,
'links' => $friendlinkList
]
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '服务器错误:' . $e->getMessage(),
'data' => null
]);
}
}
}
@@ -1,434 +0,0 @@
<?php
declare(strict_types=1);
namespace app\index\controller;
use app\index\BaseController;
use Symfony\Component\VarDumper\VarDumper;
use think\exception\ValidateException;
use think\facade\Request;
use think\facade\Session;
use think\response\Json;
use think\db\exception\DbException;
use app\model\Articles;
use app\model\ArticlesCategory;
class NewsCenterController extends BaseController
{
/**
* 根据分类获取文章
* @param string $category 分类名称
* @return Json
*/
public function getNewsByCategory(string $category): Json
{
// 查询分类
$categoryInfo = ArticlesCategory::where('name', $category)
->where('delete_time', null)
->find();
if (!$categoryInfo) {
return json([
'code' => 200,
'msg' => 'success',
'list' => [],
]);
}
// 查询文章
$articles = Articles::published()
->where('cate', $categoryInfo['id'])
->order('publish_date', 'desc')
->limit(4)
->select();
return json([
'code' => 200,
'msg' => 'success',
'list' => $articles,
]);
}
/**
* 获取企业新闻
* @return Json
*/
public function getCompanyNews(): Json
{
// 获取分页参数
$page = Request::param('page', 1);
$limit = Request::param('limit', 10);
$page = max(1, intval($page));
$limit = max(1, min(50, intval($limit))); // 限制每页最多50条
// 查询分类
$categoryInfo = ArticlesCategory::where('name', '企业新闻')
->where('delete_time', null)
->find();
if (!$categoryInfo) {
return json([
'code' => 200,
'msg' => 'success',
'list' => [],
'total' => 0,
'page' => $page,
'limit' => $limit,
]);
}
// 查询总数
$total = Articles::where('cate', $categoryInfo['id'])
->where('delete_time', null)
->where('status', 2)
->count();
// 查询文章
$articles = Articles::where('cate', $categoryInfo['id'])
->where('delete_time', null)
->where('status', 2)
->order('top', 'desc')
->order('recommend', 'desc')
->order('sort', 'desc')
->order('publish_date', 'desc')
->page($page, $limit)
->select();
return json([
'code' => 200,
'msg' => 'success',
'list' => $articles,
'total' => $total,
'page' => $page,
'limit' => $limit,
]);
}
/**
* 获取金蝶新闻
* @return Json
*/
public function getKingdeeNews(): Json
{
// 获取分页参数
$page = Request::param('page', 1);
$limit = Request::param('limit', 10);
$page = max(1, intval($page));
$limit = max(1, min(50, intval($limit))); // 限制每页最多50条
// 查询分类
$categoryInfo = ArticlesCategory::where('name', '金蝶新闻')
->where('delete_time', null)
->find();
if (!$categoryInfo) {
return json([
'code' => 200,
'msg' => 'success',
'list' => [],
'total' => 0,
'page' => $page,
'limit' => $limit,
]);
}
// 查询总数
$total = Articles::where('cate', $categoryInfo['id'])
->where('delete_time', null)
->where('status', 2)
->count();
// 查询文章
$articles = Articles::where('cate', $categoryInfo['id'])
->where('delete_time', null)
->where('status', 2)
->order('top', 'desc')
->order('recommend', 'desc')
->order('sort', 'desc')
->order('publish_date', 'desc')
->page($page, $limit)
->select();
return json([
'code' => 200,
'msg' => 'success',
'list' => $articles,
'total' => $total,
'page' => $page,
'limit' => $limit,
]);
}
/**
* 获取技术中心子分类
* @return Json
*/
public function getTechnologyCategories(): Json
{
// 获取"技术中心"主分类
$parentCategory = ArticlesCategory::where('name', '技术中心')
->where('delete_time', null)
->find();
if (!$parentCategory) {
return json([
'code' => 200,
'msg' => 'success',
'data' => []
]);
}
// 查找所有子分类
$subCategories = ArticlesCategory::where('cid', $parentCategory['id'])
->where('delete_time', null)
->field('id,name,desc,sort,image')
->order('sort', 'desc')
->select();
return json([
'code' => 200,
'msg' => 'success',
'data' => $subCategories
]);
}
/**
* 获取技术中心
* @return Json
*/
public function getTechnologyCenter(): Json
{
// 1. 分页参数规范化
$page = max(1, intval(Request::param('page', 1)));
$limit = max(1, min(50, intval(Request::param('limit', 10))));
// 获取分类ID参数(可选)
$categoryId = Request::param('category_id', null);
// 2. 第一步:获取“技术中心”主分类的 id
$parentCategory = ArticlesCategory::where('name', '技术中心')
->where('delete_time', null)
->find();
// print_r($parentCategory['id']);
if (!$parentCategory) {
return $this->emptyResponse($page, $limit);
}
// 3. 第二步:查找 cid 等于主分类 id 的所有子分类 id
$subCategoryQuery = ArticlesCategory::where('cid', $parentCategory['id'])
->where('delete_time', null);
// print_r($subCategoryQuery->select());
// 如果指定了分类ID,则只查询该分类
if ($categoryId) {
$subCategoryQuery->where('id', $categoryId);
}
$subCategoryIds = $subCategoryQuery->column('id');
// print_r($subCategoryIds);
if (empty($subCategoryIds)) {
return $this->emptyResponse($page, $limit);
}
// 5. 第三步:查询 Articles 表,并限定字段
$articleQuery = Articles::whereIn('cate', $subCategoryIds)
->where('delete_time', null)
->where('status', 2);
// 获取总数
$total = (clone $articleQuery)->count();
// 获取列表:限定返回 id, title, desc, publish_date, image, likes, views
$articles = $articleQuery->field('id,title,desc,publish_date,image,likes,views,cate')
->order([
'top' => 'desc',
'recommend' => 'desc',
'sort' => 'desc',
'publish_date' => 'desc'
])
->page($page, $limit)
->select();
// 如果文章没有image,查询对应分类的image
$categoryImages = ArticlesCategory::whereIn('id', $subCategoryIds)->column('image', 'id');
foreach ($articles as &$article) {
if (empty($article['image']) && isset($categoryImages[$article['cate']])) {
$article['image'] = $categoryImages[$article['cate']];
}
}
return json([
'code' => 200,
'msg' => 'success',
'list' => $articles,
'total' => $total,
'page' => $page,
'limit' => $limit,
]);
}
/**
* 统一空返回
*/
private function emptyResponse($page, $limit): Json
{
return json([
'code' => 200,
'msg' => 'success',
'list' => [],
'total' => 0,
'page' => $page,
'limit' => $limit,
]);
}
/**
* 获取金蝶新闻详情
*/
public function getKingdeeNewsDetail(int $id): Json
{
$article = Articles::where('id', $id)
->where('delete_time', null)
->where('status', 2)
->field('id,title,cate,image,desc,author,content,publisher,publish_date,views,likes,is_trans,transurl')
->find();
if (!$article) {
return json(['code' => 404, 'msg' => '文章不存在', 'data' => null]);
}
$cate = (int) $article['cate'];
// 转换为数组再处理
$articleData = $article->toArray();
// $articleData['catename'] = $this->getCategoryName($cate);
// 获取相关文章
$articleData['relatedArticles'] = $this->getRelatedArticles($id, $cate);
// 获取上一篇下一篇
$articleData['nextPreviousArticles'] = $this->getNextPreviousArticles($id, $cate);
// 增加浏览量
Articles::where('id', $id)->inc('views')->update();
return json(['code' => 200, 'msg' => 'success', 'data' => $articleData]);
}
/**
* 获取企业新闻详情
*/
public function getCompanyNewsDetail(int $id): Json
{
$article = Articles::where('id', $id)
->where('delete_time', null)
->where('status', 2)
->find();
if (!$article) {
return json(['code' => 404, 'msg' => '文章不存在', 'data' => null]);
}
$cate = (int) $article['cate'];
// 转换为数组再处理
$articleData = $article->toArray();
$articleData['catename'] = $this->getCategoryName($cate);
// 获取相关文章
$articleData['relatedArticles'] = $this->getRelatedArticles($id, $cate);
// 获取上一篇下一篇
$articleData['nextPreviousArticles'] = $this->getNextPreviousArticles($id, $cate);
// 增加浏览量
Articles::where('id', $id)->inc('views')->update();
return json(['code' => 200, 'msg' => 'success', 'data' => $articleData]);
}
/**
* 获取上一篇下一篇
* @param int $id 文章ID
* @param int $cate 分类ID
* @return array
*/
private function getNextPreviousArticles(int $id, int $cate): array
{
$nextArticle = Articles::where('id', '<', $id)
->where('cate', $cate)
->where('delete_time', null)
->where('status', 2)
->field('id,title')
->find();
$previousArticle = Articles::where('id', '>', $id)
->where('cate', $cate)
->where('delete_time', null)
->where('status', 2)
->field('id,title')
->find();
return [
'code' => 200,
'msg' => 'success',
'next' => $nextArticle,
'previous' => $previousArticle,
];
}
/**
* 通过分类 ID 获取分类名称
* @param int $cateId 分类ID
* @return string
*/
private function getCategoryName(int $id): string
{
$categoryInfo = ArticlesCategory::where('id', $id)
->where('delete_time', null)
->find();
if (!$categoryInfo) {
return '未分类';
}
return $categoryInfo['name'];
}
/**
* 获取相关文章
*/
private function getRelatedArticles(int $id, int $cate): array
{
$articles = Articles::where('id', '<>', $id)
->where('cate', $cate)
->where('delete_time', null)
->where('status', 2)
->order('top', 'desc')
->order('recommend', 'desc')
->order('sort', 'desc')
->order('id', 'desc')
->limit(5)
->select();
foreach ($articles as &$article) {
$article['cate'] = $this->getCategoryName($article['cate']);
}
return [
'code' => 200,
'msg' => 'success',
'list' => $articles,
];
}
}
+2 -8
View File
@@ -1,12 +1,6 @@
<?php
// 全局中间件定义文件
return [
// 全局跨域中间件
\think\middleware\AllowCrossDomain::class,
// 全局请求缓存
// \think\middleware\CheckRequestCache::class,
// 多语言加载
// \think\middleware\LoadLangPack::class,
// Session初始化
\app\common\middleware\AllowCrossDomain::class,
\app\common\middleware\DomainParse::class,
\think\middleware\SessionInit::class,
];
+25 -14
View File
@@ -5,23 +5,34 @@ use think\facade\Route;
Route::get('/', 'app\index\controller\Index@index');
Route::get('index/index', 'app\index\controller\Index@index');
// --- 文章详情路由 ---
Route::get('kingdeenews/detail/:id', 'app\index\controller\NewsCenterController@getKingdeeNewsDetail');
Route::get('companynews/detail/:id', 'app\index\controller\NewsCenterController@getCompanyNewsDetail');
// --- 模板通用页面路由 ---
Route::get('article_detail/:id', 'app\index\controller\Index@articleDetail');
Route::get(':page', 'app\index\controller\Index@page')
->pattern(['page' => 'about|blog|blog-details|contact|portfolio|portfolio-details|service-details|services|team|articles']);
// --- 文章列表路由 ---
Route::get('kingdeenews$', 'app\index\controller\NewsCenterController@getKingdeeNews');
Route::get('companynews$', 'app\index\controller\NewsCenterController@getCompanyNews');
Route::get('technologyCenter$', 'app\index\controller\NewsCenterController@getTechnologyCenter');
Route::get('technologyCategories$', 'app\index\controller\NewsCenterController@getTechnologyCategories');
Route::get('newscentertop4', 'app\index\controller\Article\ArticleController@getNewsCenterTop4');
Route::get('newsbycategory/:category', 'app\index\controller\NewsCenterController@getNewsByCategory');
// --- 模板初始化接口 ---
Route::get('init', 'app\index\controller\Index@init');
// --- 文章互动路由 ---
// --- Banner 路由 ---
Route::get('getBanners', 'app\index\controller\BannerController@getBanners');
// --- 前端其他数据路由 ---
Route::get('footerdata', 'app\index\controller\Index@getFooterData');
Route::get('companyInfos', 'app\index\controller\Index@getCompanyInfos');
Route::post('requirement', 'app\index\controller\Index@requirement');
Route::get('homeData', 'app\index\controller\Index@getHomeData');
// --- 新闻中心列表路由 ---
Route::get('getCenterNews', 'app\index\controller\Article\NewsCenterController@getCenterNews');
Route::get('getNewsDetail/:id', 'app\index\controller\Article\NewsCenterController@getNewsDetail');
// --- 文章分类路由 ---
Route::get('getArticleCategories', 'app\index\controller\Article\ArticleController@getArticleCategories');
// --- 新闻中心互动路由 ---
Route::post('articleViews/:id', 'app\index\controller\Article\ArticleController@articleViews');
Route::post('articleLikes/:id', 'app\index\controller\Article\ArticleController@articleLikes');
Route::post('articleUnlikes/:id', 'app\index\controller\Article\ArticleController@articleUnlikes');
// --- 前端导航与单页路由 ---
Route::get('headmenu', 'app\index\controller\Index@getHeadMenu');
Route::rule('onepage/:path', 'app\index\controller\Index@getOnePageByPath', 'GET')->pattern(['path' => '.*']);
+104
View File
@@ -0,0 +1,104 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>后端运行状态</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
background-color: #f5f5f5;
}
h1 {
color: #333;
text-align: center;
}
p {
color: #666;
text-align: center;
}
#log-content {
background-color: #fff;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 20px;
margin: 20px auto;
max-width: 1200px;
max-height: 600px;
overflow-y: auto;
}
#log-content div {
padding: 5px 0;
border-bottom: 1px solid #eee;
font-size: 14px;
line-height: 1.4;
}
#log-content div:hover {
background-color: #f9f9f9;
}
#log-content p {
text-align: left;
}
.refresh-info {
text-align: center;
font-size: 12px;
color: #999;
margin-top: 10px;
}
</style>
</head>
<body>
<h1>后端服务运行状态</h1>
<p>实时监控系统运行情况</p>
<div id="log-content">
<p>正在加载日志...</p>
</div>
<div class="refresh-info">
<span id="last-refresh">最后刷新: 从未</span>
<span> | </span>
<span>每5秒自动刷新</span>
</div>
<script>
// 加载日志
function loadLogs() {
fetch('/getLogs')
.then(response => response.json())
.then(data => {
const logContent = document.getElementById('log-content');
if (data.code === 200) {
logContent.innerHTML = '';
if (data.data && data.data.length > 0) {
data.data.forEach(log => {
const logItem = document.createElement('div');
logItem.textContent = log;
logContent.appendChild(logItem);
});
} else {
logContent.innerHTML = '<p>暂无日志记录</p>';
}
} else {
logContent.innerHTML = '<p>加载日志失败: ' + (data.msg || '未知错误') + '</p>';
}
// 更新最后刷新时间
const now = new Date();
const timeStr = now.toLocaleString('zh-CN');
document.getElementById('last-refresh').textContent = '最后刷新: ' + timeStr;
})
.catch(error => {
console.error('加载日志失败:', error);
const logContent = document.getElementById('log-content');
logContent.innerHTML = '<p>加载日志失败: 网络错误</p>';
});
}
// 初始加载日志
loadLogs();
// 定时刷新日志,每5秒一次
setInterval(loadLogs, 5000);
</script>
</body>
</html>
+3 -1
View File
@@ -11,15 +11,17 @@ class AdminModules extends Model
{
use SoftDelete;
protected $name = 'admin_modules';
protected $name = 'mete_admin_modules';
protected $type = [
'id' => 'integer',
'mid' => 'integer',
'name' => 'string',
'code' => 'string',
'path' => 'string',
'icon' => 'string',
'description' => 'string',
'type' => 'integer',
'sort' => 'integer',
'status' => 'integer',
'is_show' => 'integer',
+48
View File
@@ -0,0 +1,48 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace app\model;
use think\Model;
use think\model\concern\SoftDelete;
/**
* 文章分类模型
*/
class AppsBabyhealthBabys extends Model
{
// 启用软删除
use SoftDelete;
// 数据库表名
protected $name = 'mete_apps_babyhealth_babys';
// 字段类型转换
protected $type = [
'id' => 'integer',
'name' => 'string',
'nickname' => 'string',
'sex' => 'integer',
'birth' => 'date',
'father' => 'integer',
'mother' => 'integer',
'status' => 'integer',
'height' => 'double',
'weight' => 'double',
'avatar' => 'string',
'status' => 'integer',
'create_time' => 'datetime',
'update_time' => 'datetime',
'delete_time' => 'datetime',
];
}
+46
View File
@@ -0,0 +1,46 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace app\model;
use think\Model;
use think\model\concern\SoftDelete;
/**
* 文章分类模型
*/
class AppsBabyhealthUsers extends Model
{
// 启用软删除
use SoftDelete;
// 数据库表名
protected $name = 'mete_apps_babyhealth_users';
// 字段类型转换
protected $type = [
'id' => 'integer',
'account' => 'string',
'password' => 'string',
'phone' => 'string',
'email' => 'string',
'name' => 'string',
'sex' => 'integer',
'birth' => 'date',
'avatar' => 'string',
'status' => 'integer',
'create_time' => 'datetime',
'update_time' => 'datetime',
'delete_time' => 'datetime',
];
}
@@ -9,7 +9,7 @@
// | Author: Liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace app\model;
namespace app\model\Cms;
use think\Model;
use think\model\concern\SoftDelete;
@@ -36,6 +36,7 @@ class Articles extends Model
// 字段类型转换
protected $type = [
'id' => 'integer',
'tid' => 'integer',
'cate' => 'integer',
'sort' => 'integer',
'status' => 'integer',
@@ -9,7 +9,7 @@
// | Author: Liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace app\model;
namespace app\model\Cms;
use think\Model;
use think\model\concern\SoftDelete;
@@ -36,6 +36,11 @@ class ArticlesCategory extends Model
// 字段类型转换
protected $type = [
'id' => 'integer',
'cid' => 'integer',
'tid' => 'integer',
'name' => 'string',
'image' => 'string',
'desc' => 'string',
'sort' => 'integer',
'status' => 'integer',
'create_time' => 'datetime',
@@ -9,7 +9,7 @@
// | Author: Liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace app\model;
namespace app\model\Cms;
use think\Model;
use think\model\concern\SoftDelete;
@@ -23,11 +23,12 @@ class Banner extends Model
use SoftDelete;
// 数据库表名
protected $name = 'mete_banner';
protected $name = 'mete_apps_cms_banner';
// 字段类型转换
protected $type = [
'id' => 'integer',
'tid' => 'integer',
'title' => 'string',
'desc' => 'string',
'url' => 'string',
+44
View File
@@ -0,0 +1,44 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace app\model\Cms;
use think\Model;
use think\model\concern\SoftDelete;
/**
* 文章分类模型
*/
class Demand extends Model
{
// 启用软删除
use SoftDelete;
// 数据库表名
protected $name = 'mete_apps_cms_demand';
// 字段类型转换
protected $type = [
'id' => 'integer',
'tid' => 'integer',
'title' => 'string',
'desc' => 'string',
'applicant' => 'string',
'phone' => 'string',
'status' => 'integer',
'create_time' => 'datetime',
'update_time' => 'datetime',
'delete_time' => 'datetime',
];
}
@@ -9,7 +9,7 @@
// | Author: Liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace app\model;
namespace app\model\Cms;
use think\Model;
use think\model\concern\SoftDelete;
@@ -17,22 +17,22 @@ use think\model\concern\SoftDelete;
/**
* 文章分类模型
*/
class AdminUser extends Model
class DemandCategory extends Model
{
// 启用软删除
use SoftDelete;
// 数据库表名
protected $name = 'mete_admin_user';
protected $name = 'mete_demand_category';
// 字段类型转换
protected $type = [
'id' => 'integer',
'sex' => 'integer',
'group_id' => 'integer',
'login_count' => 'integer',
'title' => 'string',
'desc' => 'string',
'applicant' => 'string',
'phone' => 'string',
'status' => 'integer',
'api_key_status' => 'integer',
'create_time' => 'datetime',
'update_time' => 'datetime',
'delete_time' => 'datetime',
@@ -40,3 +40,4 @@ class AdminUser extends Model
}
+44
View File
@@ -0,0 +1,44 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace app\model\Cms;
use think\Model;
use think\model\concern\SoftDelete;
/**
* 文章分类模型
*/
class Friendlink extends Model
{
// 启用软删除
use SoftDelete;
// 数据库表名
protected $name = 'mete_apps_cms_friendlink';
// 字段类型转换
protected $type = [
'id' => 'integer',
'tid' => 'integer',
'link_name' => 'string',
'link_url' => 'string',
'link_logo' => 'string',
'description' => 'string',
'sort' => 'integer',
'status' => 'integer',
'create_time' => 'datetime',
'update_time' => 'datetime',
'delete_time' => 'datetime',
];
}
@@ -9,7 +9,7 @@
// | Author: Liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace app\model;
namespace app\model\Cms;
use think\Model;
use think\model\concern\SoftDelete;
@@ -23,16 +23,19 @@ class FrontMenu extends Model
use SoftDelete;
// 数据库表名
protected $name = 'mete_front_menu';
protected $name = 'mete_apps_cms_front_menu';
// 字段类型转换
protected $type = [
'id' => 'integer',
'pid' => 'integer',
'tid' => 'integer',
'title' => 'string',
'image' => 'string',
'type' => 'integer',
'path' => 'string',
'component_path' => 'string',
'sort' => 'integer',
'desc' => 'string',
'create_time' => 'datetime',
'update_time' => 'datetime',
@@ -9,7 +9,7 @@
// | Author: Liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace app\model;
namespace app\model\Cms;
use think\Model;
use think\model\concern\SoftDelete;
@@ -23,11 +23,12 @@ class OnePage extends Model
use SoftDelete;
// 数据库表名
protected $name = 'mete_onepage';
protected $name = 'mete_apps_cms_onepage';
// 字段类型转换
protected $type = [
'id' => 'integer',
'tid' => 'integer',
'title' => 'string',
'content' => 'string',
'path' => 'string',
+44
View File
@@ -0,0 +1,44 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace app\model\Cms;
use think\Model;
use think\model\concern\SoftDelete;
/**
* 特色服务模型
*/
class Products extends Model
{
// 启用软删除
use SoftDelete;
// 数据库表名
protected $name = 'mete_apps_cms_products';
// 字段类型转换
protected $type = [
'id' => 'integer',
'tid' => 'integer',
'title' => 'string',
'desc' => 'string',
'content' => 'string',
'thumb' => 'string',
'url' => 'string',
'sort' => 'integer',
'create_time' => 'datetime',
'update_time' => 'datetime',
'delete_time' => 'datetime',
];
}

Some files were not shown because too many files have changed in this diff Show More