Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ee8d9b828 | ||
|
|
6107bde666 | ||
|
|
e69a91feb3 | ||
|
|
174df30978 | ||
|
|
8ffac1376a | ||
|
|
433a46bba9 | ||
|
|
ba3d5299a0 | ||
|
|
0aa6911159 | ||
|
|
1cef313689 | ||
|
|
4a198ccd8f | ||
|
|
11e08fea73 | ||
|
|
a5be461021 | ||
|
|
d2e04843a4 | ||
|
|
d0276e63c6 | ||
|
|
2d4fa5735b | ||
|
|
1c367eb687 | ||
|
|
56cf534340 | ||
|
|
01fbc8b39d | ||
|
|
91f3ea7c46 | ||
|
|
d11a89d02c | ||
|
|
b580a9f7ad | ||
|
|
c2db186620 | ||
|
|
679a0002a7 | ||
|
|
81fdcce40f | ||
|
|
8af540f9b2 | ||
|
|
79898d87c0 | ||
|
|
3c400af559 | ||
|
|
c8375b846a | ||
|
|
765f74b619 | ||
|
|
2fcb7fb59b | ||
|
|
d828ed83a5 | ||
|
|
22993283f0 | ||
|
|
69f0d3b2bc | ||
|
|
ab0a5898cb | ||
|
|
60a41cae41 | ||
|
|
6c7f7f9543 | ||
|
|
d571d6e121 | ||
|
|
b7b933c89d | ||
|
|
7e98a78333 | ||
|
|
342b9d6fc6 | ||
|
|
ef7ed58a22 | ||
|
|
160c736b38 | ||
|
|
01bb72523e | ||
|
|
ec62d7be5a | ||
|
|
f8cbd31f61 | ||
|
|
8c04a432a8 | ||
|
|
3a9001e091 | ||
|
|
a172909ddf | ||
|
|
54d695f851 | ||
|
|
0147e7f1e1 |
@@ -1,13 +1,14 @@
|
||||
APP_NAME=DooTask
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_DEBUG=false
|
||||
APP_SCHEME=auto
|
||||
APP_URL=http://localhost
|
||||
|
||||
APP_ID=
|
||||
APP_IPPR=
|
||||
APP_PORT=2222
|
||||
APP_DEV_PORT=
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_LEVEL=debug
|
||||
@@ -53,6 +54,9 @@ PUSHER_APP_KEY=
|
||||
PUSHER_APP_SECRET=
|
||||
PUSHER_APP_CLUSTER=mt1
|
||||
|
||||
JUKE_KEY_JOKE=
|
||||
JUKE_KEY_SOUP=
|
||||
|
||||
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
|
||||
85
.github/workflows/electron-generic.yml
vendored
85
.github/workflows/electron-generic.yml
vendored
@@ -1,85 +0,0 @@
|
||||
name: Build Generic
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
build-mac:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
id: [ "com.dootask.task", "com.hitosea.task" ]
|
||||
runs-on: macos-latest
|
||||
environment: build
|
||||
|
||||
if: startsWith(github.event.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Create changelog text
|
||||
id: changelog
|
||||
uses: loopwerk/tag-changelog@v1
|
||||
with:
|
||||
token: ${{ secrets.GH_PAT }}
|
||||
exclude_types: other,chore,build
|
||||
|
||||
- name: Use Node.js 16.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Build for MacOS
|
||||
env:
|
||||
APPID: ${{ matrix.id }}
|
||||
PROVIDER: "generic"
|
||||
APPLEID: ${{ secrets.APPLEID }}
|
||||
APPLEIDPASS: ${{ secrets.APPLEIDPASS }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
RELEASE_BODY: ${{ steps.changelog.outputs.changes }}
|
||||
run: ./cmd electron build-mac
|
||||
|
||||
build-win:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
id: [ "com.dootask.task", "com.hitosea.task" ]
|
||||
runs-on: windows-latest
|
||||
environment: build
|
||||
|
||||
if: startsWith(github.event.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Create changelog text
|
||||
id: changelog
|
||||
uses: loopwerk/tag-changelog@v1
|
||||
with:
|
||||
token: ${{ secrets.GH_PAT }}
|
||||
exclude_types: other,chore,build
|
||||
|
||||
- name: Use Node.js 16.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Build for Windows
|
||||
shell: powershell
|
||||
env:
|
||||
APPID: ${{ matrix.id }}
|
||||
PROVIDER: "generic"
|
||||
RELEASE_BODY: ${{ steps.changelog.outputs.changes }}
|
||||
run: |
|
||||
npm install
|
||||
cd electron
|
||||
npm install
|
||||
cd ../
|
||||
mkdir -p ./electron/public
|
||||
cp ./electron/index.html ./electron/public/index.html
|
||||
npx mix --production -- --env --electron
|
||||
node ./electron/build.js build-win
|
||||
|
||||
97
.github/workflows/electron-github.yml
vendored
97
.github/workflows/electron-github.yml
vendored
@@ -1,97 +0,0 @@
|
||||
name: Build Github
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
if: ${{ (startsWith(github.event.ref, 'refs/tags/v')) && (github.repository == 'kuaifan/dootask') }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Create changelog text
|
||||
id: changelog
|
||||
uses: loopwerk/tag-changelog@v1
|
||||
with:
|
||||
token: ${{ secrets.GH_PAT }}
|
||||
exclude_types: other,chore,build
|
||||
|
||||
- name: Create release
|
||||
uses: actions/create-release@latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
body: ${{ steps.changelog.outputs.changes }}
|
||||
|
||||
build-mac:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
id: [ "com.dootask.task", "com.hitosea.task" ]
|
||||
runs-on: macos-latest
|
||||
environment: build
|
||||
|
||||
if: ${{ (startsWith(github.event.ref, 'refs/tags/v')) && (github.repository == 'kuaifan/dootask') }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js 16.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Build for MacOS
|
||||
env:
|
||||
APPID: ${{ matrix.id }}
|
||||
PROVIDER: "github"
|
||||
APPLEID: ${{ secrets.APPLEID }}
|
||||
APPLEIDPASS: ${{ secrets.APPLEIDPASS }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
GH_TOKEN: ${{ secrets.GH_PAT }}
|
||||
EP_PRE_RELEASE: true
|
||||
run: ./cmd electron build-mac
|
||||
|
||||
build-win:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
id: [ "com.dootask.task", "com.hitosea.task" ]
|
||||
runs-on: windows-latest
|
||||
environment: build
|
||||
|
||||
if: ${{ (startsWith(github.event.ref, 'refs/tags/v')) && (github.repository == 'kuaifan/dootask') }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js 16.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Build for Windows
|
||||
shell: powershell
|
||||
env:
|
||||
APPID: ${{ matrix.id }}
|
||||
PROVIDER: "github"
|
||||
GH_TOKEN: ${{ secrets.GH_PAT }}
|
||||
EP_PRE_RELEASE: true
|
||||
run: |
|
||||
npm install
|
||||
cd electron
|
||||
npm install
|
||||
cd ../
|
||||
mkdir -p ./electron/public
|
||||
cp ./electron/index.html ./electron/public/index.html
|
||||
npx mix --production -- --env --electron
|
||||
node ./electron/build.js build-win
|
||||
|
||||
33
.github/workflows/publish-desktop.yml
vendored
Normal file
33
.github/workflows/publish-desktop.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Publish Desktop
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
environment: build
|
||||
|
||||
if: startsWith(github.event.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Use Node.js 16.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
APPLEID: ${{ secrets.APPLEID }}
|
||||
APPLEIDPASS: ${{ secrets.APPLEIDPASS }}
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
DP_KEY: ${{ secrets.DP_KEY }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
GH_REPOSITORY: ${{ github.repository }}
|
||||
run: ./cmd electron all
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,10 +5,10 @@
|
||||
/public/.well-known
|
||||
/public/.user.ini
|
||||
/storage/*.key
|
||||
/config/LICENSE
|
||||
/vendor
|
||||
/build
|
||||
/tmp
|
||||
/CHANGELOG.md
|
||||
._*
|
||||
.env
|
||||
.idea
|
||||
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -3,4 +3,4 @@
|
||||
url = https://github.com/jgraph/drawio.git
|
||||
[submodule "resources/mobile"]
|
||||
path = resources/mobile
|
||||
url = git@github.com:kuaifan/dootask-app.git
|
||||
url = https://github.com/kuaifan/dootask-app.git
|
||||
|
||||
13
.gitpod.yml
Normal file
13
.gitpod.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
# This configuration file was automatically generated by Gitpod.
|
||||
# Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file)
|
||||
# and commit this file to your remote git repository to share the goodness with others.
|
||||
|
||||
tasks:
|
||||
- init: sudo ./cmd install
|
||||
command: ./cmd dev
|
||||
|
||||
ports:
|
||||
- port: 2222
|
||||
visibility: public
|
||||
- port: 22222
|
||||
visibility: public
|
||||
2017
CHANGELOG.md
Normal file
2017
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -15,15 +15,15 @@ Group No.: `546574618`
|
||||
- System: `Centos/Debian/Ubuntu/macOS`
|
||||
- Hardware suggestion: 2 cores and above 4G memory
|
||||
|
||||
### Deployment project
|
||||
### Deployment (Pro Edition)
|
||||
|
||||
```bash
|
||||
# 1、Clone the repository
|
||||
|
||||
# Clone projects on github
|
||||
git clone --depth=1 https://github.com/kuaifan/dootask.git
|
||||
git clone -b pro --depth=1 https://github.com/kuaifan/dootask.git
|
||||
# Or you can use gitee
|
||||
git clone --depth=1 https://gitee.com/aipaw/dootask.git
|
||||
git clone -b pro --depth=1 https://gitee.com/aipaw/dootask.git
|
||||
|
||||
# 2、Enter directory
|
||||
cd dootask
|
||||
@@ -116,7 +116,8 @@ git pull
|
||||
./cmd mysql recovery
|
||||
```
|
||||
|
||||
If 502 after the upgrade please run `./cmd restart` restart the service.
|
||||
* Please try again if the upgrade fails across a large version.
|
||||
* If 502 after the upgrade please run `./cmd restart` restart the service.
|
||||
|
||||
## Transfer
|
||||
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
- 支持环境:`Centos/Debian/Ubuntu/macOS`
|
||||
- 硬件建议:2核4G以上
|
||||
|
||||
### 部署项目
|
||||
### 部署项目(Pro版)
|
||||
|
||||
```bash
|
||||
# 1、克隆项目到您的本地或服务器
|
||||
|
||||
# 通过github克隆项目
|
||||
git clone --depth=1 https://github.com/kuaifan/dootask.git
|
||||
git clone -b pro --depth=1 https://github.com/kuaifan/dootask.git
|
||||
# 或者你也可以使用gitee
|
||||
git clone --depth=1 https://gitee.com/aipaw/dootask.git
|
||||
git clone -b pro --depth=1 https://gitee.com/aipaw/dootask.git
|
||||
|
||||
# 2、进入目录
|
||||
cd dootask
|
||||
@@ -117,7 +117,8 @@ git pull
|
||||
./cmd mysql recovery
|
||||
```
|
||||
|
||||
如果升级后出现502请运行 `./cmd restart` 重启服务即可。
|
||||
* 跨越大版本升级失败时请重试执行一次。
|
||||
* 如果升级后出现502请运行 `./cmd restart` 重启服务即可。
|
||||
|
||||
## 迁移项目
|
||||
|
||||
|
||||
@@ -2,25 +2,23 @@
|
||||
|
||||
## 发布前
|
||||
|
||||
1. 添加环境变量 `APPLEID`、`APPLEIDPASS`、`CSC_LINK`
|
||||
2. 发布GitHub还需要添加 `GH_PAT`
|
||||
1. 添加环境变量 `APPLEID`、`APPLEIDPASS` 用于公证
|
||||
2. 添加环境变量 `CSC_LINK`、`CSC_KEY_PASSWORD` 用于签名
|
||||
3. 添加环境变量 `GH_TOKEN`、`GH_REPOSITORY` 用于发布到GitHub
|
||||
4. 添加环境变量 `DP_KEY` 用于发布到私有服务器
|
||||
|
||||
## 通过 GitHub Actions 发布
|
||||
|
||||
1. 执行 `./cmd prod` 编译
|
||||
2. 执行 `node ./version.js` 制作版本
|
||||
3. 执行 `git commit` 相关操作
|
||||
4. 制作标签
|
||||
5. 推送标签
|
||||
1. 执行 `npm run version` 生成版本
|
||||
2. 执行 `npm run build` 编译前端
|
||||
3. 执行 `git commit` 提交并推送
|
||||
4. 添加并推送标签
|
||||
|
||||
## 本地发布
|
||||
|
||||
1. 执行 `./cmd prod` 编译
|
||||
2. 执行 `node ./version.js` 制作版本
|
||||
3. 执行 `git commit` 相关操作
|
||||
4. 制作标签
|
||||
5. 执行 `./cmd electron` 相关操作
|
||||
|
||||
1. 执行 `npm run version` 生成版本
|
||||
2. 执行 `npm run build` 编译前端
|
||||
3. 执行 `./cmd electron` 相关操作
|
||||
|
||||
## 编译App
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
/**
|
||||
* A helper file for Laravel, to provide autocomplete information to your IDE
|
||||
* Generated for Laravel 8.83.16.
|
||||
* Generated for Laravel 8.83.27.
|
||||
*
|
||||
* This file should not be included in your code, only analyzed by your IDE!
|
||||
*
|
||||
@@ -2167,6 +2167,17 @@
|
||||
{
|
||||
/** @var \Illuminate\Auth\SessionGuard $instance */
|
||||
return $instance->setRequest($request);
|
||||
}
|
||||
/**
|
||||
* Get the timebox instance used by the guard.
|
||||
*
|
||||
* @return \Illuminate\Support\Timebox
|
||||
* @static
|
||||
*/
|
||||
public static function getTimebox()
|
||||
{
|
||||
/** @var \Illuminate\Auth\SessionGuard $instance */
|
||||
return $instance->getTimebox();
|
||||
}
|
||||
/**
|
||||
* Determine if the current user is authenticated. If not, throw an exception.
|
||||
@@ -9878,12 +9889,12 @@
|
||||
* Clones a request and overrides some of its parameters.
|
||||
*
|
||||
* @return static
|
||||
* @param array $query The GET parameters
|
||||
* @param array $request The POST parameters
|
||||
* @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
|
||||
* @param array $cookies The COOKIE parameters
|
||||
* @param array $files The FILES parameters
|
||||
* @param array $server The SERVER parameters
|
||||
* @param array|null $query The GET parameters
|
||||
* @param array|null $request The POST parameters
|
||||
* @param array|null $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
|
||||
* @param array|null $cookies The COOKIE parameters
|
||||
* @param array|null $files The FILES parameters
|
||||
* @param array|null $server The SERVER parameters
|
||||
* @return static
|
||||
* @static
|
||||
*/
|
||||
@@ -16109,6 +16120,16 @@
|
||||
{
|
||||
/** @var \Facade\FlareClient\Flare $instance */
|
||||
return $instance->filterExceptionsUsing($filterExceptionsCallable);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
public static function filterReportsUsing($filterReportsCallable)
|
||||
{
|
||||
/** @var \Facade\FlareClient\Flare $instance */
|
||||
return $instance->filterReportsUsing($filterReportsCallable);
|
||||
}
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -67,7 +67,13 @@ class Handler extends ExceptionHandler
|
||||
public function report(Throwable $e)
|
||||
{
|
||||
if ($e instanceof ApiException) {
|
||||
Log::error($e->getMessage(), ['exception' => ' at ' . $e->getFile() .':' . $e->getLine()]);
|
||||
if ($e->getCode() !== -1) {
|
||||
Log::error($e->getMessage(), [
|
||||
'code' => $e->getCode(),
|
||||
'data' => $e->getData(),
|
||||
'exception' => ' at ' . $e->getFile() . ':' . $e->getLine()
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
parent::report($e);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\AbstractModel;
|
||||
use App\Models\Deleted;
|
||||
use App\Models\File;
|
||||
use App\Models\FileContent;
|
||||
use App\Models\ProjectTask;
|
||||
@@ -13,11 +15,11 @@ use App\Models\WebSocketDialogMsgRead;
|
||||
use App\Models\WebSocketDialogMsgTodo;
|
||||
use App\Models\WebSocketDialogUser;
|
||||
use App\Module\Base;
|
||||
use App\Module\TimeRange;
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use Redirect;
|
||||
use Request;
|
||||
use Response;
|
||||
|
||||
/**
|
||||
* @apiDefine dialog
|
||||
@@ -34,9 +36,12 @@ class DialogController extends AbstractController
|
||||
* @apiGroup dialog
|
||||
* @apiName lists
|
||||
*
|
||||
* @apiParam {String} [at_after] 只读取在这个时间之后更新的对话
|
||||
* @apiParam {String} [timerange] 时间范围(如:1678248944,1678248944)
|
||||
* - 第一个时间: 读取在这个时间之后更新的数据
|
||||
* - 第二个时间: 读取在这个时间之后删除的数据ID(第1页附加返回数据: deleted_id)
|
||||
*
|
||||
* @apiParam {Number} [page] 当前页,默认:1
|
||||
* @apiParam {Number} [pagesize] 每页显示数量,默认:100,最大:200
|
||||
* @apiParam {Number} [pagesize] 每页显示数量,默认:50,最大:100
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
@@ -46,21 +51,28 @@ class DialogController extends AbstractController
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$builder = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread'])
|
||||
$timerange = TimeRange::parse(Request::input());
|
||||
//
|
||||
$builder = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.updated_at as user_at'])
|
||||
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
|
||||
->where('u.userid', $user->userid);
|
||||
if (Request::exists('at_after')) {
|
||||
$builder->where('web_socket_dialogs.last_at', '>', Carbon::parse(Request::input('at_after')));
|
||||
if ($timerange->updated) {
|
||||
$builder->where('u.updated_at', '>', $timerange->updated);
|
||||
}
|
||||
$list = $builder
|
||||
->orderByDesc('u.top_at')
|
||||
->orderByDesc('web_socket_dialogs.last_at')
|
||||
->paginate(Base::getPaginate(200, 100));
|
||||
->paginate(Base::getPaginate(100, 50));
|
||||
$list->transform(function (WebSocketDialog $item) use ($user) {
|
||||
return $item->formatData($user->userid);
|
||||
});
|
||||
//
|
||||
return Base::retSuccess('success', $list);
|
||||
$data = $list->toArray();
|
||||
if ($list->currentPage() === 1) {
|
||||
$data['deleted_id'] = Deleted::ids('dialog', $user->userid, $timerange->deleted);
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('success', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,8 +97,8 @@ class DialogController extends AbstractController
|
||||
if (empty($key)) {
|
||||
return Base::retError('请输入搜索关键词');
|
||||
}
|
||||
//
|
||||
$list = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread'])
|
||||
// 搜索会话
|
||||
$dialogs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.updated_at as user_at'])
|
||||
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
|
||||
->where('web_socket_dialogs.name', 'LIKE', "%{$key}%")
|
||||
->where('u.userid', $user->userid)
|
||||
@@ -94,11 +106,36 @@ class DialogController extends AbstractController
|
||||
->orderByDesc('web_socket_dialogs.last_at')
|
||||
->take(20)
|
||||
->get();
|
||||
$list->transform(function (WebSocketDialog $item) use ($user) {
|
||||
$dialogs->transform(function (WebSocketDialog $item) use ($user) {
|
||||
return $item->formatData($user->userid);
|
||||
});
|
||||
$list = $dialogs->toArray();
|
||||
// 搜索联系人
|
||||
if (count($list) < 20 && Base::judgeClientVersion("0.21.60")) {
|
||||
$users = User::select(User::$basicField)
|
||||
->where(function ($query) use ($key) {
|
||||
if (str_contains($key, "@")) {
|
||||
$query->where("email", "like", "%{$key}%");
|
||||
} else {
|
||||
$query->where("nickname", "like", "%{$key}%")->orWhere("pinyin", "like", "%{$key}%");
|
||||
}
|
||||
})->orderBy('userid')
|
||||
->take(20 - count($list))
|
||||
->get();
|
||||
$users->transform(function (User $item) {
|
||||
return [
|
||||
'id' => 'u:' . $item->userid,
|
||||
'type' => 'user',
|
||||
'name' => $item->nickname,
|
||||
'dialog_user' => $item,
|
||||
'last_msg' => null,
|
||||
];
|
||||
});
|
||||
$list = array_merge($list, $users->toArray());
|
||||
}
|
||||
// 搜索消息会话
|
||||
if (count($list) < 20) {
|
||||
$msgs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'm.id as search_msg_id'])
|
||||
$msgs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.updated_at as user_at', 'm.id as search_msg_id'])
|
||||
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
|
||||
->join('web_socket_dialog_msgs as m', 'web_socket_dialogs.id', '=', 'm.dialog_id')
|
||||
->where('u.userid', $user->userid)
|
||||
@@ -109,7 +146,7 @@ class DialogController extends AbstractController
|
||||
$msgs->transform(function (WebSocketDialog $item) use ($user) {
|
||||
return $item->formatData($user->userid);
|
||||
});
|
||||
$list = array_merge($list->toArray(), $msgs->toArray());
|
||||
$list = array_merge($list, $msgs->toArray());
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('success', $list);
|
||||
@@ -135,7 +172,7 @@ class DialogController extends AbstractController
|
||||
//
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
//
|
||||
$item = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread'])
|
||||
$item = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.updated_at as user_at'])
|
||||
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
|
||||
->where('web_socket_dialogs.id', $dialog_id)
|
||||
->where('u.userid', $user->userid)
|
||||
@@ -279,6 +316,9 @@ class DialogController extends AbstractController
|
||||
if (empty($callUser) || empty($callUser->tel)) {
|
||||
return Base::retError("对方未设置联系电话");
|
||||
}
|
||||
if ($user->isTemp()) {
|
||||
return Base::retError("无法查看联系电话");
|
||||
}
|
||||
//
|
||||
$add = null;
|
||||
$res = WebSocketDialogMsg::sendMsg(null, $dialog->id, 'notice', [
|
||||
@@ -317,7 +357,7 @@ class DialogController extends AbstractController
|
||||
return Base::retError('错误的会话');
|
||||
}
|
||||
//
|
||||
$dialog = WebSocketDialog::checkUserDialog($user->userid, $userid);
|
||||
$dialog = WebSocketDialog::checkUserDialog($user, $userid);
|
||||
if (empty($dialog)) {
|
||||
return Base::retError('打开会话失败');
|
||||
}
|
||||
@@ -334,7 +374,7 @@ class DialogController extends AbstractController
|
||||
* @apiName msg__list
|
||||
*
|
||||
* @apiParam {Number} dialog_id 对话ID
|
||||
* @apiParam {Number} msg_id 消息ID
|
||||
* @apiParam {Number} [msg_id] 消息ID
|
||||
* @apiParam {Number} [position_id] 此消息ID前后的数据
|
||||
* @apiParam {Number} [prev_id] 此消息ID之前的数据
|
||||
* @apiParam {Number} [next_id] 此消息ID之后的数据
|
||||
@@ -436,6 +476,7 @@ class DialogController extends AbstractController
|
||||
->value('id'));
|
||||
}
|
||||
$data['list'] = $list;
|
||||
$data['time'] = Base::time();
|
||||
// 记录当前打开的任务对话
|
||||
if ($dialog->type == 'group' && $dialog->group_type == 'task') {
|
||||
$user->task_dialog_id = $dialog->id;
|
||||
@@ -456,7 +497,44 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/one 10. 获取单条消息
|
||||
* @api {get} api/dialog/msg/search 10. 搜索消息位置
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__search
|
||||
*
|
||||
* @apiParam {Number} dialog_id 对话ID
|
||||
* @apiParam {String} key 搜索关键词
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__search()
|
||||
{
|
||||
User::auth();
|
||||
//
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
$key = trim(Request::input('key'));
|
||||
//
|
||||
if (empty($key)) {
|
||||
return Base::retError('关键词不能为空');
|
||||
}
|
||||
//
|
||||
WebSocketDialog::checkDialog($dialog_id);
|
||||
//
|
||||
$data = WebSocketDialogMsg::whereDialogId($dialog_id)
|
||||
->where('key', 'LIKE', "%{$key}%")
|
||||
->take(200)
|
||||
->pluck('id');
|
||||
return Base::retSuccess('success', [
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/one 11. 获取单条消息
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -485,7 +563,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/read 11. 标记已读
|
||||
* @api {get} api/dialog/msg/read 12. 已读聊天消息
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -505,50 +583,79 @@ class DialogController extends AbstractController
|
||||
$id = Request::input('id');
|
||||
$ids = Base::explodeInt($id);
|
||||
//
|
||||
WebSocketDialogMsg::whereIn('id', $ids)->chunkById(20, function($list) use ($user) {
|
||||
$dialogIds = [];
|
||||
WebSocketDialogMsg::whereIn('id', $ids)->chunkById(20, function($list) use ($user, &$dialogIds) {
|
||||
/** @var WebSocketDialogMsg $item */
|
||||
foreach ($list as $item) {
|
||||
$item->readSuccess($user->userid);
|
||||
$dialogIds[$item->dialog_id] = $item->dialog_id;
|
||||
}
|
||||
});
|
||||
return Base::retSuccess('success');
|
||||
//
|
||||
$data = [];
|
||||
$dialogUsers = WebSocketDialogUser::with(['webSocketDialog'])->whereUserid($user->userid)->whereIn('dialog_id', array_values($dialogIds))->get();
|
||||
foreach ($dialogUsers as $dialogUser) {
|
||||
if (!$dialogUser->webSocketDialog) {
|
||||
continue;
|
||||
}
|
||||
$dialogUser->updated_at = Carbon::now();
|
||||
$dialogUser->save();
|
||||
//
|
||||
$dialogUser->webSocketDialog->generateUnread($user->userid);
|
||||
$data[] = [
|
||||
'id' => $dialogUser->webSocketDialog->id,
|
||||
'unread' => $dialogUser->webSocketDialog->unread,
|
||||
'mention' => $dialogUser->webSocketDialog->mention,
|
||||
'user_at' => Carbon::parse($dialogUser->updated_at)->toDateTimeString('millisecond'),
|
||||
'user_ms' => Carbon::parse($dialogUser->updated_at)->valueOf()
|
||||
];
|
||||
}
|
||||
return Base::retSuccess('success', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/unread 12. 获取未读消息数量
|
||||
* @api {get} api/dialog/msg/unread 13. 获取未读消息数据
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__unread
|
||||
*
|
||||
* @apiParam {Number} [dialog_id] 对话ID,留空获取总未读消息数量
|
||||
* @apiParam {Number} dialog_id 对话ID
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
* @apiSuccessExample {json} data:
|
||||
{
|
||||
"unread": 43, // 未读消息数
|
||||
"last_umid": 308 // 最新的一条未读消息ID,用于判断是否更新前端的未读数量
|
||||
"id": 43,
|
||||
"unread": 308,
|
||||
"mention": 11,
|
||||
"user_at": "2020-12-12 00:00:00.000",
|
||||
"user_ms": 1677558147167,
|
||||
}
|
||||
*/
|
||||
public function msg__unread()
|
||||
{
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
//
|
||||
$builder = WebSocketDialogMsgRead::whereUserid(User::userid())->whereReadAt(null);
|
||||
if ($dialog_id > 0) {
|
||||
$builder->whereDialogId($dialog_id);
|
||||
$dialogUser = WebSocketDialogUser::with(['webSocketDialog'])->whereDialogId($dialog_id)->whereUserid(User::userid())->first();
|
||||
if (empty($dialogUser?->webSocketDialog)) {
|
||||
return Base::retError('会话不存在');
|
||||
}
|
||||
$dialogUser->webSocketDialog->generateUnread($dialogUser->userid);
|
||||
//
|
||||
return Base::retSuccess('success', [
|
||||
'unread' => $builder->count(),
|
||||
'last_umid' => intval($builder->orderByDesc('msg_id')->value('msg_id')),
|
||||
'id' => $dialogUser->webSocketDialog->id,
|
||||
'unread' => $dialogUser->webSocketDialog->unread,
|
||||
'mention' => $dialogUser->webSocketDialog->mention,
|
||||
'user_at' => Carbon::parse($dialogUser->updated_at)->toDateTimeString('millisecond'),
|
||||
'user_ms' => Carbon::parse($dialogUser->updated_at)->valueOf()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/dialog/msg/sendtext 13. 发送消息
|
||||
* @api {post} api/dialog/msg/sendtext 14. 发送消息
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -556,9 +663,15 @@ class DialogController extends AbstractController
|
||||
* @apiName msg__sendtext
|
||||
*
|
||||
* @apiParam {Number} dialog_id 对话ID
|
||||
* @apiParam {Number} [update_id] 更新消息ID(优先大于reply_id)
|
||||
* @apiParam {Number} [reply_id] 回复ID
|
||||
* @apiParam {String} text 消息内容
|
||||
* @apiParam {String} [text_type] 消息类型
|
||||
* - html: HTML(默认)
|
||||
* - md: MARKDOWN
|
||||
* @apiParam {Number} [update_id] 更新消息ID(优先大于 reply_id)
|
||||
* @apiParam {Number} [reply_id] 回复ID
|
||||
* @apiParam {String} [silence] 是否静默发送
|
||||
* - no: 正常发送(默认)
|
||||
* - yes: 静默发送
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
@@ -566,23 +679,27 @@ class DialogController extends AbstractController
|
||||
*/
|
||||
public function msg__sendtext()
|
||||
{
|
||||
Base::checkClientVersion('0.19.0');
|
||||
$user = User::auth();
|
||||
//
|
||||
$chat_information = Base::settingFind('system', 'chat_information');
|
||||
if ($chat_information == 'required') {
|
||||
if (empty($user->getRawOriginal('nickname'))) {
|
||||
return Base::retError('请设置昵称', [], -2);
|
||||
}
|
||||
if (empty($user->getRawOriginal('tel'))) {
|
||||
return Base::retError('请设置联系电话', [], -3);
|
||||
if (!$user->bot) {
|
||||
$chatInformation = Base::settingFind('system', 'chat_information');
|
||||
if ($chatInformation == 'required') {
|
||||
if (empty($user->getRawOriginal('nickname'))) {
|
||||
return Base::retError('请设置昵称', [], -2);
|
||||
}
|
||||
if (empty($user->getRawOriginal('tel'))) {
|
||||
return Base::retError('请设置联系电话', [], -3);
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
$dialog_id = Base::getPostInt('dialog_id');
|
||||
$update_id = Base::getPostInt('update_id');
|
||||
$reply_id = Base::getPostInt('reply_id');
|
||||
$text = trim(Base::getPostValue('text'));
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
$update_id = intval(Request::input('update_id'));
|
||||
$reply_id = intval(Request::input('reply_id'));
|
||||
$text = trim(Request::input('text'));
|
||||
$text_type = strtolower(trim(Request::input('text_type')));
|
||||
$silence = in_array(strtolower(trim(Request::input('silence'))), ['yes', 'true', '1']);
|
||||
$markdown = in_array($text_type, ['md', 'markdown']);
|
||||
//
|
||||
WebSocketDialog::checkDialog($dialog_id);
|
||||
//
|
||||
@@ -594,14 +711,18 @@ class DialogController extends AbstractController
|
||||
$action = "";
|
||||
}
|
||||
//
|
||||
$text = WebSocketDialogMsg::formatMsg($text, $dialog_id);
|
||||
if (!$markdown) {
|
||||
$text = WebSocketDialogMsg::formatMsg($text, $dialog_id);
|
||||
}
|
||||
$strlen = mb_strlen($text);
|
||||
$noimglen = mb_strlen(preg_replace("/<img[^>]*?>/i", "", $text));
|
||||
if ($strlen < 1) {
|
||||
return Base::retError('消息内容不能为空');
|
||||
} elseif ($strlen > 200000) {
|
||||
}
|
||||
if ($noimglen > 200000) {
|
||||
return Base::retError('消息内容最大不能超过200000字');
|
||||
}
|
||||
if ($strlen > 2000) {
|
||||
if ($noimglen > 5000) {
|
||||
// 内容过长转成文件发送
|
||||
$path = "uploads/chat/" . date("Ym") . "/" . $dialog_id . "/";
|
||||
Base::makeDir(public_path($path));
|
||||
@@ -612,8 +733,9 @@ class DialogController extends AbstractController
|
||||
if (empty($size)) {
|
||||
return Base::retError('消息发送保存失败');
|
||||
}
|
||||
$ext = $markdown ? 'md' : 'htm';
|
||||
$fileData = [
|
||||
'name' => "LongText-{$strlen}.htm",
|
||||
'name' => "LongText-{$strlen}.{$ext}",
|
||||
'size' => $size,
|
||||
'file' => $file,
|
||||
'path' => $path,
|
||||
@@ -621,16 +743,20 @@ class DialogController extends AbstractController
|
||||
'thumb' => '',
|
||||
'width' => -1,
|
||||
'height' => -1,
|
||||
'ext' => 'htm',
|
||||
'ext' => $ext,
|
||||
];
|
||||
return WebSocketDialogMsg::sendMsg($action, $dialog_id, 'file', $fileData, $user->userid);
|
||||
return WebSocketDialogMsg::sendMsg($action, $dialog_id, 'file', $fileData, $user->userid, false, false, $silence);
|
||||
}
|
||||
//
|
||||
return WebSocketDialogMsg::sendMsg($action, $dialog_id, 'text', ['text' => $text], $user->userid);
|
||||
$msgData = ['text' => $text];
|
||||
if ($markdown) {
|
||||
$msgData['type'] = 'md';
|
||||
}
|
||||
return WebSocketDialogMsg::sendMsg($action, $dialog_id, 'text', $msgData, $user->userid, false, false, $silence);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/dialog/msg/sendrecord 14. 发送语音
|
||||
* @api {post} api/dialog/msg/sendrecord 15. 发送语音
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -650,15 +776,15 @@ class DialogController extends AbstractController
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$dialog_id = Base::getPostInt('dialog_id');
|
||||
$reply_id = Base::getPostInt('reply_id');
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
$reply_id = intval(Request::input('reply_id'));
|
||||
//
|
||||
WebSocketDialog::checkDialog($dialog_id);
|
||||
//
|
||||
$action = $reply_id > 0 ? "reply-$reply_id" : "";
|
||||
$path = "uploads/chat/" . date("Ym") . "/" . $dialog_id . "/";
|
||||
$base64 = Base::getPostValue('base64');
|
||||
$duration = Base::getPostInt('duration');
|
||||
$base64 = Request::input('base64');
|
||||
$duration = intval(Request::input('duration'));
|
||||
if ($duration < 600) {
|
||||
return Base::retError('说话时间太短');
|
||||
}
|
||||
@@ -677,7 +803,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/dialog/msg/sendfile 15. 文件上传
|
||||
* @api {post} api/dialog/msg/sendfile 16. 文件上传
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -699,16 +825,16 @@ class DialogController extends AbstractController
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$dialog_id = Base::getPostInt('dialog_id');
|
||||
$reply_id = Base::getPostInt('reply_id');
|
||||
$image_attachment = Base::getPostInt('image_attachment');
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
$reply_id = intval(Request::input('reply_id'));
|
||||
$image_attachment = intval(Request::input('image_attachment'));
|
||||
//
|
||||
$dialog = WebSocketDialog::checkDialog($dialog_id);
|
||||
//
|
||||
$action = $reply_id > 0 ? "reply-$reply_id" : "";
|
||||
$path = "uploads/chat/" . date("Ym") . "/" . $dialog_id . "/";
|
||||
$image64 = Base::getPostValue('image64');
|
||||
$fileName = Base::getPostValue('filename');
|
||||
$image64 = Request::input('image64');
|
||||
$fileName = Request::input('filename');
|
||||
if ($image64) {
|
||||
$data = Base::image64save([
|
||||
"image64" => $image64,
|
||||
@@ -761,7 +887,130 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/readlist 16. 获取消息阅读情况
|
||||
* @api {get} api/dialog/msg/sendfileid 17. 通过文件ID发送文件
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__sendfileid
|
||||
*
|
||||
* @apiParam {Number} file_id 消息ID
|
||||
* @apiParam {Array} dialogids 转发给的对话ID
|
||||
* @apiParam {Array} userids 转发给的成员ID
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__sendfileid()
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$file_id = intval(Request::input("file_id"));
|
||||
$dialogids = Request::input('dialogids');
|
||||
$userids = Request::input('userids');
|
||||
//
|
||||
if (empty($dialogids) && empty($userids)) {
|
||||
return Base::retError("请选择转发对话或成员");
|
||||
}
|
||||
//
|
||||
$file = File::permissionFind($file_id, $user);
|
||||
$fileLink = $file->getShareLink($user->userid);
|
||||
$fileMsg = "<a class=\"mention file\" href=\"{{RemoteURL}}single/file/{$fileLink['code']}\" target=\"_blank\">~{$file->getNameAndExt()}</a>";
|
||||
//
|
||||
return AbstractModel::transaction(function() use ($user, $fileMsg, $userids, $dialogids) {
|
||||
$msgs = [];
|
||||
$already = [];
|
||||
if ($dialogids) {
|
||||
if (!is_array($dialogids)) {
|
||||
$dialogids = [$dialogids];
|
||||
}
|
||||
foreach ($dialogids as $dialogid) {
|
||||
$res = WebSocketDialogMsg::sendMsg(null, $dialogid, 'text', ['text' => $fileMsg], $user->userid);
|
||||
if (Base::isSuccess($res)) {
|
||||
$msgs[] = $res['data'];
|
||||
$already[] = $dialogid;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($userids) {
|
||||
if (!is_array($userids)) {
|
||||
$userids = [$userids];
|
||||
}
|
||||
foreach ($userids as $userid) {
|
||||
if (!User::whereUserid($userid)->exists()) {
|
||||
continue;
|
||||
}
|
||||
$dialog = WebSocketDialog::checkUserDialog($user, $userid);
|
||||
if ($dialog && !in_array($dialog->id, $already)) {
|
||||
$res = WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $fileMsg], $user->userid);
|
||||
if (Base::isSuccess($res)) {
|
||||
$msgs[] = $res['data'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Base::retSuccess('发送成功', [
|
||||
'msgs' => $msgs
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/dialog/msg/sendanon 18. 发送匿名消息
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__sendanon
|
||||
*
|
||||
* @apiParam {Number} userid 对方会员ID
|
||||
* @apiParam {String} text 消息内容
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__sendanon()
|
||||
{
|
||||
User::auth();
|
||||
//
|
||||
$userid = intval(Request::input('userid'));
|
||||
$text = trim(Request::input('text'));
|
||||
//
|
||||
$anonMessage = Base::settingFind('system', 'anon_message', 'open');
|
||||
if ($anonMessage != 'open') {
|
||||
return Base::retError("匿名消息功能暂停使用");
|
||||
}
|
||||
//
|
||||
$toUser = User::whereUserid($userid)->first();
|
||||
if (empty($toUser) || $toUser->bot) {
|
||||
return Base::retError("匿名消息仅允许发送给个人");
|
||||
}
|
||||
if ($toUser->isDisable()) {
|
||||
return Base::retError("对方已离职");
|
||||
}
|
||||
$strlen = mb_strlen($text);
|
||||
if ($strlen < 1) {
|
||||
return Base::retError('消息内容不能为空');
|
||||
}
|
||||
if ($strlen > 2000) {
|
||||
return Base::retError('消息内容最大不能超过2000字');
|
||||
}
|
||||
//
|
||||
$botUser = User::botGetOrCreate('anon-msg');
|
||||
if (empty($botUser)) {
|
||||
return Base::retError('匿名机器人不存在');
|
||||
}
|
||||
$dialog = WebSocketDialog::checkUserDialog($botUser, $toUser->userid);
|
||||
if (empty($dialog)) {
|
||||
return Base::retError('匿名机器人会话不存在');
|
||||
}
|
||||
return WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => "<p>{$text}</p>"], $botUser->userid, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/readlist 19. 获取消息阅读情况
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -790,7 +1039,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/detail 17. 消息详情
|
||||
* @api {get} api/dialog/msg/detail 20. 消息详情
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -838,7 +1087,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/download 18. 文件下载
|
||||
* @api {get} api/dialog/msg/download 21. 文件下载
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -874,11 +1123,14 @@ class DialogController extends AbstractController
|
||||
return Redirect::to(FileContent::toPreviewUrl($array));
|
||||
}
|
||||
//
|
||||
return Response::download(public_path($array['path']), $array['name']);
|
||||
$filePath = public_path($array['path']);
|
||||
return Base::streamDownload(function() use ($filePath) {
|
||||
echo file_get_contents($filePath);
|
||||
}, $array['name']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/withdraw 19. 聊天消息撤回
|
||||
* @api {get} api/dialog/msg/withdraw 22. 聊天消息撤回
|
||||
*
|
||||
* @apiDescription 消息撤回限制24小时内,需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -899,12 +1151,12 @@ class DialogController extends AbstractController
|
||||
if (empty($msg)) {
|
||||
return Base::retError("消息不存在或已被删除");
|
||||
}
|
||||
$msg->deleteMsg();
|
||||
$msg->withdrawMsg();
|
||||
return Base::retSuccess("success");
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/mark 20. 消息标记操作
|
||||
* @api {get} api/dialog/msg/mark 23. 消息标记操作
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -913,14 +1165,81 @@ class DialogController extends AbstractController
|
||||
*
|
||||
* @apiParam {Number} dialog_id 会话ID
|
||||
* @apiParam {String} type 类型
|
||||
* - read
|
||||
* - unread
|
||||
* - read: 已读
|
||||
* - unread: 未读
|
||||
* @apiParam {Number} [after_msg_id] 仅标记已读指定之后(含)的消息
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__mark()
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
$type = Request::input('type');
|
||||
$after_msg_id = intval(Request::input('after_msg_id'));
|
||||
//
|
||||
$dialogUser = WebSocketDialogUser::with(['webSocketDialog'])->whereDialogId($dialog_id)->whereUserid($user->userid)->first();
|
||||
if (empty($dialogUser?->webSocketDialog)) {
|
||||
return Base::retError('会话不存在');
|
||||
}
|
||||
switch ($type) {
|
||||
case 'read':
|
||||
$builder = WebSocketDialogMsgRead::whereDialogId($dialog_id)->whereUserid($user->userid)->whereReadAt(null);
|
||||
if ($after_msg_id > 0) {
|
||||
$builder->where('msg_id', '>=', $after_msg_id);
|
||||
}
|
||||
$builder->chunkById(100, function ($list) {
|
||||
WebSocketDialogMsgRead::onlyMarkRead($list);
|
||||
});
|
||||
//
|
||||
$dialogUser->webSocketDialog->generateUnread($user->userid);
|
||||
$data = [
|
||||
'id' => $dialogUser->webSocketDialog->id,
|
||||
'unread' => $dialogUser->webSocketDialog->unread,
|
||||
'mention' => $dialogUser->webSocketDialog->mention,
|
||||
'mark_unread' => 0,
|
||||
];
|
||||
break;
|
||||
|
||||
case 'unread':
|
||||
$data = [
|
||||
'id' => $dialogUser->webSocketDialog->id,
|
||||
'mark_unread' => 1,
|
||||
];
|
||||
break;
|
||||
|
||||
default:
|
||||
return Base::retError("参数错误");
|
||||
}
|
||||
$dialogUser->mark_unread = $data['mark_unread'];
|
||||
$dialogUser->save();
|
||||
return Base::retSuccess("success", array_merge($data, [
|
||||
'user_at' => Carbon::parse($dialogUser->updated_at)->toDateTimeString('millisecond'),
|
||||
'user_ms' => Carbon::parse($dialogUser->updated_at)->valueOf(),
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/silence 24. 消息免打扰
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName msg__silence
|
||||
*
|
||||
* @apiParam {Number} dialog_id 会话ID
|
||||
* @apiParam {String} type 类型
|
||||
* - set
|
||||
* - cancel
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function msg__silence()
|
||||
{
|
||||
$user = User::auth();
|
||||
$dialogId = intval(Request::input('dialog_id'));
|
||||
@@ -929,34 +1248,45 @@ class DialogController extends AbstractController
|
||||
if (!$dialogUser) {
|
||||
return Base::retError("会话不存在");
|
||||
}
|
||||
//
|
||||
$dialogData = WebSocketDialog::find($dialogId);
|
||||
if (empty($dialogData)) {
|
||||
return Base::retError("会话不存在");
|
||||
}
|
||||
if ($dialogData->type === 'group' && $dialogData->group_type !== 'user') {
|
||||
return Base::retError("此会话不允许设置免打扰");
|
||||
}
|
||||
//
|
||||
switch ($type) {
|
||||
case 'read':
|
||||
case 'set':
|
||||
$data['silence'] = 0;
|
||||
WebSocketDialogMsgRead::whereUserid($user->userid)
|
||||
->whereReadAt(null)
|
||||
->whereDialogId($dialogId)
|
||||
->chunkById(100, function ($list) {
|
||||
WebSocketDialogMsgRead::onlyMarkRead($list);
|
||||
});
|
||||
$dialogUser->mark_unread = 0;
|
||||
$dialogUser->silence = 1;
|
||||
$dialogUser->save();
|
||||
break;
|
||||
|
||||
case 'unread':
|
||||
$dialogUser->mark_unread = 1;
|
||||
case 'cancel':
|
||||
$dialogUser->silence = 0;
|
||||
$dialogUser->save();
|
||||
break;
|
||||
|
||||
default:
|
||||
return Base::retError("参数错误");
|
||||
}
|
||||
return Base::retSuccess("success", [
|
||||
$data = [
|
||||
'id' => $dialogId,
|
||||
'mark_unread' => $dialogUser->mark_unread,
|
||||
]);
|
||||
'silence' => $dialogUser->silence,
|
||||
];
|
||||
return Base::retSuccess("success", $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/forward 21. 转发消息给
|
||||
* @api {get} api/dialog/msg/forward 25. 转发消息给
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -989,11 +1319,11 @@ class DialogController extends AbstractController
|
||||
}
|
||||
WebSocketDialog::checkDialog($msg->dialog_id);
|
||||
//
|
||||
return $msg->forwardMsg($dialogids, $userids, $user->userid);
|
||||
return $msg->forwardMsg($dialogids, $userids, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/emoji 22. emoji回复
|
||||
* @api {get} api/dialog/msg/emoji 26. emoji回复
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1028,7 +1358,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/tag 23. 标注/取消标注
|
||||
* @api {get} api/dialog/msg/tag 27. 标注/取消标注
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1057,7 +1387,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/todo 24. 设待办/取消待办
|
||||
* @api {get} api/dialog/msg/todo 28. 设待办/取消待办
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1100,7 +1430,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/todolist 25. 获取消息待办情况
|
||||
* @api {get} api/dialog/msg/todolist 29. 获取消息待办情况
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1130,7 +1460,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/done 26. 完成待办
|
||||
* @api {get} api/dialog/msg/done 30. 完成待办
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1177,15 +1507,16 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/group/add 27. 新增群组
|
||||
* @api {get} api/dialog/group/add 31. 新增群组
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup dialog
|
||||
* @apiName group__add
|
||||
*
|
||||
* @apiParam {String} [avatar] 群头像
|
||||
* @apiParam {String} [chat_name] 群名称
|
||||
* @apiParam {Array} userids 群成员,格式: [userid1, userid2, userid3]
|
||||
* @apiParam {String} chat_name 群名称
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
@@ -1195,8 +1526,10 @@ class DialogController extends AbstractController
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$userids = Request::input('userids');
|
||||
$avatar = Request::input('avatar');
|
||||
$avatar = $avatar ? Base::unFillUrl(is_array($avatar) ? $avatar[0]['path'] : $avatar) : '';
|
||||
$chatName = trim(Request::input('chat_name'));
|
||||
$userids = Request::input('userids');
|
||||
//
|
||||
if (!is_array($userids)) {
|
||||
return Base::retError('请选择群成员');
|
||||
@@ -1218,10 +1551,17 @@ class DialogController extends AbstractController
|
||||
}
|
||||
$chatName = implode(", ", $array);
|
||||
}
|
||||
if ($user->isTemp()) {
|
||||
return Base::retError('无法创建群组');
|
||||
}
|
||||
$dialog = WebSocketDialog::createGroup($chatName, $userids, 'user', $user->userid);
|
||||
if (empty($dialog)) {
|
||||
return Base::retError('创建群组失败');
|
||||
}
|
||||
if ($avatar) {
|
||||
$dialog->avatar = $avatar;
|
||||
$dialog->save();
|
||||
}
|
||||
$data = WebSocketDialog::find($dialog->id)?->formatData($user->userid);
|
||||
$userids = array_values(array_diff($userids, [$user->userid]));
|
||||
$dialog->pushMsg("groupAdd", null, $userids);
|
||||
@@ -1229,7 +1569,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/group/edit 28. 修改群组
|
||||
* @api {get} api/dialog/group/edit 32. 修改群组
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1237,7 +1577,9 @@ class DialogController extends AbstractController
|
||||
* @apiName group__edit
|
||||
*
|
||||
* @apiParam {Number} dialog_id 会话ID
|
||||
* @apiParam {String} chat_name 群名称
|
||||
* @apiParam {String} [avatar] 群头像
|
||||
* @apiParam {String} [chat_name] 群名称
|
||||
* @apiParam {Number} [admin] 系统管理员操作(1:只判断是不是系统管理员,否则判断是否群管理员)
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
@@ -1248,27 +1590,47 @@ class DialogController extends AbstractController
|
||||
$user = User::auth();
|
||||
//
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
$chatName = trim(Request::input('chat_name'));
|
||||
$admin = intval(Request::input('admin'));
|
||||
//
|
||||
if (mb_strlen($chatName) < 2) {
|
||||
return Base::retError('群名称至少2个字');
|
||||
}
|
||||
if (mb_strlen($chatName) > 100) {
|
||||
return Base::retError('群名称最长限制100个字');
|
||||
if ($admin === 1) {
|
||||
$user->checkAdmin();
|
||||
$dialog = WebSocketDialog::find($dialog_id);
|
||||
if (empty($dialog)) {
|
||||
return Base::retError('对话不存在或已被删除', ['dialog_id' => $dialog_id], -4003);
|
||||
}
|
||||
} else {
|
||||
$dialog = WebSocketDialog::checkDialog($dialog_id, true);
|
||||
}
|
||||
//
|
||||
$dialog = WebSocketDialog::checkDialog($dialog_id, true);
|
||||
$data = ['id' => $dialog->id];
|
||||
$array = [];
|
||||
if (Request::exists('avatar')) {
|
||||
$avatar = Request::input('avatar');
|
||||
$avatar = $avatar ? Base::unFillUrl(is_array($avatar) ? $avatar[0]['path'] : $avatar) : '';
|
||||
$data['avatar'] = Base::fillUrl($array['avatar'] = $avatar);
|
||||
}
|
||||
if (Request::exists('chat_name') && $dialog->group_type === 'user') {
|
||||
$chatName = trim(Request::input('chat_name'));
|
||||
if (mb_strlen($chatName) < 2) {
|
||||
return Base::retError('群名称至少2个字');
|
||||
}
|
||||
if (mb_strlen($chatName) > 100) {
|
||||
return Base::retError('群名称最长限制100个字');
|
||||
}
|
||||
$data['name'] = $array['name'] = $chatName;
|
||||
}
|
||||
//
|
||||
$dialog->name = $chatName;
|
||||
$dialog->save();
|
||||
return Base::retSuccess('修改成功', [
|
||||
'id' => $dialog->id,
|
||||
'name' => $dialog->name,
|
||||
]);
|
||||
if ($array) {
|
||||
$dialog->updateInstance($array);
|
||||
$dialog->save();
|
||||
WebSocketDialogUser::whereDialogId($dialog->id)->change(['updated_at' => Carbon::now()->toDateTimeString('millisecond')]);
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('修改成功', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/group/adduser 29. 添加群成员
|
||||
* @api {get} api/dialog/group/adduser 33. 添加群成员
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* - 有群主时:只有群主可以邀请
|
||||
@@ -1304,7 +1666,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/group/deluser 30. 移出(退出)群成员
|
||||
* @api {get} api/dialog/group/deluser 34. 移出(退出)群成员
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* - 只有群主、邀请人可以踢人
|
||||
@@ -1348,7 +1710,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/group/transfer 31. 转让群组
|
||||
* @api {get} api/dialog/group/transfer 35. 转让群组
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* - 只有群主且是个人类型群可以解散
|
||||
@@ -1392,7 +1754,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/group/disband 32. 解散群组
|
||||
* @api {get} api/dialog/group/disband 36. 解散群组
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* - 只有群主且是个人类型群可以解散
|
||||
@@ -1416,12 +1778,11 @@ class DialogController extends AbstractController
|
||||
//
|
||||
$dialog->checkGroup('user');
|
||||
$dialog->deleteDialog();
|
||||
$dialog->pushMsg("groupDelete");
|
||||
return Base::retSuccess('解散成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/group/searchuser 33. 搜索个人群(仅限管理员)
|
||||
* @api {get} api/dialog/group/searchuser 37. 搜索个人群(仅限管理员)
|
||||
*
|
||||
* @apiDescription 需要token身份,用于创建部门搜索个人群组
|
||||
* @apiVersion 1.0.0
|
||||
|
||||
@@ -45,9 +45,10 @@ class FileController extends AbstractController
|
||||
$pid = intval($data['pid']);
|
||||
//
|
||||
$permission = 1000;
|
||||
$userids = $user->isTemp() ? [$user->userid] : [0, $user->userid];
|
||||
$builder = File::wherePid($pid);
|
||||
if ($pid > 0) {
|
||||
File::permissionFind($pid, 0, $permission);
|
||||
File::permissionFind($pid, $userids, 0, $permission);
|
||||
} else {
|
||||
$builder->whereUserid($user->userid);
|
||||
}
|
||||
@@ -66,7 +67,7 @@ class FileController extends AbstractController
|
||||
}
|
||||
$pid = $file->pid;
|
||||
$temp = $file->toArray();
|
||||
$temp['permission'] = $file->getPermission($user->userid);
|
||||
$temp['permission'] = $file->getPermission($userids);
|
||||
$array[] = $temp;
|
||||
}
|
||||
// 去除没有权限的文件
|
||||
@@ -92,9 +93,7 @@ class FileController extends AbstractController
|
||||
$list = File::select(["files.*", DB::raw("MAX({$pre}file_users.permission) as permission")])
|
||||
->join('file_users', 'files.id', '=', 'file_users.file_id')
|
||||
->where('files.userid', '!=', $user->userid)
|
||||
->where(function ($query) use ($user) {
|
||||
$query->whereIn('file_users.userid', [0, $user->userid]);
|
||||
})
|
||||
->whereIn('file_users.userid', $userids)
|
||||
->groupBy('files.id')
|
||||
->take(100)
|
||||
->get();
|
||||
@@ -135,13 +134,18 @@ class FileController extends AbstractController
|
||||
//
|
||||
$permission = 0;
|
||||
if (Base::isNumber($id)) {
|
||||
User::auth();
|
||||
$file = File::permissionFind(intval($id), 0, $permission);
|
||||
$user = User::auth();
|
||||
$file = File::permissionFind(intval($id), $user, 0, $permission);
|
||||
} elseif ($id) {
|
||||
$fileLink = FileLink::whereCode($id)->first();
|
||||
$file = $fileLink?->file;
|
||||
if (empty($file)) {
|
||||
return Base::retError('链接不存在');
|
||||
$msg = '文件链接不存在';
|
||||
$data = File::code2IdName($id);
|
||||
if ($data) {
|
||||
$msg = "【{$data->name}】 {$msg}";
|
||||
}
|
||||
return Base::retError($msg, $data);
|
||||
}
|
||||
} else {
|
||||
return Base::retError('参数错误');
|
||||
@@ -160,6 +164,7 @@ class FileController extends AbstractController
|
||||
* @apiGroup file
|
||||
* @apiName search
|
||||
*
|
||||
* @apiParam {String} [link] 通过分享地址搜索(如:https://t.hitosea.com/single/file/ODcwOCwzOSxpa0JBS2lmVQ==)
|
||||
* @apiParam {String} [key] 关键词
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
@@ -170,28 +175,45 @@ class FileController extends AbstractController
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$link = trim(Request::input('link'));
|
||||
$key = trim(Request::input('key'));
|
||||
$id = 0;
|
||||
$take = 50;
|
||||
if (preg_match("/\/single\/file\/(.*?)$/i", $link, $match)) {
|
||||
$id = intval(FileLink::whereCode($match[1])->value('file_id'));
|
||||
$take = 1;
|
||||
if (empty($id)) {
|
||||
return Base::retSuccess('success', []);
|
||||
}
|
||||
}
|
||||
// 搜索自己的
|
||||
$builder = File::whereUserid($user->userid);
|
||||
if ($id) {
|
||||
$builder->where("id", $id);
|
||||
}
|
||||
if ($key) {
|
||||
$builder->where("name", "like", "%{$key}%");
|
||||
}
|
||||
$array = $builder->take(50)->get()->toArray();
|
||||
$array = $builder->take($take)->get()->toArray();
|
||||
// 搜索共享的
|
||||
$take = 50 - count($array);
|
||||
if ($take > 0 && $key) {
|
||||
$list = File::where("name", "like", "%{$key}%")
|
||||
->whereIn('pshare', function ($queryA) use ($user) {
|
||||
$queryA->select('files.id')
|
||||
->from('files')
|
||||
->join('file_users', 'files.id', '=', 'file_users.file_id')
|
||||
->where('files.userid', '!=', $user->userid)
|
||||
->where(function ($queryB) use ($user) {
|
||||
$queryB->whereIn('file_users.userid', [0, $user->userid]);
|
||||
});
|
||||
})
|
||||
->take($take)
|
||||
->get();
|
||||
$take = $take - count($array);
|
||||
if ($take > 0 && ($id || $key)) {
|
||||
$builder = File::whereIn('pshare', function ($queryA) use ($user) {
|
||||
$queryA->select('files.id')
|
||||
->from('files')
|
||||
->join('file_users', 'files.id', '=', 'file_users.file_id')
|
||||
->where('files.userid', '!=', $user->userid)
|
||||
->where(function ($queryB) use ($user) {
|
||||
$queryB->whereIn('file_users.userid', [0, $user->userid]);
|
||||
});
|
||||
});
|
||||
if ($id) {
|
||||
$builder->where("id", $id);
|
||||
}
|
||||
if ($key) {
|
||||
$builder->where("name", "like", "%{$key}%");
|
||||
}
|
||||
$list = $builder->take($take)->get();
|
||||
if ($list->isNotEmpty()) {
|
||||
foreach ($list as $file) {
|
||||
$temp = $file->toArray();
|
||||
@@ -236,15 +258,24 @@ class FileController extends AbstractController
|
||||
} elseif (mb_strlen($name) > 32) {
|
||||
return Base::retError('文件名称最多只能设置32个字');
|
||||
}
|
||||
$tmpName = preg_replace("/[\\\\\/:*?\"<>|]/", '', $name);
|
||||
if ($tmpName != $name) {
|
||||
return Base::retError("文件名称不能包含这些字符:\/:*?\"<>|");
|
||||
}
|
||||
//
|
||||
if ($id > 0) {
|
||||
// 修改
|
||||
$file = File::permissionFind($id, 1);
|
||||
$file = File::permissionFind($id, $user, 1);
|
||||
//
|
||||
$file->name = $name;
|
||||
$file->handleDuplicateName();
|
||||
$file->save();
|
||||
$file->pushMsg('update', $file);
|
||||
return Base::retSuccess('修改成功', $file);
|
||||
$data = [
|
||||
'id' => $file->id,
|
||||
'name' => $file->name,
|
||||
];
|
||||
$file->pushMsg('update', $data);
|
||||
return Base::retSuccess('修改成功', $data);
|
||||
} else {
|
||||
// 添加
|
||||
if (!in_array($type, [
|
||||
@@ -281,7 +312,7 @@ class FileController extends AbstractController
|
||||
if (File::wherePid($pid)->count() >= 300) {
|
||||
return Base::retError('每个文件夹里最多只能创建300个文件或文件夹');
|
||||
}
|
||||
$row = File::permissionFind($pid, 1);
|
||||
$row = File::permissionFind($pid, $user, 1);
|
||||
$userid = $row->userid;
|
||||
} else {
|
||||
if (File::whereUserid($user->userid)->wherePid(0)->count() >= 300) {
|
||||
@@ -326,7 +357,7 @@ class FileController extends AbstractController
|
||||
//
|
||||
$id = intval(Request::input('id'));
|
||||
//
|
||||
$row = File::permissionFind($id);
|
||||
$row = File::permissionFind($id, $user);
|
||||
//
|
||||
$userid = $user->userid;
|
||||
if ($row->pid > 0) {
|
||||
@@ -396,14 +427,14 @@ class FileController extends AbstractController
|
||||
}
|
||||
$toShareFile = false;
|
||||
if ($pid > 0) {
|
||||
$tmpFile = File::permissionFind($pid, 1);
|
||||
$tmpFile = File::permissionFind($pid, $user, 1);
|
||||
$toShareFile = $tmpFile->getShareInfo();
|
||||
}
|
||||
//
|
||||
$files = [];
|
||||
AbstractModel::transaction(function() use ($user, $pid, $ids, $toShareFile, &$files) {
|
||||
foreach ($ids as $id) {
|
||||
$file = File::permissionFind($id, 1000);
|
||||
$file = File::permissionFind($id, $user, 1000);
|
||||
//
|
||||
if ($pid > 0) {
|
||||
if ($toShareFile) {
|
||||
@@ -457,7 +488,7 @@ class FileController extends AbstractController
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
User::auth();
|
||||
$user = User::auth();
|
||||
//
|
||||
$ids = Request::input('ids');
|
||||
//
|
||||
@@ -469,9 +500,9 @@ class FileController extends AbstractController
|
||||
}
|
||||
//
|
||||
$files = [];
|
||||
AbstractModel::transaction(function() use ($ids, &$files) {
|
||||
AbstractModel::transaction(function() use ($user, $ids, &$files) {
|
||||
foreach ($ids as $id) {
|
||||
$file = File::permissionFind($id, 1000);
|
||||
$file = File::permissionFind($id, $user, 1000);
|
||||
$file->deleteFile();
|
||||
$files[] = $file;
|
||||
}
|
||||
@@ -512,13 +543,18 @@ class FileController extends AbstractController
|
||||
$history_id = intval(Request::input('history_id'));
|
||||
//
|
||||
if (Base::isNumber($id)) {
|
||||
User::auth();
|
||||
$file = File::permissionFind(intval($id));
|
||||
$user = User::auth();
|
||||
$file = File::permissionFind(intval($id), $user);
|
||||
} elseif ($id) {
|
||||
$fileLink = FileLink::whereCode($id)->first();
|
||||
$file = $fileLink?->file;
|
||||
if (empty($file)) {
|
||||
return Base::retError('链接不存在');
|
||||
$msg = '文件链接不存在';
|
||||
$data = File::code2IdName($id);
|
||||
if ($data) {
|
||||
$msg = "【{$data->name}】 {$msg}";
|
||||
}
|
||||
return Base::retError($msg, $data);
|
||||
}
|
||||
} else {
|
||||
return Base::retError('参数错误');
|
||||
@@ -562,21 +598,21 @@ class FileController extends AbstractController
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$id = Base::getPostInt('id');
|
||||
$content = Base::getPostValue('content');
|
||||
$id = intval(Request::input('id'));
|
||||
$content = Request::input('content');
|
||||
//
|
||||
$file = File::permissionFind($id, 1);
|
||||
$file = File::permissionFind($id, $user, 1);
|
||||
//
|
||||
$text = '';
|
||||
if ($file->type == 'document') {
|
||||
$data = Base::json2array($content);
|
||||
$isRep = false;
|
||||
preg_match_all("/<img\s+src=\"data:image\/(png|jpg|jpeg);base64,(.*?)\"/s", $data['content'], $matchs);
|
||||
preg_match_all("/<img\s+src=\"data:image\/(png|jpg|jpeg|webp);base64,(.*?)\"/s", $data['content'], $matchs);
|
||||
foreach ($matchs[2] as $key => $text) {
|
||||
$tmpPath = "uploads/file/document/" . date("Ym") . "/" . $id . "/attached/";
|
||||
Base::makeDir(public_path($tmpPath));
|
||||
$tmpPath .= md5($text) . "." . $matchs[1][$key];
|
||||
if (file_put_contents(public_path($tmpPath), base64_decode($text))) {
|
||||
if (Base::saveContentImage(public_path($tmpPath), base64_decode($text))) {
|
||||
$paramet = getimagesize(public_path($tmpPath));
|
||||
$data['content'] = str_replace($matchs[0][$key], '<img src="' . Base::fillUrl($tmpPath) . '" original-width="' . $paramet[0] . '" original-height="' . $paramet[1] . '"', $data['content']);
|
||||
$isRep = true;
|
||||
@@ -658,7 +694,7 @@ class FileController extends AbstractController
|
||||
$key = Request::input('key');
|
||||
$url = Request::input('url');
|
||||
//
|
||||
$file = File::permissionFind($id, 1);
|
||||
$file = File::permissionFind($id, $user, 1);
|
||||
//
|
||||
if ($status === 2) {
|
||||
$parse = parse_url($url);
|
||||
@@ -716,7 +752,7 @@ class FileController extends AbstractController
|
||||
if (File::wherePid($pid)->count() >= 300) {
|
||||
return Base::retError('每个文件夹里最多只能创建300个文件或文件夹');
|
||||
}
|
||||
$row = File::permissionFind($pid, 1);
|
||||
$row = File::permissionFind($pid, $user, 1);
|
||||
$userid = $row->userid;
|
||||
} else {
|
||||
if (File::whereUserid($user->userid)->wherePid(0)->count() >= 300) {
|
||||
@@ -775,7 +811,7 @@ class FileController extends AbstractController
|
||||
'xls', 'xlsx' => "excel",
|
||||
'ppt', 'pptx' => "ppt",
|
||||
'wps' => "wps",
|
||||
'jpg', 'jpeg', 'png', 'gif', 'bmp', 'ico', 'raw', 'svg' => "picture",
|
||||
'jpg', 'jpeg', 'webp', 'png', 'gif', 'bmp', 'ico', 'raw', 'svg' => "picture",
|
||||
'rar', 'zip', 'jar', '7-zip', 'tar', 'gzip', '7z', 'gz', 'apk', 'dmg' => "archive",
|
||||
'tif', 'tiff' => "tif",
|
||||
'dwg', 'dxf' => "cad",
|
||||
@@ -862,9 +898,11 @@ class FileController extends AbstractController
|
||||
*/
|
||||
public function content__history()
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$id = Request::input('id');
|
||||
//
|
||||
$file = File::permissionFind(intval($id));
|
||||
$file = File::permissionFind(intval($id), $user);
|
||||
//
|
||||
$data = FileContent::select(['id', 'size', 'userid', 'created_at'])
|
||||
->whereFid($file->id)
|
||||
@@ -895,7 +933,7 @@ class FileController extends AbstractController
|
||||
$id = intval(Request::input('id'));
|
||||
$history_id = intval(Request::input('history_id'));
|
||||
//
|
||||
$file = File::permissionFind($id);
|
||||
$file = File::permissionFind($id, $user);
|
||||
//
|
||||
$history = FileContent::whereFid($file->id)->whereId($history_id)->first();
|
||||
if (empty($history)) {
|
||||
@@ -1059,7 +1097,7 @@ class FileController extends AbstractController
|
||||
//
|
||||
$id = intval(Request::input('id'));
|
||||
//
|
||||
$file = File::permissionFind($id);
|
||||
$file = File::permissionFind($id, $user);
|
||||
//
|
||||
if ($file->userid == $user->userid) {
|
||||
return Base::retError('不能退出自己共享的文件');
|
||||
@@ -1097,12 +1135,9 @@ class FileController extends AbstractController
|
||||
$id = intval(Request::input('id'));
|
||||
$refresh = Request::input('refresh', 'no');
|
||||
//
|
||||
$file = File::permissionFind($id);
|
||||
if ($file->type == 'folder') {
|
||||
return Base::retError('文件夹暂不支持此功能');
|
||||
}
|
||||
$file = File::permissionFind($id, $user);
|
||||
$fileLink = $file->getShareLink($user->userid, $refresh == 'yes');
|
||||
//
|
||||
$data = FileLink::generateLink($file->id, $user->userid, $refresh == 'yes');
|
||||
return Base::retSuccess('success', $data);
|
||||
return Base::retSuccess('success', $fileLink);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Models\AbstractModel;
|
||||
use App\Models\Deleted;
|
||||
use App\Models\File;
|
||||
use App\Models\FileContent;
|
||||
use App\Models\Project;
|
||||
@@ -20,6 +21,9 @@ use App\Models\User;
|
||||
use App\Models\WebSocketDialog;
|
||||
use App\Module\Base;
|
||||
use App\Module\BillExport;
|
||||
use App\Module\BillMultipleExport;
|
||||
use App\Module\Doo;
|
||||
use App\Module\TimeRange;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Arr;
|
||||
use Madzipper;
|
||||
@@ -57,6 +61,9 @@ class ProjectController extends AbstractController
|
||||
* - yes:取列表
|
||||
* @apiParam {Object} [keys] 搜索条件
|
||||
* - keys.name: 项目名称
|
||||
* @apiParam {String} [timerange] 时间范围(如:1678248944,1678248944)
|
||||
* - 第一个时间: 读取在这个时间之后更新的数据
|
||||
* - 第二个时间: 读取在这个时间之后删除的数据ID(第1页附加返回数据: deleted_id)
|
||||
*
|
||||
* @apiParam {Number} [page] 当前页,默认:1
|
||||
* @apiParam {Number} [pagesize] 每页显示数量,默认:50,最大:100
|
||||
@@ -103,6 +110,8 @@ class ProjectController extends AbstractController
|
||||
$type = Request::input('type', 'all');
|
||||
$archived = Request::input('archived', 'no');
|
||||
$getcolumn = Request::input('getcolumn', 'no');
|
||||
$keys = Request::input('keys');
|
||||
$timerange = TimeRange::parse(Request::input('timerange'));
|
||||
//
|
||||
if ($all) {
|
||||
$user->identity('admin');
|
||||
@@ -127,24 +136,29 @@ class ProjectController extends AbstractController
|
||||
$builder->whereNull('projects.archived_at');
|
||||
}
|
||||
//
|
||||
$keys = Request::input('keys');
|
||||
if (is_array($keys) || $timerange->updated) {
|
||||
$totalAll = $builder->clone()->count();
|
||||
}
|
||||
//
|
||||
if (is_array($keys)) {
|
||||
$buildClone = $builder->clone();
|
||||
if ($keys['name']) {
|
||||
$builder->where("projects.name", "like", "%{$keys['name']}%");
|
||||
}
|
||||
}
|
||||
//
|
||||
if ($timerange->updated) {
|
||||
$builder->where('projects.updated_at', '>', $timerange->updated);
|
||||
}
|
||||
//
|
||||
$list = $builder->orderByDesc('projects.id')->paginate(Base::getPaginate(100, 50));
|
||||
$list->transform(function (Project $project) use ($user) {
|
||||
return array_merge($project->toArray(), $project->getTaskStatistics($user->userid));
|
||||
});
|
||||
//
|
||||
$data = $list->toArray();
|
||||
if (isset($buildClone)) {
|
||||
$data['total_all'] = $buildClone->count();
|
||||
} else {
|
||||
$data['total_all'] = $data['total'];
|
||||
$data['total_all'] = $totalAll ?? $data['total'];
|
||||
if ($list->currentPage() === 1) {
|
||||
$data['deleted_id'] = Deleted::ids('project', $user->userid, $timerange->deleted);
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('success', $data);
|
||||
@@ -502,7 +516,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
//
|
||||
AbstractModel::transaction(function() use ($owner_userid, $project) {
|
||||
ProjectUser::whereProjectId($project->id)->update(['owner' => 0]);
|
||||
ProjectUser::whereProjectId($project->id)->change(['owner' => 0]);
|
||||
ProjectUser::updateInsert([
|
||||
'project_id' => $project->id,
|
||||
'userid' => $owner_userid,
|
||||
@@ -563,11 +577,11 @@ class ProjectController extends AbstractController
|
||||
if (!is_array($item['task'])) continue;
|
||||
$index = 0;
|
||||
foreach ($item['task'] as $task_id) {
|
||||
if (ProjectTask::whereId($task_id)->whereProjectId($project->id)->whereCompleteAt(null)->update([
|
||||
if (ProjectTask::whereId($task_id)->whereProjectId($project->id)->whereCompleteAt(null)->change([
|
||||
'column_id' => $item['id'],
|
||||
'sort' => $index
|
||||
])) {
|
||||
ProjectTask::whereParentId($task_id)->whereProjectId($project->id)->update([
|
||||
ProjectTask::whereParentId($task_id)->whereProjectId($project->id)->change([
|
||||
'column_id' => $item['id'],
|
||||
]);
|
||||
}
|
||||
@@ -833,27 +847,28 @@ class ProjectController extends AbstractController
|
||||
* @apiName task__lists
|
||||
*
|
||||
* @apiParam {Object} [keys] 搜索条件
|
||||
* - keys.name: 任务名称
|
||||
* - keys.name: ID、任务名称
|
||||
*
|
||||
* @apiParam {Number} [project_id] 项目ID
|
||||
* @apiParam {Number} [parent_id] 主任务ID(project_id && parent_id ≤ 0 时 仅查询自己参与的任务)
|
||||
* - 大于0:指定主任务下的子任务
|
||||
* - 等于-1:表示仅主任务
|
||||
* @apiParam {String} [name] 任务描述关键词
|
||||
*
|
||||
* @apiParam {Array} [time] 指定时间范围,如:['2020-12-12', '2020-12-30']
|
||||
* @apiParam {String} [time_before] 指定时间之前,如:2020-12-30 00:00:00(填写此项时 time 参数无效)
|
||||
* @apiParam {String} [complete] 完成状态
|
||||
* - all:所有(默认)
|
||||
* - yes:已完成
|
||||
* - no:未完成
|
||||
* @apiParam {String} [timerange] 时间范围(如:1678248944,1678248944)
|
||||
* - 第一个时间: 读取在这个时间之后更新的数据
|
||||
* - 第二个时间: 读取在这个时间之后删除的数据ID(第1页附加返回数据: deleted_id)
|
||||
*
|
||||
* @apiParam {String} [archived] 归档状态
|
||||
* - all:所有
|
||||
* - all:所有(parent_id > 0 时强制 all)
|
||||
* - yes:已归档
|
||||
* - no:未归档(默认)
|
||||
* @apiParam {String} [deleted] 是否读取已删除
|
||||
* - all:所有
|
||||
* - yes:已删除
|
||||
* - no:未删除(默认)
|
||||
* @apiParam {Object} sorts 排序方式
|
||||
*
|
||||
* @apiParam {Object} [sorts] 排序方式
|
||||
* - sorts.complete_at 完成时间:asc|desc
|
||||
* - sorts.archived_at 归档时间:asc|desc
|
||||
* - sorts.end_at 到期时间:asc|desc
|
||||
@@ -864,7 +879,7 @@ class ProjectController extends AbstractController
|
||||
*/
|
||||
public function task__lists()
|
||||
{
|
||||
User::auth();
|
||||
$user = User::auth();
|
||||
//
|
||||
$builder = ProjectTask::with(['taskUser', 'taskTag']);
|
||||
//
|
||||
@@ -872,8 +887,7 @@ class ProjectController extends AbstractController
|
||||
$project_id = intval(Request::input('project_id'));
|
||||
$name = Request::input('name');
|
||||
$time = Request::input('time');
|
||||
$time_before = Request::input('time_before');
|
||||
$complete = Request::input('complete', 'all');
|
||||
$timerange = TimeRange::parse(Request::input('timerange'));
|
||||
$archived = Request::input('archived', 'no');
|
||||
$deleted = Request::input('deleted', 'no');
|
||||
$keys = Request::input('keys');
|
||||
@@ -882,7 +896,11 @@ class ProjectController extends AbstractController
|
||||
$sorts = is_array($sorts) ? $sorts : [];
|
||||
//
|
||||
if ($keys['name']) {
|
||||
$builder->where("project_tasks.name", "like", "%{$keys['name']}%");
|
||||
if (Base::isNumber($keys['name'])) {
|
||||
$builder->where("project_tasks.id", intval($keys['name']));
|
||||
} else {
|
||||
$builder->where("project_tasks.name", "like", "%{$keys['name']}%");
|
||||
}
|
||||
}
|
||||
//
|
||||
$scopeAll = false;
|
||||
@@ -891,6 +909,7 @@ class ProjectController extends AbstractController
|
||||
$isDeleted = str_replace(['all', 'yes', 'no'], [null, false, true], $deleted);
|
||||
ProjectTask::userTask($parent_id, $isArchived, $isDeleted);
|
||||
$scopeAll = true;
|
||||
$archived = 'all';
|
||||
$builder->where('project_tasks.parent_id', $parent_id);
|
||||
} elseif ($parent_id === -1) {
|
||||
$builder->where('project_tasks.parent_id', 0);
|
||||
@@ -912,18 +931,13 @@ class ProjectController extends AbstractController
|
||||
});
|
||||
}
|
||||
//
|
||||
if (Base::isDateOrTime($time_before)) {
|
||||
$builder->whereNotNull('project_tasks.end_at')->where('project_tasks.end_at', '<', Carbon::parse($time_before));
|
||||
} elseif (is_array($time)) {
|
||||
if (is_array($time)) {
|
||||
if (Base::isDateOrTime($time[0]) && Base::isDateOrTime($time[1])) {
|
||||
$builder->betweenTime(Carbon::parse($time[0])->startOfDay(), Carbon::parse($time[1])->endOfDay());
|
||||
}
|
||||
}
|
||||
//
|
||||
if ($complete === 'yes') {
|
||||
$builder->whereNotNull('project_tasks.complete_at');
|
||||
} elseif ($complete === 'no') {
|
||||
$builder->whereNull('project_tasks.complete_at');
|
||||
if ($timerange->updated) {
|
||||
$builder->where('project_tasks.updated_at', '>', $timerange->updated);
|
||||
}
|
||||
//
|
||||
if ($archived == 'yes') {
|
||||
@@ -946,7 +960,12 @@ class ProjectController extends AbstractController
|
||||
//
|
||||
$list = $builder->orderByDesc('project_tasks.id')->paginate(Base::getPaginate(200, 100));
|
||||
//
|
||||
return Base::retSuccess('success', $list);
|
||||
$data = $list->toArray();
|
||||
if ($list->currentPage() === 1) {
|
||||
$data['deleted_id'] = Deleted::ids('projectTask', $user->userid, $timerange->deleted);
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('success', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -977,8 +996,8 @@ class ProjectController extends AbstractController
|
||||
if (empty($userid) || empty($time)) {
|
||||
return Base::retError('参数错误');
|
||||
}
|
||||
if (count($userid) > 20) {
|
||||
return Base::retError('导出成员限制最多20个');
|
||||
if (count($userid) > 100) {
|
||||
return Base::retError('导出成员限制最多100个');
|
||||
}
|
||||
if (!(is_array($time) && Base::isDateOrTime($time[0]) && Base::isDateOrTime($time[1]))) {
|
||||
return Base::retError('时间选择错误');
|
||||
@@ -1067,7 +1086,21 @@ class ProjectController extends AbstractController
|
||||
} elseif ($task->complete_at) {
|
||||
$statusText = '已完成';
|
||||
}
|
||||
$datas[] = [
|
||||
if (!isset($datas[$task->ownerid])) {
|
||||
$datas[$task->ownerid] = [
|
||||
'index' => 1,
|
||||
'nickname' => Base::filterEmoji(User::userid2nickname($task->ownerid)),
|
||||
'styles' => ["A1:P1" => ["font" => ["bold" => true]]],
|
||||
'data' => [],
|
||||
];
|
||||
}
|
||||
$datas[$task->ownerid]['index']++;
|
||||
if ($statusText === '未完成') {
|
||||
$datas[$task->ownerid]['styles']["P{$datas[$task->ownerid]['index']}"] = ["font" => ["color" => ["rgb" => "ff0000"]]]; // 未完成
|
||||
} elseif ($statusText === '已完成' && $task->end_at && Carbon::parse($task->complete_at)->gt($task->end_at)) {
|
||||
$datas[$task->ownerid]['styles']["P{$datas[$task->ownerid]['index']}"] = ["font" => ["color" => ["rgb" => "436FF6"]]]; // 已完成超期
|
||||
}
|
||||
$datas[$task->ownerid]['data'][] = [
|
||||
$task->id,
|
||||
$task->parent_id ?: '-',
|
||||
Base::filterEmoji($task->project?->name) ?: '-',
|
||||
@@ -1087,6 +1120,20 @@ class ProjectController extends AbstractController
|
||||
];
|
||||
}
|
||||
});
|
||||
if (empty($datas)) {
|
||||
return Base::retError('没有任何数据');
|
||||
}
|
||||
//
|
||||
$sheets = [];
|
||||
foreach ($userid as $ownerid) {
|
||||
$data = $datas[$ownerid] ?? [
|
||||
'nickname' => Base::filterEmoji(User::userid2nickname($ownerid)),
|
||||
'styles' => ["A1:P1" => ["font" => ["bold" => true]]],
|
||||
'data' => [],
|
||||
];
|
||||
$title = (count($sheets) + 1) . "." . ($data['nickname'] ?: $ownerid);
|
||||
$sheets[] = BillExport::create()->setTitle($title)->setHeadings($headings)->setData($data['data'])->setStyles($data['styles']);
|
||||
}
|
||||
//
|
||||
$fileName = User::userid2nickname($userid[0]) ?: $userid[0];
|
||||
if (count($userid) > 1) {
|
||||
@@ -1094,7 +1141,8 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
$fileName .= '任务统计_' . Base::time() . '.xls';
|
||||
$filePath = "temp/task/export/" . date("Ym", Base::time());
|
||||
$res = BillExport::create()->setHeadings($headings)->setData($datas)->store($filePath . "/" . $fileName);
|
||||
$export = new BillMultipleExport($sheets);
|
||||
$res = $export->store($filePath . "/" . $fileName);
|
||||
if ($res != 1) {
|
||||
return Base::retError('导出失败,' . $fileName . '!');
|
||||
}
|
||||
@@ -1124,11 +1172,119 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/down 20. 导出任务(限管理员)
|
||||
* @api {get} api/project/task/exportoverdue 20. 导出超期任务(限管理员)
|
||||
*
|
||||
* @apiDescription 导出指定范围任务(已完成、未完成、已归档),返回下载地址,需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup project
|
||||
* @apiName task__exportoverdue
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function task__exportoverdue()
|
||||
{
|
||||
$user = User::auth('admin');
|
||||
//
|
||||
$headings = [];
|
||||
$headings[] = '任务ID';
|
||||
$headings[] = '父级任务ID';
|
||||
$headings[] = '所属项目';
|
||||
$headings[] = '任务标题';
|
||||
$headings[] = '任务开始时间';
|
||||
$headings[] = '任务结束时间';
|
||||
$headings[] = '任务计划用时';
|
||||
$headings[] = '超时时间';
|
||||
$headings[] = '负责人';
|
||||
$headings[] = '创建人';
|
||||
$data = [];
|
||||
//
|
||||
ProjectTask::whereNull('complete_at')
|
||||
->whereNotNull('end_at')
|
||||
->where('end_at', '<=', Carbon::now())
|
||||
->orderBy('end_at')
|
||||
->chunk(100, function ($tasks) use (&$data) {
|
||||
/** @var ProjectTask $task */
|
||||
foreach ($tasks as $task) {
|
||||
$taskStartTime = Carbon::parse($task->start_at ?: $task->created_at)->timestamp;
|
||||
$totalTime = time() - $taskStartTime; //开发测试总用时
|
||||
$planTime = '-';//任务计划用时
|
||||
$overTime = '-';//超时时间
|
||||
if ($task->end_at) {
|
||||
$startTime = Carbon::parse($task->start_at)->timestamp;
|
||||
$endTime = Carbon::parse($task->end_at)->timestamp;
|
||||
$planTotalTime = $endTime - $startTime;
|
||||
$residueTime = $planTotalTime - $totalTime;
|
||||
if ($residueTime < 0) {
|
||||
$overTime = Base::timeFormat(abs($residueTime));
|
||||
}
|
||||
$planTime = Base::timeDiff($startTime, $endTime);
|
||||
}
|
||||
$ownerIds = $task->taskUser->where('owner', 1)->pluck('userid')->toArray();
|
||||
$ownerNames = [];
|
||||
foreach ($ownerIds as $ownerId) {
|
||||
$ownerNames[] = Base::filterEmoji(User::userid2nickname($ownerId)) . " (ID: {$ownerId})";
|
||||
}
|
||||
$data[] = [
|
||||
$task->id,
|
||||
$task->parent_id ?: '-',
|
||||
Base::filterEmoji($task->project?->name) ?: '-',
|
||||
Base::filterEmoji($task->name),
|
||||
$task->start_at ?: '-',
|
||||
$task->end_at ?: '-',
|
||||
$planTime ?: '-',
|
||||
$overTime,
|
||||
implode("、", $ownerNames),
|
||||
Base::filterEmoji(User::userid2nickname($task->userid)) . " (ID: {$task->userid})",
|
||||
];
|
||||
}
|
||||
});
|
||||
if (empty($data)) {
|
||||
return Base::retError('没有任何数据');
|
||||
}
|
||||
//
|
||||
$sheets = [
|
||||
BillExport::create()->setTitle("超期任务")->setHeadings($headings)->setData($data)->setStyles(["A1:J1" => ["font" => ["bold" => true]]])
|
||||
];
|
||||
//
|
||||
$fileName = '超期任务_' . Base::time() . '.xls';
|
||||
$filePath = "temp/task/export/" . date("Ym", Base::time());
|
||||
$export = new BillMultipleExport($sheets);
|
||||
$res = $export->store($filePath . "/" . $fileName);
|
||||
if ($res != 1) {
|
||||
return Base::retError('导出失败,' . $fileName . '!');
|
||||
}
|
||||
$xlsPath = storage_path("app/" . $filePath . "/" . $fileName);
|
||||
$zipFile = "app/" . $filePath . "/" . Base::rightDelete($fileName, '.xls') . ".zip";
|
||||
$zipPath = storage_path($zipFile);
|
||||
if (file_exists($zipPath)) {
|
||||
Base::deleteDirAndFile($zipPath, true);
|
||||
}
|
||||
try {
|
||||
Madzipper::make($zipPath)->add($xlsPath)->close();
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
//
|
||||
if (file_exists($zipPath)) {
|
||||
$base64 = base64_encode(Base::array2string([
|
||||
'file' => $zipFile,
|
||||
]));
|
||||
Session::put('task::export:userid', $user->userid);
|
||||
return Base::retSuccess('success', [
|
||||
'size' => Base::twoFloat(filesize($zipPath) / 1024, true),
|
||||
'url' => Base::fillUrl('api/project/task/down?key=' . urlencode($base64)),
|
||||
]);
|
||||
} else {
|
||||
return Base::retError('打包失败,请稍后再试...');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/down 21. 下载导出的任务
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup project
|
||||
* @apiName task__down
|
||||
*
|
||||
* @apiParam {String} key 通过export接口得到的下载钥匙
|
||||
@@ -1147,11 +1303,11 @@ class ProjectController extends AbstractController
|
||||
if (empty($file) || !file_exists(storage_path($file))) {
|
||||
return Base::ajaxError("文件不存在!", [], 0, 502);
|
||||
}
|
||||
return response()->download(storage_path($file));
|
||||
return Response::download(storage_path($file));
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/one 21. 获取单个任务信息
|
||||
* @api {get} api/project/task/one 22. 获取单个任务信息
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1185,7 +1341,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/content 22. 获取任务详细描述
|
||||
* @api {get} api/project/task/content 23. 获取任务详细描述
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1213,7 +1369,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/files 23. 获取任务文件列表
|
||||
* @api {get} api/project/task/files 24. 获取任务文件列表
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1238,7 +1394,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/filedelete 24. 删除任务文件
|
||||
* @api {get} api/project/task/filedelete 25. 删除任务文件
|
||||
*
|
||||
* @apiDescription 需要token身份(限:项目、任务负责人)
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1271,7 +1427,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/filedetail 25. 获取任务文件详情
|
||||
* @api {get} api/project/task/filedetail 26. 获取任务文件详情
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1315,7 +1471,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/filedown 26. 下载任务文件
|
||||
* @api {get} api/project/task/filedown 27. 下载任务文件
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1357,11 +1513,14 @@ class ProjectController extends AbstractController
|
||||
]));
|
||||
}
|
||||
//
|
||||
return Response::download(public_path($file->getRawOriginal('path')), $file->name);
|
||||
$filePath = public_path($file->getRawOriginal('path'));
|
||||
return Base::streamDownload(function() use ($filePath) {
|
||||
echo file_get_contents($filePath);
|
||||
}, $file->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/project/task/add 27. 添加任务
|
||||
* @api {post} api/project/task/add 28. 添加任务
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1384,7 +1543,8 @@ class ProjectController extends AbstractController
|
||||
public function task__add()
|
||||
{
|
||||
User::auth();
|
||||
parse_str(Request::getContent(), $data);
|
||||
//
|
||||
$data = Request::input();
|
||||
$project_id = intval($data['project_id']);
|
||||
$column_id = $data['column_id'];
|
||||
// 项目
|
||||
@@ -1428,11 +1588,12 @@ class ProjectController extends AbstractController
|
||||
$data['new_column'] = $newColumn;
|
||||
}
|
||||
$task->pushMsg('add', $data);
|
||||
$task->taskPush(null, 0);
|
||||
return Base::retSuccess('添加成功', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/addsub 28. 添加子任务
|
||||
* @api {get} api/project/task/addsub 29. 添加子任务
|
||||
*
|
||||
* @apiDescription 需要token身份(限:项目、任务负责人)
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1472,7 +1633,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/project/task/update 29. 修改任务、子任务
|
||||
* @api {post} api/project/task/update 30. 修改任务、子任务
|
||||
*
|
||||
* @apiDescription 需要token身份(限:项目、任务负责人)
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1503,7 +1664,7 @@ class ProjectController extends AbstractController
|
||||
{
|
||||
User::auth();
|
||||
//
|
||||
parse_str(Request::getContent(), $data);
|
||||
$data = Request::input();
|
||||
$task_id = intval($data['task_id']);
|
||||
//
|
||||
$task = ProjectTask::userTask($task_id, true, true, 2);
|
||||
@@ -1519,7 +1680,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/dialog 30. 创建/获取聊天室
|
||||
* @api {get} api/project/task/dialog 31. 创建/获取聊天室
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1568,7 +1729,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/archived 31. 归档任务
|
||||
* @api {get} api/project/task/archived 32. 归档任务
|
||||
*
|
||||
* @apiDescription 需要token身份(限:项目、任务负责人)
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1610,7 +1771,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/remove 32. 删除任务
|
||||
* @api {get} api/project/task/remove 33. 删除任务
|
||||
*
|
||||
* @apiDescription 需要token身份(限:项目、任务负责人)
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1635,7 +1796,7 @@ class ProjectController extends AbstractController
|
||||
//
|
||||
$task = ProjectTask::userTask($task_id, null, $type !== 'recovery', true);
|
||||
if ($type == 'recovery') {
|
||||
$task->recoveryTask();
|
||||
$task->restoreTask();
|
||||
return Base::retSuccess('操作成功', ['id' => $task->id]);
|
||||
} else {
|
||||
$task->deleteTask();
|
||||
@@ -1644,7 +1805,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/resetfromlog 33. 根据日志重置任务
|
||||
* @api {get} api/project/task/resetfromlog 34. 根据日志重置任务
|
||||
*
|
||||
* @apiDescription 需要token身份(限:项目、任务负责人)
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1703,7 +1864,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/flow 34. 任务工作流信息
|
||||
* @api {get} api/project/task/flow 35. 任务工作流信息
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1785,7 +1946,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/flow/list 35. 工作流列表
|
||||
* @api {get} api/project/flow/list 36. 工作流列表
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1811,7 +1972,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/project/flow/save 36. 保存工作流
|
||||
* @api {post} api/project/flow/save 37. 保存工作流
|
||||
*
|
||||
* @apiDescription 需要token身份(限:项目负责人)
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1829,8 +1990,8 @@ class ProjectController extends AbstractController
|
||||
{
|
||||
User::auth();
|
||||
//
|
||||
$project_id = intval(Base::getContentValue('project_id'));
|
||||
$flows = Base::getContentValue('flows');
|
||||
$project_id = intval(Request::input('project_id'));
|
||||
$flows = Request::input('flows');
|
||||
//
|
||||
if (!is_array($flows)) {
|
||||
return Base::retError('参数错误');
|
||||
@@ -1845,7 +2006,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/flow/delete 37. 删除工作流
|
||||
* @api {get} api/project/flow/delete 38. 删除工作流
|
||||
*
|
||||
* @apiDescription 需要token身份(限:项目负责人)
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1877,7 +2038,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/log/lists 38. 获取项目、任务日志
|
||||
* @api {get} api/project/log/lists 39. 获取项目、任务日志
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1916,11 +2077,12 @@ class ProjectController extends AbstractController
|
||||
if ($task_id === 0) {
|
||||
$log->projectTask?->cancelAppend();
|
||||
}
|
||||
$log->detail = Doo::translate($log->detail);
|
||||
$log->time = [
|
||||
'ymd' => date(date("Y", $timestamp) == date("Y", Base::time()) ? "m-d" : "Y-m-d", $timestamp),
|
||||
'hi' => date("h:i", $timestamp) ,
|
||||
'week' => "周" . Base::getTimeWeek($timestamp),
|
||||
'segment' => Base::getTimeDayeSegment($timestamp),
|
||||
'week' => Doo::translate("周" . Base::getTimeWeek($timestamp)),
|
||||
'segment' => Doo::translate(Base::getTimeDayeSegment($timestamp)),
|
||||
];
|
||||
return $log;
|
||||
});
|
||||
@@ -1929,7 +2091,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/top 39. 项目置顶
|
||||
* @api {get} api/project/top 40. 项目置顶
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
|
||||
179
app/Http/Controllers/Api/PublicController.php
Executable file
179
app/Http/Controllers/Api/PublicController.php
Executable file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\UserCheckinMac;
|
||||
use App\Models\UserCheckinRecord;
|
||||
use App\Models\WebSocketDialog;
|
||||
use App\Models\WebSocketDialogMsg;
|
||||
use App\Module\Base;
|
||||
use Cache;
|
||||
use Carbon\Carbon;
|
||||
use Request;
|
||||
|
||||
/**
|
||||
* @apiDefine public
|
||||
*
|
||||
* 公开
|
||||
*/
|
||||
class PublicController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* 签到 - 路由器(openwrt)功能安装脚本
|
||||
*
|
||||
* @apiParam {String} key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function checkin__install()
|
||||
{
|
||||
$key = trim(Request::input('key'));
|
||||
//
|
||||
$setting = Base::setting('checkinSetting');
|
||||
if ($setting['open'] !== 'open') {
|
||||
return <<<EOF
|
||||
#!/bin/sh
|
||||
echo "function off"
|
||||
EOF;
|
||||
}
|
||||
if ($key != $setting['key']) {
|
||||
return <<<EOF
|
||||
#!/bin/sh
|
||||
echo "key error"
|
||||
EOF;
|
||||
}
|
||||
//
|
||||
$reportUrl = Base::fillUrl("api/public/checkin/report");
|
||||
return <<<EOE
|
||||
#!/bin/sh
|
||||
echo 'installing...'
|
||||
|
||||
cat > /etc/init.d/dootask-checkin-report <<EOF
|
||||
#!/bin/sh
|
||||
mac=\\\$(awk 'NR!=1&&\\\$3=="0x2" {print \\\$4}' /proc/net/arp | tr "\\n" ",")
|
||||
tmp='{"key":"{$setting['key']}","mac":"'\\\${mac}'","time":"'\\\$(date +%s)'"}'
|
||||
curl -4 -X POST "{$reportUrl}" -H "Content-Type: application/json" -d \\\${tmp}
|
||||
EOF
|
||||
|
||||
chmod +x /etc/init.d/dootask-checkin-report
|
||||
crontab -l >/tmp/cronbak
|
||||
sed -i '/\/etc\/init.d\/dootask-checkin-report/d' /tmp/cronbak
|
||||
sed -i '/^$/d' /tmp/cronbak
|
||||
echo "* * * * * sh /etc/init.d/dootask-checkin-report" >>/tmp/cronbak
|
||||
crontab /tmp/cronbak
|
||||
rm -f /tmp/cronbak
|
||||
/etc/init.d/cron enable
|
||||
/etc/init.d/cron restart
|
||||
|
||||
echo 'installed'
|
||||
EOE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {post} 签到 - 路由器(openwrt)上报
|
||||
*
|
||||
* @apiParam {String} key
|
||||
* @apiParam {String} mac 使用逗号分割多个
|
||||
* @apiParam {String} time
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function checkin__report()
|
||||
{
|
||||
$key = trim(Request::input('key'));
|
||||
$mac = trim(Request::input('mac'));
|
||||
$time = intval(Request::input('time'));
|
||||
//
|
||||
$setting = Base::setting('checkinSetting');
|
||||
if ($setting['open'] !== 'open') {
|
||||
return 'function off';
|
||||
}
|
||||
if ($key != $setting['key']) {
|
||||
return 'key error';
|
||||
}
|
||||
$times = $setting['time'] ? Base::json2array($setting['time']) : ['09:00', '18:00'];
|
||||
$advance = (intval($setting['advance']) ?: 120) * 60;
|
||||
$delay = (intval($setting['delay']) ?: 120) * 60;
|
||||
//
|
||||
$nowDate = date("Y-m-d");
|
||||
$nowTime = date("H:i:s");
|
||||
//
|
||||
$timeStart = strtotime("{$nowDate} {$times[0]}");
|
||||
$timeEnd = strtotime("{$nowDate} {$times[1]}");
|
||||
$timeAdvance = max($timeStart - $advance, strtotime($nowDate));
|
||||
$timeDelay = min($timeEnd + $delay, strtotime("{$nowDate} 23:59:59"));
|
||||
if (Base::time() < $timeAdvance || $timeDelay < Base::time()) {
|
||||
return "not in valid time, valid time is " . date("H:i", $timeAdvance) . "-" . date("H:i", $timeDelay);
|
||||
}
|
||||
//
|
||||
$macs = explode(",", $mac);
|
||||
$checkins = [];
|
||||
foreach ($macs as $mac) {
|
||||
$mac = strtoupper($mac);
|
||||
if (Base::isMac($mac) && $UserCheckinMac = UserCheckinMac::whereMac($mac)->first()) {
|
||||
$checkins[] = $UserCheckinMac;
|
||||
$array = [
|
||||
'userid' => $UserCheckinMac->userid,
|
||||
'mac' => $UserCheckinMac->mac,
|
||||
'date' => $nowDate,
|
||||
];
|
||||
$record = UserCheckinRecord::where($array)->first();
|
||||
if (empty($record)) {
|
||||
$record = UserCheckinRecord::createInstance($array);
|
||||
}
|
||||
$record->times = Base::array2json(array_merge($record->times, [$nowTime]));
|
||||
$record->report_time = $time;
|
||||
$record->save();
|
||||
}
|
||||
}
|
||||
//
|
||||
if ($checkins && $botUser = User::botGetOrCreate('check-in')) {
|
||||
$getJokeSoup = function($type) {
|
||||
$pre = $type == "up" ? "每日开心:" : "心灵鸡汤:";
|
||||
$key = $type == "up" ? "JokeSoupTask:jokes" : "JokeSoupTask:soups";
|
||||
$array = Base::json2array(Cache::get($key));
|
||||
if ($array) {
|
||||
$item = $array[array_rand($array)];
|
||||
if ($item) {
|
||||
return $pre . $item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
$sendMsg = function($type, UserCheckinMac $checkin) use ($getJokeSoup, $botUser, $nowDate) {
|
||||
$cacheKey = "Checkin::sendMsg-{$nowDate}-{$type}:" . $checkin->userid;
|
||||
if (Cache::get($cacheKey) === "yes") {
|
||||
return;
|
||||
}
|
||||
Cache::put($cacheKey, "yes", Carbon::now()->addDay());
|
||||
//
|
||||
$dialog = WebSocketDialog::checkUserDialog($botUser, $checkin->userid);
|
||||
if ($dialog) {
|
||||
$hi = date("H:i");
|
||||
$pre = $type == "up" ? "上班" : "下班";
|
||||
$remark = $checkin->remark ? " ({$checkin->remark})": "";
|
||||
$text = "<p>{$pre}打卡成功,打卡时间: {$hi}{$remark}</p>";
|
||||
$suff = $getJokeSoup($type);
|
||||
if ($suff) {
|
||||
$text = "{$text}<p>----------</p><p>{$suff}</p>";
|
||||
}
|
||||
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid);
|
||||
}
|
||||
};
|
||||
if ($timeAdvance <= Base::time() && Base::time() < $timeEnd) {
|
||||
// 上班打卡通知(从最早打卡时间 到 下班打卡时间)
|
||||
foreach ($checkins as $checkin) {
|
||||
$sendMsg('up', $checkin);
|
||||
}
|
||||
}
|
||||
if ($timeEnd <= Base::time() && Base::time() <= $timeDelay) {
|
||||
// 下班打卡通知(下班打卡时间 到 最晚打卡时间)
|
||||
foreach ($checkins as $checkin) {
|
||||
$sendMsg('down', $checkin);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 'success';
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ use App\Models\Report;
|
||||
use App\Models\ReportReceive;
|
||||
use App\Models\User;
|
||||
use App\Module\Base;
|
||||
use App\Module\Doo;
|
||||
use App\Tasks\PushTask;
|
||||
use Carbon\Carbon;
|
||||
use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||
@@ -119,12 +120,13 @@ class ReportController extends AbstractController
|
||||
* @apiGroup report
|
||||
* @apiName store
|
||||
*
|
||||
* @apiParam {Number} [id] 汇报ID
|
||||
* @apiParam {String} [title] 汇报标题
|
||||
* @apiParam {Array} [type] 汇报类型,weekly:周报,daily:日报
|
||||
* @apiParam {Number} [content] 内容
|
||||
* @apiParam {Number} [receive] 汇报对象
|
||||
* @apiParam {Number} [offset] 偏移量
|
||||
* @apiParam {Number} id 汇报ID,0为新建
|
||||
* @apiParam {String} [sign] 唯一签名,通过[api/report/template]接口返回
|
||||
* @apiParam {String} title 汇报标题
|
||||
* @apiParam {Array} type 汇报类型,weekly:周报,daily:日报
|
||||
* @apiParam {Number} content 内容
|
||||
* @apiParam {Number} [receive] 汇报对象
|
||||
* @apiParam {Number} offset 时间偏移量
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
@@ -132,21 +134,23 @@ class ReportController extends AbstractController
|
||||
*/
|
||||
public function store(): array
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$input = [
|
||||
"id" => Base::getPostValue("id", 0),
|
||||
"title" => Base::getPostValue("title"),
|
||||
"type" => Base::getPostValue("type"),
|
||||
"content" => Base::getPostValue("content"),
|
||||
"receive" => Base::getPostValue("receive"),
|
||||
"id" => Request::input("id", 0),
|
||||
"sign" => Request::input("sign"),
|
||||
"title" => Request::input("title"),
|
||||
"type" => Request::input("type"),
|
||||
"content" => Request::input("content"),
|
||||
"receive" => Request::input("receive"),
|
||||
// 以当前日期为基础的周期偏移量。例如选择了上一周那么就是 -1,上一天同理。
|
||||
"offset" => Base::getPostValue("offset", 0),
|
||||
"offset" => Request::input("offset", 0),
|
||||
];
|
||||
$validator = Validator::make($input, [
|
||||
'id' => 'numeric',
|
||||
'title' => 'required',
|
||||
'type' => ['required', Rule::in([Report::WEEKLY, Report::DAILY])],
|
||||
'content' => 'required',
|
||||
'receive' => 'required',
|
||||
'offset' => ['numeric', 'max:0'],
|
||||
], [
|
||||
'id.numeric' => 'ID只能是数字',
|
||||
@@ -154,14 +158,12 @@ class ReportController extends AbstractController
|
||||
'type.required' => '请选择汇报类型',
|
||||
'type.in' => '汇报类型错误',
|
||||
'content.required' => '请填写汇报内容',
|
||||
'receive.required' => '请选择接收人',
|
||||
'offset.numeric' => '工作汇报周期格式错误,只能是数字',
|
||||
'offset.max' => '只能提交当天/本周或者之前的的工作汇报',
|
||||
]);
|
||||
if ($validator->fails())
|
||||
return Base::retError($validator->errors()->first());
|
||||
|
||||
$user = User::auth();
|
||||
// 接收人
|
||||
if (is_array($input["receive"])) {
|
||||
// 删除当前登录人
|
||||
@@ -193,25 +195,24 @@ class ReportController extends AbstractController
|
||||
]);
|
||||
} else {
|
||||
// 生成唯一标识
|
||||
$sign = Report::generateSign($input["type"], $input["offset"]);
|
||||
$sign = Base::isNumber($input["sign"]) ? $input["sign"] : Report::generateSign($input["type"], $input["offset"]);
|
||||
// 检查唯一标识是否存在
|
||||
if (empty($input["id"])) {
|
||||
if (Report::query()->whereSign($sign)->whereType($input["type"])->count() > 0)
|
||||
throw new ApiException("请勿重复提交工作汇报");
|
||||
if (empty($input["id"]) && Report::query()->whereSign($sign)->whereType($input["type"])->count() > 0) {
|
||||
throw new ApiException("请勿重复提交工作汇报");
|
||||
}
|
||||
$report = Report::createInstance([
|
||||
"sign" => $sign,
|
||||
"title" => $input["title"],
|
||||
"type" => $input["type"],
|
||||
"content" => htmlspecialchars($input["content"]),
|
||||
"userid" => $user->userid,
|
||||
"sign" => $sign,
|
||||
"content" => htmlspecialchars($input["content"]),
|
||||
]);
|
||||
}
|
||||
|
||||
$report->save();
|
||||
if (!empty($input["receive_content"])) {
|
||||
// 删除关联
|
||||
$report->Receives()->delete();
|
||||
|
||||
// 删除关联
|
||||
$report->Receives()->delete();
|
||||
if ($input["receive_content"]) {
|
||||
// 保存接收人
|
||||
$report->Receives()->createMany($input["receive_content"]);
|
||||
}
|
||||
@@ -286,9 +287,10 @@ class ReportController extends AbstractController
|
||||
// 如果已经提交了相关汇报
|
||||
if ($one && $id > 0) {
|
||||
return Base::retSuccess('success', [
|
||||
"content" => $one->content,
|
||||
"title" => $one->title,
|
||||
"id" => $one->id,
|
||||
"sign" => $one->sign,
|
||||
"title" => $one->title,
|
||||
"content" => $one->content,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -305,8 +307,8 @@ class ReportController extends AbstractController
|
||||
if ($complete_task->isNotEmpty()) {
|
||||
foreach ($complete_task as $task) {
|
||||
$complete_at = Carbon::parse($task->complete_at);
|
||||
$pre = $type == Report::WEEKLY ? ('<span>[' . Base::Lang('周' . ['日', '一', '二', '三', '四', '五', '六'][$complete_at->dayOfWeek]) . ']</span> ') : '';
|
||||
$completeContent .= '<li>' . $pre . $task->name . '</li>';
|
||||
$pre = $type == Report::WEEKLY ? ('<span>[' . Doo::translate('周' . ['日', '一', '二', '三', '四', '五', '六'][$complete_at->dayOfWeek]) . ']</span> ') : '';
|
||||
$completeContent .= "<li>{$pre}[{$task->project->name}] {$task->name}</li>";
|
||||
}
|
||||
} else {
|
||||
$completeContent = '<li> </li>';
|
||||
@@ -326,8 +328,8 @@ class ReportController extends AbstractController
|
||||
if ($unfinished_task->isNotEmpty()) {
|
||||
foreach ($unfinished_task as $task) {
|
||||
empty($task->end_at) || $end_at = Carbon::parse($task->end_at);
|
||||
$pre = (!empty($end_at) && $end_at->lt($now_dt)) ? '<span style="color:#ff0000;">[' . Base::Lang('超期') . ']</span> ' : '';
|
||||
$unfinishedContent .= '<li>' . $pre . $task->name . '</li>';
|
||||
$pre = (!empty($end_at) && $end_at->lt($now_dt)) ? '<span style="color:#ff0000;">[' . Doo::translate('超期') . ']</span> ' : '';
|
||||
$unfinishedContent .= "<li>{$pre}[{$task->project->name}] {$task->name}</li>";
|
||||
}
|
||||
} else {
|
||||
$unfinishedContent = '<li> </li>';
|
||||
@@ -339,15 +341,21 @@ class ReportController extends AbstractController
|
||||
} else {
|
||||
$title = $user->nickname . "的日报[" . $start_time->format("Y/m/d") . "]";
|
||||
}
|
||||
// 生成内容
|
||||
$content = '<h2>' . Doo::translate('已完成工作') . '</h2><ol>' .
|
||||
$completeContent . '</ol><h2>' .
|
||||
Doo::translate('未完成的工作') . '</h2><ol>' .
|
||||
$unfinishedContent . '</ol>';
|
||||
if ($type === Report::WEEKLY) {
|
||||
$content .= "<h2>" . Doo::translate("下周拟定计划") . "[" . $start_time->addWeek()->format("m/d") . "-" . $end_time->addWeek()->format("m/d") . "]</h2><ol><li> </li></ol>";
|
||||
}
|
||||
$data = [
|
||||
"time" => $start_time->toDateTimeString(),
|
||||
"sign" => $sign,
|
||||
"title" => $title,
|
||||
"content" => $content,
|
||||
"complete_task" => $complete_task,
|
||||
"unfinished_task" => $unfinished_task,
|
||||
"content" => '<h2>' . Base::Lang('已完成工作') . '</h2><ol>' .
|
||||
$completeContent . '</ol><h2>' .
|
||||
Base::Lang('未完成的工作') . '</h2><ol>' .
|
||||
$unfinishedContent . '</ol>',
|
||||
"title" => $title,
|
||||
];
|
||||
if ($one) {
|
||||
$data['id'] = $one->id;
|
||||
@@ -458,17 +466,14 @@ class ReportController extends AbstractController
|
||||
* @apiGroup report
|
||||
* @apiName unread
|
||||
*
|
||||
* @apiParam {Number} [userid] 用户id
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function unread(): array
|
||||
{
|
||||
$userid = intval(trim(Request::input("userid")));
|
||||
$user = empty($userid) ? User::auth() : User::find($userid);
|
||||
|
||||
$user = User::auth();
|
||||
//
|
||||
$data = Report::whereHas("Receives", function (Builder $query) use ($user) {
|
||||
$query->where("userid", $user->userid)->where("read", 0);
|
||||
})->orderByDesc('created_at')->paginate(Base::getPaginate(50, 20));
|
||||
|
||||
@@ -4,11 +4,21 @@ namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Models\UserCheckinRecord;
|
||||
use App\Module\Base;
|
||||
use Arr;
|
||||
use App\Module\BillExport;
|
||||
use App\Module\BillMultipleExport;
|
||||
use App\Module\Doo;
|
||||
use App\Module\Extranet;
|
||||
use Carbon\Carbon;
|
||||
use Guanguans\Notify\Factory;
|
||||
use Guanguans\Notify\Messages\EmailMessage;
|
||||
use LdapRecord\Container;
|
||||
use LdapRecord\LdapRecordException;
|
||||
use Madzipper;
|
||||
use Request;
|
||||
use Response;
|
||||
use Session;
|
||||
|
||||
/**
|
||||
* @apiDefine system
|
||||
@@ -28,7 +38,7 @@ class SystemController extends AbstractController
|
||||
* @apiParam {String} type
|
||||
* - get: 获取(默认)
|
||||
* - all: 获取所有(需要管理员权限)
|
||||
* - save: 保存设置(参数:['reg', 'reg_invite', 'login_code', 'password_policy', 'project_invite', 'chat_information', 'auto_archived', 'archived_day', 'all_group_mute', 'all_group_autoin', 'start_home', 'home_footer'])
|
||||
* - save: 保存设置(参数:['reg', 'reg_identity', 'reg_invite', 'login_code', 'password_policy', 'project_invite', 'chat_information', 'anon_message', 'auto_archived', 'archived_day', 'all_group_mute', 'all_group_autoin', 'image_compress', 'image_save_local', 'start_home', 'home_footer'])
|
||||
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
@@ -46,15 +56,19 @@ class SystemController extends AbstractController
|
||||
foreach ($all AS $key => $value) {
|
||||
if (!in_array($key, [
|
||||
'reg',
|
||||
'reg_identity',
|
||||
'reg_invite',
|
||||
'login_code',
|
||||
'password_policy',
|
||||
'project_invite',
|
||||
'chat_information',
|
||||
'anon_message',
|
||||
'auto_archived',
|
||||
'archived_day',
|
||||
'all_group_mute',
|
||||
'all_group_autoin',
|
||||
'image_compress',
|
||||
'image_save_local',
|
||||
'start_home',
|
||||
'home_footer'
|
||||
])) {
|
||||
@@ -82,14 +96,18 @@ class SystemController extends AbstractController
|
||||
}
|
||||
//
|
||||
$setting['reg'] = $setting['reg'] ?: 'open';
|
||||
$setting['reg_identity'] = $setting['reg_identity'] ?: 'normal';
|
||||
$setting['login_code'] = $setting['login_code'] ?: 'auto';
|
||||
$setting['password_policy'] = $setting['password_policy'] ?: 'simple';
|
||||
$setting['project_invite'] = $setting['project_invite'] ?: 'open';
|
||||
$setting['chat_information'] = $setting['chat_information'] ?: 'optional';
|
||||
$setting['anon_message'] = $setting['anon_message'] ?: 'open';
|
||||
$setting['auto_archived'] = $setting['auto_archived'] ?: 'close';
|
||||
$setting['archived_day'] = floatval($setting['archived_day']) ?: 7;
|
||||
$setting['all_group_mute'] = $setting['all_group_mute'] ?: 'open';
|
||||
$setting['all_group_autoin'] = $setting['all_group_autoin'] ?: 'yes';
|
||||
$setting['image_compress'] = $setting['image_compress'] ?: 'open';
|
||||
$setting['image_save_local'] = $setting['image_save_local'] ?: 'open';
|
||||
$setting['start_home'] = $setting['start_home'] ?: 'close';
|
||||
//
|
||||
return Base::retSuccess('success', $setting ?: json_decode('{}'));
|
||||
@@ -104,7 +122,7 @@ class SystemController extends AbstractController
|
||||
*
|
||||
* @apiParam {String} type
|
||||
* - get: 获取(默认)
|
||||
* - save: 保存设置(参数:['smtp_server', 'port', 'account', 'password', 'reg_verify', 'notice', 'task_start_minute', 'task_remind_hours', 'task_remind_hours2', 'notice_msg', 'msg_unread_user_minute', 'msg_unread_group_minute', 'ignore_addr'])
|
||||
* - save: 保存设置(参数:['smtp_server', 'port', 'account', 'password', 'reg_verify', 'notice_msg', 'msg_unread_user_minute', 'msg_unread_group_minute', 'ignore_addr'])
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
@@ -127,10 +145,6 @@ class SystemController extends AbstractController
|
||||
'account',
|
||||
'password',
|
||||
'reg_verify',
|
||||
'notice',
|
||||
'task_start_minute',
|
||||
'task_remind_hours',
|
||||
'task_remind_hours2',
|
||||
'notice_msg',
|
||||
'msg_unread_user_minute',
|
||||
'msg_unread_group_minute',
|
||||
@@ -149,10 +163,6 @@ class SystemController extends AbstractController
|
||||
$setting['account'] = $setting['account'] ?: '';
|
||||
$setting['password'] = $setting['password'] ?: '';
|
||||
$setting['reg_verify'] = $setting['reg_verify'] ?: 'close';
|
||||
$setting['notice'] = $setting['notice'] ?: 'close';
|
||||
$setting['task_start_minute'] = intval($setting['task_start_minute'] ?? -1);
|
||||
$setting['task_remind_hours'] = floatval($setting['task_remind_hours'] ?? -1);
|
||||
$setting['task_remind_hours2'] = floatval($setting['task_remind_hours2'] ?? -1);
|
||||
$setting['notice_msg'] = $setting['notice_msg'] ?: 'close';
|
||||
$setting['msg_unread_user_minute'] = intval($setting['msg_unread_user_minute'] ?? -1);
|
||||
$setting['msg_unread_group_minute'] = intval($setting['msg_unread_group_minute'] ?? -1);
|
||||
@@ -198,18 +208,88 @@ class SystemController extends AbstractController
|
||||
unset($all[$key]);
|
||||
}
|
||||
}
|
||||
if ($all['open'] === 'open' && (!$all['appid'] || !$all['app_certificate'])) {
|
||||
return Base::retError('请填写完整的参数');
|
||||
}
|
||||
$setting = Base::setting('meetingSetting', Base::newTrim($all));
|
||||
} else {
|
||||
$setting = Base::setting('meetingSetting');
|
||||
}
|
||||
//
|
||||
$setting['open'] = $setting['open'] ?: 'close';
|
||||
if (env("SYSTEM_SETTING") == 'disabled') {
|
||||
$setting['appid'] = substr($setting['appid'], 0, 4) . str_repeat('*', strlen($setting['appid']) - 8) . substr($setting['appid'], -4);
|
||||
$setting['app_certificate'] = substr($setting['app_certificate'], 0, 4) . str_repeat('*', strlen($setting['app_certificate']) - 8) . substr($setting['app_certificate'], -4);
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('success', $setting ?: json_decode('{}'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/setting/apppush 04. 获取APP推送设置、保存APP推送设置(限管理员)
|
||||
* @api {get} api/system/setting/checkin 04. 获取签到设置、保存签到设置(限管理员)
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
* @apiName setting__checkin
|
||||
*
|
||||
* @apiParam {String} type
|
||||
* - get: 获取(默认)
|
||||
* - save: 保存设置(参数:['open', 'time', 'advance', 'delay', 'remindin', 'remindexceed', 'edit', 'key'])
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function setting__checkin()
|
||||
{
|
||||
User::auth('admin');
|
||||
//
|
||||
$type = trim(Request::input('type'));
|
||||
if ($type == 'save') {
|
||||
if (env("SYSTEM_SETTING") == 'disabled') {
|
||||
return Base::retError('当前环境禁止修改');
|
||||
}
|
||||
$all = Request::input();
|
||||
foreach ($all as $key => $value) {
|
||||
if (!in_array($key, [
|
||||
'open',
|
||||
'time',
|
||||
'advance',
|
||||
'delay',
|
||||
'remindin',
|
||||
'remindexceed',
|
||||
'edit',
|
||||
'key',
|
||||
])) {
|
||||
unset($all[$key]);
|
||||
}
|
||||
}
|
||||
if ($all['open'] === 'close') {
|
||||
$all['key'] = md5(Base::generatePassword(32));
|
||||
}
|
||||
$setting = Base::setting('checkinSetting', Base::newTrim($all));
|
||||
} else {
|
||||
$setting = Base::setting('checkinSetting');
|
||||
}
|
||||
//
|
||||
if (empty($setting['key'])) {
|
||||
$setting['key'] = md5(Base::generatePassword(32));
|
||||
Base::setting('checkinSetting', $setting);
|
||||
}
|
||||
//
|
||||
$setting['open'] = $setting['open'] ?: 'close';
|
||||
$setting['time'] = $setting['time'] ? Base::json2array($setting['time']) : ['09:00', '18:00'];
|
||||
$setting['advance'] = intval($setting['advance']) ?: 120;
|
||||
$setting['delay'] = intval($setting['delay']) ?: 120;
|
||||
$setting['remindin'] = intval($setting['remindin']) ?: 5;
|
||||
$setting['remindexceed'] = intval($setting['remindexceed']) ?: 10;
|
||||
$setting['edit'] = $setting['edit'] ?: 'close';
|
||||
$setting['cmd'] = "curl -sSL '" . Base::fillUrl("api/public/checkin/install?key={$setting['key']}") . "' | sh";
|
||||
//
|
||||
return Base::retSuccess('success', $setting ?: json_decode('{}'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/setting/apppush 05. 获取APP推送设置、保存APP推送设置(限管理员)
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@@ -217,7 +297,7 @@ class SystemController extends AbstractController
|
||||
*
|
||||
* @apiParam {String} type
|
||||
* - get: 获取(默认)
|
||||
* - save: 保存设置(参数:['push', 'ios_key', 'ios_secret', 'android_key', 'android_secret', 'push_msg', 'push_task', 'task_start_minute', 'task_remind_hours', 'task_remind_hours2'])
|
||||
* - save: 保存设置(参数:['push', 'ios_key', 'ios_secret', 'android_key', 'android_secret'])
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
@@ -239,11 +319,6 @@ class SystemController extends AbstractController
|
||||
'ios_secret',
|
||||
'android_key',
|
||||
'android_secret',
|
||||
'push_msg',
|
||||
'push_task',
|
||||
'task_start_minute',
|
||||
'task_remind_hours',
|
||||
'task_remind_hours2'
|
||||
])) {
|
||||
unset($all[$key]);
|
||||
}
|
||||
@@ -254,17 +329,82 @@ class SystemController extends AbstractController
|
||||
}
|
||||
//
|
||||
$setting['push'] = $setting['push'] ?: 'close';
|
||||
$setting['push_msg'] = $setting['push_msg'] ?: 'open';
|
||||
$setting['push_task'] = $setting['push_task'] ?: 'open';
|
||||
$setting['task_start_minute'] = intval($setting['task_start_minute']);
|
||||
$setting['task_remind_hours'] = floatval($setting['task_remind_hours']);
|
||||
$setting['task_remind_hours2'] = floatval($setting['task_remind_hours2']);
|
||||
//
|
||||
return Base::retSuccess('success', $setting ?: json_decode('{}'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/demo 05. 获取演示帐号
|
||||
* @api {get} api/system/setting/thirdaccess 06. 第三方帐号(限管理员)
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
* @apiName setting__thirdaccess
|
||||
*
|
||||
* @apiParam {String} type
|
||||
* - get: 获取(默认)
|
||||
* - save: 保存设置(参数:['ldap_open', 'ldap_host', 'ldap_port', 'ldap_password', 'ldap_user_dn', 'ldap_base_dn', 'ldap_sync_local'])
|
||||
* - testldap: 测试ldap连接
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function setting__thirdaccess()
|
||||
{
|
||||
User::auth('admin');
|
||||
//
|
||||
$type = trim(Request::input('type'));
|
||||
if ($type == 'testldap') {
|
||||
$all = Base::newTrim(Request::input());
|
||||
$connection = Container::getDefaultConnection();
|
||||
try {
|
||||
$connection->setConfiguration([
|
||||
"hosts" => [$all['ldap_host']],
|
||||
"port" => intval($all['ldap_port']),
|
||||
"password" => $all['ldap_password'],
|
||||
"username" => $all['ldap_user_dn'],
|
||||
"base_dn" => $all['ldap_base_dn'],
|
||||
]);
|
||||
if ($connection->auth()->attempt($all['ldap_user_dn'], $all['ldap_password'])) {
|
||||
return Base::retSuccess('验证通过');
|
||||
} else {
|
||||
return Base::retError('验证失败');
|
||||
}
|
||||
} catch (LdapRecordException $e) {
|
||||
return Base::retError($e->getMessage() ?: "验证失败:未知错误", config("ldap.connections.default"));
|
||||
}
|
||||
} elseif ($type == 'save') {
|
||||
if (env("SYSTEM_SETTING") == 'disabled') {
|
||||
return Base::retError('当前环境禁止修改');
|
||||
}
|
||||
$all = Base::newTrim(Request::input());
|
||||
foreach ($all as $key => $value) {
|
||||
if (!in_array($key, [
|
||||
'ldap_open',
|
||||
'ldap_host',
|
||||
'ldap_port',
|
||||
'ldap_password',
|
||||
'ldap_user_dn',
|
||||
'ldap_base_dn',
|
||||
'ldap_sync_local'
|
||||
])) {
|
||||
unset($all[$key]);
|
||||
}
|
||||
}
|
||||
$all['ldap_port'] = intval($all['ldap_port']) ?: 389;
|
||||
$setting = Base::setting('thirdAccessSetting', Base::newTrim($all));
|
||||
} else {
|
||||
$setting = Base::setting('thirdAccessSetting');
|
||||
}
|
||||
//
|
||||
$setting['ldap_open'] = $setting['ldap_open'] ?: 'close';
|
||||
$setting['ldap_port'] = intval($setting['ldap_port']) ?: 389;
|
||||
$setting['ldap_sync_local'] = $setting['ldap_sync_local'] ?: 'close';
|
||||
//
|
||||
return Base::retSuccess('success', $setting ?: json_decode('{}'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/demo 07. 获取演示帐号
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@@ -288,7 +428,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/system/priority 06. 任务优先级
|
||||
* @api {post} api/system/priority 08. 任务优先级
|
||||
*
|
||||
* @apiDescription 获取任务优先级、保存任务优先级
|
||||
* @apiVersion 1.0.0
|
||||
@@ -309,7 +449,7 @@ class SystemController extends AbstractController
|
||||
$type = trim(Request::input('type'));
|
||||
if ($type == 'save') {
|
||||
User::auth('admin');
|
||||
$list = Base::getPostValue('list');
|
||||
$list = Request::input('list');
|
||||
$array = [];
|
||||
if (empty($list) || !is_array($list)) {
|
||||
return Base::retError('参数错误');
|
||||
@@ -337,7 +477,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/system/column/template 07. 创建项目模板
|
||||
* @api {post} api/system/column/template 09. 创建项目模板
|
||||
*
|
||||
* @apiDescription 获取创建项目模板、保存创建项目模板
|
||||
* @apiVersion 1.0.0
|
||||
@@ -358,7 +498,7 @@ class SystemController extends AbstractController
|
||||
$type = trim(Request::input('type'));
|
||||
if ($type == 'save') {
|
||||
User::auth('admin');
|
||||
$list = Base::getPostValue('list');
|
||||
$list = Request::input('list');
|
||||
$array = [];
|
||||
if (empty($list) || !is_array($list)) {
|
||||
return Base::retError('参数错误');
|
||||
@@ -384,7 +524,70 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/info 08. 获取终端详细信息
|
||||
* @api {post} api/system/license 10. License
|
||||
*
|
||||
* @apiDescription 获取License信息、保存License(限管理员)
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
* @apiName license
|
||||
*
|
||||
* @apiParam {String} type
|
||||
* - get: 获取
|
||||
* - save: 保存
|
||||
* @apiParam {String} license License 原文
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function license()
|
||||
{
|
||||
User::auth('admin');
|
||||
//
|
||||
$type = trim(Request::input('type'));
|
||||
if ($type == 'save') {
|
||||
$license = Request::input('license');
|
||||
Doo::licenseSave($license);
|
||||
}
|
||||
//
|
||||
$data = [
|
||||
'license' => Doo::licenseContent(),
|
||||
'info' => Doo::license(),
|
||||
'macs' => Doo::macs(),
|
||||
'doo_sn' => Doo::dooSN(),
|
||||
'user_count' => User::whereBot(0)->whereNull('disable_at')->count(),
|
||||
'error' => []
|
||||
];
|
||||
if ($data['info']['people'] > 3) {
|
||||
// 小于3人的License不检查
|
||||
if ($data['info']['sn'] != $data['doo_sn']) {
|
||||
$data['error'][] = '终端SN与License不匹配';
|
||||
}
|
||||
if ($data['info']['mac']) {
|
||||
$approved = false;
|
||||
foreach ($data['info']['mac'] as $mac) {
|
||||
if (in_array($mac, $data['macs'])) {
|
||||
$approved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$approved) {
|
||||
$data['error'][] = '终端MAC与License不匹配';
|
||||
}
|
||||
}
|
||||
if ($data['user_count'] > $data['info']['people']) {
|
||||
$data['error'][] = '终端用户数超过License限制';
|
||||
}
|
||||
if ($data['info']['expired_at'] && strtotime($data['info']['expired_at']) <= Base::time()) {
|
||||
$data['error'][] = '终端License已过期';
|
||||
}
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('success', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/info 11. 获取终端详细信息
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@@ -403,17 +606,17 @@ class SystemController extends AbstractController
|
||||
}
|
||||
return Base::retSuccess('success', [
|
||||
'ip' => Base::getIp(),
|
||||
'ip-info' => Base::getIpInfo(Base::getIp()),
|
||||
'ip-gcj02' => Base::getIpGcj02(Base::getIp()),
|
||||
'ip-info' => Extranet::getIpInfo(Base::getIp()),
|
||||
'ip-gcj02' => Extranet::getIpGcj02(Base::getIp()),
|
||||
'ip-iscn' => Base::isCnIp(Base::getIp()),
|
||||
'header' => Request::header(),
|
||||
'token' => Base::getToken(),
|
||||
'token' => Doo::userToken(),
|
||||
'url' => url('') . Base::getUrl(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/ip 09. 获取IP地址
|
||||
* @api {get} api/system/get/ip 12. 获取IP地址
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@@ -428,7 +631,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/cnip 10. 是否中国IP地址
|
||||
* @api {get} api/system/get/cnip 13. 是否中国IP地址
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@@ -445,7 +648,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/ipgcj02 11. 获取IP地址经纬度
|
||||
* @api {get} api/system/get/ipgcj02 14. 获取IP地址经纬度
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@@ -458,11 +661,11 @@ class SystemController extends AbstractController
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function get__ipgcj02() {
|
||||
return Base::getIpGcj02(Request::input("ip"));
|
||||
return Extranet::getIpGcj02(Request::input("ip"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/ipinfo 12. 获取IP地址详细信息
|
||||
* @api {get} api/system/get/ipinfo 15. 获取IP地址详细信息
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@@ -475,11 +678,11 @@ class SystemController extends AbstractController
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function get__ipinfo() {
|
||||
return Base::getIpInfo(Request::input("ip"));
|
||||
return Extranet::getIpInfo(Request::input("ip"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/system/imgupload 13. 上传图片
|
||||
* @api {post} api/system/imgupload 16. 上传图片
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -513,8 +716,8 @@ class SystemController extends AbstractController
|
||||
$scale = [$width, $height, $whcut];
|
||||
}
|
||||
$path = "uploads/user/picture/" . User::userid() . "/" . date("Ym") . "/";
|
||||
$image64 = trim(Base::getPostValue('image64'));
|
||||
$fileName = trim(Base::getPostValue('filename'));
|
||||
$image64 = trim(Request::input('image64'));
|
||||
$fileName = trim(Request::input('filename'));
|
||||
if ($image64) {
|
||||
$data = Base::image64save([
|
||||
"image64" => $image64,
|
||||
@@ -539,7 +742,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/imgview 14. 浏览图片空间
|
||||
* @api {get} api/system/get/imgview 17. 浏览图片空间
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -594,7 +797,7 @@ class SystemController extends AbstractController
|
||||
'path' => $pathTemp,
|
||||
'url' => Base::fillUrl($pathTemp),
|
||||
'thumb' => Base::fillUrl('images/other/dir.png'),
|
||||
'inode' => fileatime($v),
|
||||
'inode' => filemtime($v),
|
||||
];
|
||||
} elseif (!str_ends_with($filename, "_thumb.jpg")) {
|
||||
$array = [
|
||||
@@ -603,11 +806,11 @@ class SystemController extends AbstractController
|
||||
'path' => $pathTemp,
|
||||
'url' => Base::fillUrl($pathTemp),
|
||||
'thumb' => $pathTemp,
|
||||
'inode' => fileatime($v),
|
||||
'inode' => filemtime($v),
|
||||
];
|
||||
//
|
||||
$extension = pathinfo($dirPath . $filename, PATHINFO_EXTENSION);
|
||||
if (in_array($extension, array('gif', 'jpg', 'jpeg', 'png', 'bmp'))) {
|
||||
if (in_array($extension, array('gif', 'jpg', 'jpeg', 'webp', 'png', 'bmp'))) {
|
||||
if (file_exists($dirPath . $filename . '_thumb.jpg')) {
|
||||
$array['thumb'] .= '_thumb.jpg';
|
||||
}
|
||||
@@ -635,7 +838,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/system/fileupload 15. 上传文件
|
||||
* @api {post} api/system/fileupload 18. 上传文件
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -656,8 +859,8 @@ class SystemController extends AbstractController
|
||||
return Base::retError('身份失效,等重新登录');
|
||||
}
|
||||
$path = "uploads/user/file/" . User::userid() . "/" . date("Ym") . "/";
|
||||
$image64 = trim(Base::getPostValue('image64'));
|
||||
$fileName = trim(Base::getPostValue('filename'));
|
||||
$image64 = trim(Request::input('image64'));
|
||||
$fileName = trim(Request::input('filename'));
|
||||
if ($image64) {
|
||||
$data = Base::image64save([
|
||||
"image64" => $image64,
|
||||
@@ -677,7 +880,39 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/starthome 16. 启动首页设置信息
|
||||
* @api {get} api/system/get/showitem 19. 首页显示ITEM
|
||||
*
|
||||
* @apiDescription 用于判断首页是否显示:pro、github、更新日志...
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
* @apiName get__showitem
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function get__showitem()
|
||||
{
|
||||
$logPath = base_path('CHANGELOG.md');
|
||||
$logContent = "";
|
||||
$logVersion = "";
|
||||
if (file_exists($logPath)) {
|
||||
$logContent = file_get_contents($logPath);
|
||||
preg_match("/## \[(.*?)\]/", $logContent, $matchs);
|
||||
if ($matchs) {
|
||||
$logVersion = $matchs[1] === "Unreleased" ? $matchs[1] : "v{$matchs[1]}";
|
||||
}
|
||||
}
|
||||
return Base::retSuccess('success', [
|
||||
'pro' => str_contains(Request::getHost(), "dootask.com") || str_contains(Request::getHost(), "127.0.0.1"),
|
||||
'github' => env('GITHUB_URL') ?: false,
|
||||
'updateLog' => $logContent ?: false,
|
||||
'updateVer' => $logVersion,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/get/starthome 20. 启动首页设置信息
|
||||
*
|
||||
* @apiDescription 用于判断注册是否需要启动首页
|
||||
* @apiVersion 1.0.0
|
||||
@@ -697,7 +932,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/email/check 17. 邮件发送测试(限管理员)
|
||||
* @api {get} api/system/email/check 21. 邮件发送测试(限管理员)
|
||||
*
|
||||
* @apiDescription 测试配置邮箱是否能发送邮件
|
||||
* @apiVersion 1.0.0
|
||||
@@ -743,7 +978,200 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/version 18. 获取版本号
|
||||
* @api {get} api/system/checkin/export 22. 导出签到数据(限管理员)
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
* @apiName checkin__export
|
||||
*
|
||||
* @apiParam {Array} [userid] 指定会员,如:[1, 2]
|
||||
* @apiParam {Array} [date] 指定日期范围,如:['2020-12-12', '2020-12-30']
|
||||
* @apiParam {Array} [time] 指定时间范围,如:['09:00', '18:00']
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function checkin__export()
|
||||
{
|
||||
User::auth('admin');
|
||||
//
|
||||
$setting = Base::setting('checkinSetting');
|
||||
if ($setting['open'] !== 'open') {
|
||||
return Base::retError('此功能未开启,请前往系统设置开启');
|
||||
}
|
||||
//
|
||||
$userid = Base::arrayRetainInt(Request::input('userid'), true);
|
||||
$date = Request::input('date');
|
||||
$time = Request::input('time');
|
||||
//
|
||||
if (empty($userid) || empty($date) || empty($time)) {
|
||||
return Base::retError('参数错误');
|
||||
}
|
||||
if (count($userid) > 100) {
|
||||
return Base::retError('导出成员限制最多100个');
|
||||
}
|
||||
if (!(is_array($date) && Base::isDate($date[0]) && Base::isDate($date[1]))) {
|
||||
return Base::retError('日期选择错误');
|
||||
}
|
||||
if (Carbon::parse($date[1])->timestamp - Carbon::parse($date[0])->timestamp > 35 * 86400) {
|
||||
return Base::retError('日期范围限制最大35天');
|
||||
}
|
||||
if (!(is_array($time) && Base::isTime($time[0]) && Base::isTime($time[1]))) {
|
||||
return Base::retError('时间选择错误');
|
||||
}
|
||||
//
|
||||
$secondStart = strtotime("2000-01-01 {$time[0]}") - strtotime("2000-01-01 00:00:00");
|
||||
$secondEnd = strtotime("2000-01-01 {$time[1]}") - strtotime("2000-01-01 00:00:00");
|
||||
//
|
||||
$headings = [];
|
||||
$headings[] = '签到人';
|
||||
$headings[] = '签到日期';
|
||||
$headings[] = '班次时间';
|
||||
$headings[] = '首次签到时间';
|
||||
$headings[] = '首次签到结果';
|
||||
$headings[] = '最后签到时间';
|
||||
$headings[] = '最后签到结果';
|
||||
$headings[] = '参数数据';
|
||||
//
|
||||
$sheets = [];
|
||||
$startD = Carbon::parse($date[0])->startOfDay();
|
||||
$endD = Carbon::parse($date[1])->endOfDay();
|
||||
$users = User::whereIn('userid', $userid)->take(100)->get();
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$recordTimes = UserCheckinRecord::getTimes($user->userid, [$startD, $endD]);
|
||||
//
|
||||
$nickname = Base::filterEmoji($user->nickname);
|
||||
$styles = ["A1:H1" => ["font" => ["bold" => true]]];
|
||||
$datas = [];
|
||||
$startT = $startD->timestamp;
|
||||
$endT = $endD->timestamp;
|
||||
$index = 1;
|
||||
while ($startT < $endT) {
|
||||
$index++;
|
||||
$sameDate = date("Y-m-d", $startT);
|
||||
$sameTimes = $recordTimes[$sameDate] ?? [];
|
||||
$sameCollect = UserCheckinRecord::atCollect($sameDate, $sameTimes);
|
||||
$firstBetween = [Carbon::createFromTimestamp($startT), Carbon::createFromTimestamp($startT + $secondEnd - 1)];
|
||||
$lastBetween = [Carbon::createFromTimestamp($startT + $secondStart + 1), Carbon::createFromTimestamp($startT + 86400)];
|
||||
$firstRecord = $sameCollect?->whereBetween("datetime", $firstBetween)->first();
|
||||
$lastRecord = $sameCollect?->whereBetween("datetime", $lastBetween)->last();
|
||||
$firstTimestamp = $firstRecord['timestamp'] ?: 0;
|
||||
$lastTimestamp = $lastRecord['timestamp'] ?: 0;
|
||||
if (Base::time() < $startT + $secondStart) {
|
||||
$firstResult = "-";
|
||||
} else {
|
||||
$firstResult = "正常";
|
||||
if (empty($firstTimestamp)) {
|
||||
$firstResult = "缺卡";
|
||||
$styles["E{$index}"] = ["font" => ["color" => ["rgb" => "ff0000"]]];
|
||||
} elseif ($firstTimestamp > $startT + $secondStart) {
|
||||
$firstResult = "迟到";
|
||||
$styles["E{$index}"] = ["font" => ["color" => ["rgb" => "436FF6"]]];
|
||||
}
|
||||
}
|
||||
if (Base::time() < $startT + $secondEnd) {
|
||||
$lastResult = "-";
|
||||
$lastTimestamp = 0;
|
||||
} else {
|
||||
$lastResult = "正常";
|
||||
if (empty($lastTimestamp) || $lastTimestamp === $firstTimestamp) {
|
||||
$lastResult = "缺卡";
|
||||
$styles["G{$index}"] = ["font" => ["color" => ["rgb" => "ff0000"]]];
|
||||
} elseif ($lastTimestamp < $startT + $secondEnd) {
|
||||
$lastResult = "早退";
|
||||
$styles["G{$index}"] = ["font" => ["color" => ["rgb" => "436FF6"]]];
|
||||
}
|
||||
}
|
||||
$firstTimestamp = $firstTimestamp ? date("H:i", $firstTimestamp) : "-";
|
||||
$lastTimestamp = $lastTimestamp ? date("H:i", $lastTimestamp) : "-";
|
||||
$section = array_map(function($item) {
|
||||
return $item[0] . "-" . ($item[1] ?: "None");
|
||||
}, UserCheckinRecord::atSection($sameTimes));
|
||||
$datas[] = [
|
||||
"{$nickname} (ID: {$user->userid})",
|
||||
$sameDate,
|
||||
implode("-", $time),
|
||||
$firstTimestamp,
|
||||
$firstResult,
|
||||
$lastTimestamp,
|
||||
$lastResult,
|
||||
implode(", ", $section),
|
||||
];
|
||||
$startT += 86400;
|
||||
}
|
||||
$title = (count($sheets) + 1) . "." . ($nickname ?: $user->userid);
|
||||
$sheets[] = BillExport::create()->setTitle($title)->setHeadings($headings)->setData($datas)->setStyles($styles);
|
||||
}
|
||||
if (empty($sheets)) {
|
||||
return Base::retError('没有任何数据');
|
||||
}
|
||||
//
|
||||
$fileName = $users[0]->nickname;
|
||||
if (count($users) > 1) {
|
||||
$fileName .= "等" . count($userid) . "位成员";
|
||||
}
|
||||
$fileName .= '签到记录_' . Base::time() . '.xls';
|
||||
$filePath = "temp/checkin/export/" . date("Ym", Base::time());
|
||||
$export = new BillMultipleExport($sheets);
|
||||
$res = $export->store($filePath . "/" . $fileName);
|
||||
if ($res != 1) {
|
||||
return Base::retError('导出失败,' . $fileName . '!');
|
||||
}
|
||||
$xlsPath = storage_path("app/" . $filePath . "/" . $fileName);
|
||||
$zipFile = "app/" . $filePath . "/" . Base::rightDelete($fileName, '.xls') . ".zip";
|
||||
$zipPath = storage_path($zipFile);
|
||||
if (file_exists($zipPath)) {
|
||||
Base::deleteDirAndFile($zipPath, true);
|
||||
}
|
||||
try {
|
||||
Madzipper::make($zipPath)->add($xlsPath)->close();
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
//
|
||||
if (file_exists($zipPath)) {
|
||||
$base64 = base64_encode(Base::array2string([
|
||||
'file' => $zipFile,
|
||||
]));
|
||||
Session::put('checkin::export:userid', $user->userid);
|
||||
return Base::retSuccess('success', [
|
||||
'size' => Base::twoFloat(filesize($zipPath) / 1024, true),
|
||||
'url' => Base::fillUrl('api/system/checkin/down?key=' . urlencode($base64)),
|
||||
]);
|
||||
} else {
|
||||
return Base::retError('打包失败,请稍后再试...');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/checkin/down 23. 下载导出的签到数据
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
* @apiName checkin__down
|
||||
*
|
||||
* @apiParam {String} key 通过export接口得到的下载钥匙
|
||||
*
|
||||
* @apiSuccess {File} data 返回数据(直接下载文件)
|
||||
*/
|
||||
public function checkin__down()
|
||||
{
|
||||
$userid = Session::get('checkin::export:userid');
|
||||
if (empty($userid)) {
|
||||
return Base::ajaxError("请求已过期,请重新导出!", [], 0, 502);
|
||||
}
|
||||
//
|
||||
$array = Base::string2array(base64_decode(urldecode(Request::input('key'))));
|
||||
$file = $array['file'];
|
||||
if (empty($file) || !file_exists(storage_path($file))) {
|
||||
return Base::ajaxError("文件不存在!", [], 0, 502);
|
||||
}
|
||||
return Response::download(storage_path($file));
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/version 24. 获取版本号
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@@ -758,14 +1186,16 @@ class SystemController extends AbstractController
|
||||
$package = Base::getPackage();
|
||||
$array = [
|
||||
'version' => Base::getVersion(),
|
||||
'publish' => Arr::get($package, 'app.0.publish'),
|
||||
'publish' => [],
|
||||
];
|
||||
if (is_array($package['app'])) {
|
||||
$i = 0;
|
||||
foreach ($package['app'] as $item) {
|
||||
$urls = $item['urls'] && is_array($item['urls']) ? $item['urls'] : $item['url'];
|
||||
if (is_array($item['publish']) && Base::hostContrast($url, $urls)) {
|
||||
if (is_array($item['publish']) && ($i === 0 || Base::hostContrast($url, $urls))) {
|
||||
$array['publish'] = $item['publish'];
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,12 +4,15 @@ namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\File;
|
||||
use App\Module\Base;
|
||||
use App\Module\Ihttp;
|
||||
use App\Module\Extranet;
|
||||
use App\Module\RandomColor;
|
||||
use App\Tasks\AppPushTask;
|
||||
use App\Tasks\AutoArchivedTask;
|
||||
use App\Tasks\CheckinRemindTask;
|
||||
use App\Tasks\DeleteBotMsgTask;
|
||||
use App\Tasks\DeleteTmpTask;
|
||||
use App\Tasks\EmailNoticeTask;
|
||||
use App\Tasks\JokeSoupTask;
|
||||
use App\Tasks\LoopTask;
|
||||
use Arr;
|
||||
use Cache;
|
||||
@@ -17,6 +20,7 @@ use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||
use LasseRafn\InitialAvatarGenerator\InitialAvatar;
|
||||
use Redirect;
|
||||
use Request;
|
||||
use Response;
|
||||
|
||||
|
||||
/**
|
||||
@@ -48,19 +52,22 @@ class IndexController extends InvokeController
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
$hash = 'no';
|
||||
$path = public_path('js/hash');
|
||||
$murl = url('manifest.txt');
|
||||
if (file_exists($path)) {
|
||||
$hash = trim(file_get_contents(public_path('js/hash')));
|
||||
if (strlen($hash) > 16) {
|
||||
$hash = 'long';
|
||||
}
|
||||
$hotFile = public_path('hot');
|
||||
$manifestFile = public_path('manifest.json');
|
||||
if (file_exists($hotFile)) {
|
||||
$array = Base::json2array(file_get_contents($hotFile));
|
||||
$style = null;
|
||||
$script = preg_replace("/^(\/\/(.*?))(:\d+)?\//i", "$1:" . $array['APP_DEV_PORT'] . "/", asset_main("resources/assets/js/app.js"));
|
||||
} else {
|
||||
$array = Base::json2array(file_get_contents($manifestFile));
|
||||
$style = asset_main($array['resources/assets/js/app.js']['css'][0]);
|
||||
$script = asset_main($array['resources/assets/js/app.js']['file']);
|
||||
}
|
||||
return response()->view('main', [
|
||||
'version' => Base::getVersion(),
|
||||
'hash' => $hash
|
||||
])->header('Link', "<{$murl}>; rel=\"prefetch\"");
|
||||
'style' => $style,
|
||||
'script' => $script,
|
||||
])->header('Link', "<" . url('manifest.txt') . ">; rel=\"prefetch\"");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,28 +83,28 @@ class IndexController extends InvokeController
|
||||
}
|
||||
$array = [
|
||||
"office/web-apps/apps/api/documents/api.js?hash=" . Base::getVersion(),
|
||||
"office/7.1.1-23/web-apps/vendor/requirejs/require.js",
|
||||
"office/7.1.1-23/web-apps/apps/api/documents/api.js",
|
||||
"office/7.1.1-23/sdkjs/common/AllFonts.js",
|
||||
"office/7.1.1-23/web-apps/vendor/xregexp/xregexp-all-min.js",
|
||||
"office/7.1.1-23/web-apps/vendor/sockjs/sockjs.min.js",
|
||||
"office/7.1.1-23/web-apps/vendor/jszip/jszip.min.js",
|
||||
"office/7.1.1-23/web-apps/vendor/jszip-utils/jszip-utils.min.js",
|
||||
"office/7.1.1-23/sdkjs/common/libfont/wasm/fonts.js",
|
||||
"office/7.1.1-23/sdkjs/common/Charts/ChartStyles.js",
|
||||
"office/7.1.1-23/sdkjs/slide/themes//themes.js",
|
||||
"office/7.3.2-8/web-apps/vendor/requirejs/require.js",
|
||||
"office/7.3.2-8/web-apps/apps/api/documents/api.js",
|
||||
"office/7.3.2-8/sdkjs/common/AllFonts.js",
|
||||
"office/7.3.2-8/web-apps/vendor/xregexp/xregexp-all-min.js",
|
||||
"office/7.3.2-8/web-apps/vendor/sockjs/sockjs.min.js",
|
||||
"office/7.3.2-8/web-apps/vendor/jszip/jszip.min.js",
|
||||
"office/7.3.2-8/web-apps/vendor/jszip-utils/jszip-utils.min.js",
|
||||
"office/7.3.2-8/sdkjs/common/libfont/wasm/fonts.js",
|
||||
"office/7.3.2-8/sdkjs/common/Charts/ChartStyles.js",
|
||||
"office/7.3.2-8/sdkjs/slide/themes//themes.js",
|
||||
|
||||
"office/7.1.1-23/web-apps/apps/presentationeditor/main/app.js",
|
||||
"office/7.1.1-23/sdkjs/slide/sdk-all-min.js",
|
||||
"office/7.1.1-23/sdkjs/slide/sdk-all.js",
|
||||
"office/7.3.2-8/web-apps/apps/presentationeditor/main/app.js",
|
||||
"office/7.3.2-8/sdkjs/slide/sdk-all-min.js",
|
||||
"office/7.3.2-8/sdkjs/slide/sdk-all.js",
|
||||
|
||||
"office/7.1.1-23/web-apps/apps/documenteditor/main/app.js",
|
||||
"office/7.1.1-23/sdkjs/word/sdk-all-min.js",
|
||||
"office/7.1.1-23/sdkjs/word/sdk-all.js",
|
||||
"office/7.3.2-8/web-apps/apps/documenteditor/main/app.js",
|
||||
"office/7.3.2-8/sdkjs/word/sdk-all-min.js",
|
||||
"office/7.3.2-8/sdkjs/word/sdk-all.js",
|
||||
|
||||
"office/7.1.1-23/web-apps/apps/spreadsheeteditor/main/app.js",
|
||||
"office/7.1.1-23/sdkjs/cell/sdk-all-min.js",
|
||||
"office/7.1.1-23/sdkjs/cell/sdk-all.js",
|
||||
"office/7.3.2-8/web-apps/apps/spreadsheeteditor/main/app.js",
|
||||
"office/7.3.2-8/sdkjs/cell/sdk-all-min.js",
|
||||
"office/7.3.2-8/sdkjs/cell/sdk-all.js",
|
||||
];
|
||||
foreach ($array as &$item) {
|
||||
$item = url($item);
|
||||
@@ -186,9 +193,16 @@ class IndexController extends InvokeController
|
||||
// 删除过期的临时表数据
|
||||
Task::deliver(new DeleteTmpTask('wg_tmp_msgs', 1));
|
||||
Task::deliver(new DeleteTmpTask('task_worker', 12));
|
||||
Task::deliver(new DeleteTmpTask('tmp', 24));
|
||||
Task::deliver(new DeleteTmpTask('tmp'));
|
||||
Task::deliver(new DeleteTmpTask('file'));
|
||||
// 删除机器人消息
|
||||
Task::deliver(new DeleteBotMsgTask());
|
||||
// 周期任务
|
||||
Task::deliver(new LoopTask());
|
||||
// 签到提醒
|
||||
Task::deliver(new CheckinRemindTask());
|
||||
// 获取笑话/心灵鸡汤
|
||||
Task::deliver(new JokeSoupTask());
|
||||
|
||||
return "success";
|
||||
}
|
||||
@@ -198,24 +212,28 @@ class IndexController extends InvokeController
|
||||
*/
|
||||
public function desktop__publish($name = '')
|
||||
{
|
||||
$genericVersion = Request::header('generic-version');
|
||||
$publishVersion = Request::header('publish-version');
|
||||
$latestFile = public_path("uploads/desktop/latest");
|
||||
$latestVersion = file_exists($latestFile) ? trim(file_get_contents($latestFile)) : "0.0.1";
|
||||
if (strtolower($name) === 'latest') {
|
||||
$name = $latestVersion;
|
||||
}
|
||||
// 上传
|
||||
if (preg_match("/^\d+\.\d+\.\d+$/", $genericVersion)) {
|
||||
if (version_compare($genericVersion, $latestVersion) > -1) { // 限制上传版本必须 ≥ 当前版本
|
||||
$genericPath = "uploads/desktop/{$genericVersion}/";
|
||||
if (preg_match("/^\d+\.\d+\.\d+$/", $publishVersion)) {
|
||||
$publishKey = Request::header('publish-key');
|
||||
if ($publishKey !== env('APP_KEY')) {
|
||||
return Base::retError("key error");
|
||||
}
|
||||
if (version_compare($publishVersion, $latestVersion) > -1) { // 限制上传版本必须 ≥ 当前版本
|
||||
$publishPath = "uploads/desktop/{$publishVersion}/";
|
||||
$res = Base::upload([
|
||||
"file" => Request::file('file'),
|
||||
"type" => 'desktop',
|
||||
"path" => $genericPath,
|
||||
"path" => $publishPath,
|
||||
"fileName" => true
|
||||
]);
|
||||
if (Base::isSuccess($res)) {
|
||||
file_put_contents($latestFile, $genericVersion);
|
||||
file_put_contents($latestFile, $publishVersion);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
@@ -233,7 +251,7 @@ class IndexController extends InvokeController
|
||||
$fileName = Base::leftDelete($file, $dirPath);
|
||||
$files[] = [
|
||||
'name' => substr($fileName, 1),
|
||||
'time' => date("Y-m-d H:i:s", fileatime($file)),
|
||||
'time' => date("Y-m-d H:i:s", filemtime($file)),
|
||||
'size' => Base::readableBytes(filesize($file)),
|
||||
'url' => Base::fillUrl($path . $fileName),
|
||||
];
|
||||
@@ -247,13 +265,13 @@ class IndexController extends InvokeController
|
||||
if (!str_ends_with($file, '.apk')) {
|
||||
continue;
|
||||
}
|
||||
if ($apkFile && strtotime($apkFile['time']) > fileatime($file)) {
|
||||
if ($apkFile && strtotime($apkFile['time']) > filemtime($file)) {
|
||||
continue;
|
||||
}
|
||||
$fileName = Base::leftDelete($file, $dirPath);
|
||||
$apkFile = [
|
||||
'name' => substr($fileName, 1),
|
||||
'time' => date("Y-m-d H:i:s", fileatime($file)),
|
||||
'time' => date("Y-m-d H:i:s", filemtime($file)),
|
||||
'size' => Base::readableBytes(filesize($file)),
|
||||
'url' => Base::fillUrl($path . $fileName),
|
||||
];
|
||||
@@ -265,11 +283,11 @@ class IndexController extends InvokeController
|
||||
}
|
||||
// 下载
|
||||
if ($name && file_exists($latestFile)) {
|
||||
$genericVersion = file_get_contents($latestFile);
|
||||
if (preg_match("/^\d+\.\d+\.\d+$/", $genericVersion)) {
|
||||
$filePath = public_path("uploads/desktop/{$genericVersion}/{$name}");
|
||||
$publishVersion = file_get_contents($latestFile);
|
||||
if (preg_match("/^\d+\.\d+\.\d+$/", $publishVersion)) {
|
||||
$filePath = public_path("uploads/desktop/{$publishVersion}/{$name}");
|
||||
if (file_exists($filePath)) {
|
||||
return response()->download($filePath);
|
||||
return Response::download($filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -282,20 +300,10 @@ class IndexController extends InvokeController
|
||||
*/
|
||||
public function drawio__iconsearch()
|
||||
{
|
||||
$query = Request::input('q');
|
||||
$page = Request::input('p');
|
||||
$size = Request::input('c');
|
||||
$url = "https://app.diagrams.net/iconSearch?q={$query}&p={$page}&c={$size}";
|
||||
$result = Cache::remember("drawioIconsearch::" . md5($url), now()->addDays(15), function () use ($url) {
|
||||
return Ihttp::ihttp_get($url);
|
||||
});
|
||||
if (Base::isSuccess($result)) {
|
||||
return $result['data'];
|
||||
}
|
||||
return [
|
||||
'icons' => [],
|
||||
'total_count' => 0
|
||||
];
|
||||
$query = trim(Request::input('q'));
|
||||
$page = trim(Request::input('p'));
|
||||
$size = trim(Request::input('c'));
|
||||
return Extranet::drawioIconSearch($query, $page, $size);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -317,7 +325,7 @@ class IndexController extends InvokeController
|
||||
$userAgent = strtolower(Request::server('HTTP_USER_AGENT'));
|
||||
if ($ext === 'pdf'
|
||||
&& (str_contains($userAgent, 'electron') || str_contains($userAgent, 'chrome'))) {
|
||||
return response()->download($file, $name, [
|
||||
return Response::download($file, $name, [
|
||||
'Content-Type' => 'application/pdf'
|
||||
], 'inline');
|
||||
}
|
||||
@@ -411,9 +419,6 @@ class IndexController extends InvokeController
|
||||
$list = array_merge(Base::readDir(app_path()), Base::readDir(resource_path()));
|
||||
$array = [];
|
||||
foreach ($list as $item) {
|
||||
if (Base::rightExists($item, "language.all.js")) {
|
||||
continue;
|
||||
}
|
||||
if (Base::rightExists($item, ".php") || Base::rightExists($item, ".vue") || Base::rightExists($item, ".js")) {
|
||||
$content = file_get_contents($item);
|
||||
preg_match_all("/(['\"])(.*?)[\u{4e00}-\u{9fa5}\u{FE30}-\u{FFA0}]+([\s\S]((?!\n).)*)\\1/u", $content, $matchs);
|
||||
|
||||
@@ -12,47 +12,8 @@ class VerifyCsrfToken extends Middleware
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
// 上传图片
|
||||
'api/system/imgupload/',
|
||||
|
||||
// 上传文件
|
||||
'api/system/fileupload/',
|
||||
|
||||
// 保存任务优先级
|
||||
'api/system/priority/',
|
||||
|
||||
// 保存创建项目列表模板
|
||||
'api/system/column/template/',
|
||||
|
||||
// 添加任务
|
||||
'api/project/task/add/',
|
||||
|
||||
// 保存工作流
|
||||
'api/project/flow/save/',
|
||||
|
||||
// 修改任务
|
||||
'api/project/task/update/',
|
||||
|
||||
// 聊天发文本
|
||||
'api/dialog/msg/sendtext/',
|
||||
|
||||
// 聊天发语音
|
||||
'api/dialog/msg/sendrecord/',
|
||||
|
||||
// 聊天发文件
|
||||
'api/dialog/msg/sendfile/',
|
||||
|
||||
// 保存文件内容
|
||||
'api/file/content/save/',
|
||||
|
||||
// 保存文件内容(office)
|
||||
'api/file/content/office/',
|
||||
|
||||
// 保存文件内容(上传)
|
||||
'api/file/content/upload/',
|
||||
|
||||
// 保存汇报
|
||||
'api/report/store/',
|
||||
// 接口部分
|
||||
'api/*',
|
||||
|
||||
// 发布桌面端
|
||||
'desktop/publish/',
|
||||
|
||||
@@ -4,8 +4,9 @@ namespace App\Http\Middleware;
|
||||
|
||||
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
|
||||
|
||||
use App\Module\Base;
|
||||
use App\Module\Doo;
|
||||
use Closure;
|
||||
use Request;
|
||||
|
||||
class WebApi
|
||||
{
|
||||
@@ -21,17 +22,41 @@ class WebApi
|
||||
global $_A;
|
||||
$_A = [];
|
||||
|
||||
if (Request::input('__Access-Control-Allow-Origin') || Request::header('__Access-Control-Allow-Origin')) {
|
||||
header('Access-Control-Allow-Origin:*');
|
||||
header('Access-Control-Allow-Methods:GET,POST,PUT,DELETE,OPTIONS');
|
||||
header('Access-Control-Allow-Headers:Content-Type, platform, platform-channel, token, release, Access-Control-Allow-Origin');
|
||||
Doo::load();
|
||||
|
||||
$encrypt = Doo::pgpParseStr($request->header('encrypt'));
|
||||
if ($request->isMethod('post')) {
|
||||
$version = $request->header('version');
|
||||
if ($version && version_compare($version, '0.25.48', '<')) {
|
||||
// 旧版本兼容 php://input
|
||||
parse_str($request->getContent(), $content);
|
||||
if ($content) {
|
||||
$request->merge($content);
|
||||
}
|
||||
} elseif ($encrypt['encrypt_type'] === 'pgp' && $content = $request->input('encrypted')) {
|
||||
// 新版本解密提交的内容
|
||||
$content = Doo::pgpDecryptApi($content, $encrypt['encrypt_id']);
|
||||
if ($content) {
|
||||
$request->merge($content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 强制 https
|
||||
$APP_SCHEME = env('APP_SCHEME', 'auto');
|
||||
if (in_array(strtolower($APP_SCHEME), ['https', 'on', 'ssl', '1', 'true', 'yes'], true)) {
|
||||
$request->setTrustedProxies([$request->getClientIp()], $request::HEADER_X_FORWARDED_PROTO);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
$response = $next($request);
|
||||
|
||||
// 加密返回内容
|
||||
if ($encrypt['client_type'] === 'pgp' && $content = $response->getContent()) {
|
||||
$response->setContent(json_encode([
|
||||
'encrypted' => Doo::pgpEncryptApi($content, $encrypt['client_key'])
|
||||
]));
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
251
app/Ldap/LdapUser.php
Normal file
251
app/Ldap/LdapUser.php
Normal file
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
|
||||
namespace App\Ldap;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Module\Base;
|
||||
use LdapRecord\Configuration\ConfigurationException;
|
||||
use LdapRecord\Container;
|
||||
use LdapRecord\LdapRecordException;
|
||||
use LdapRecord\Models\Model;
|
||||
|
||||
class LdapUser extends Model
|
||||
{
|
||||
protected static $init = null;
|
||||
/**
|
||||
* The object classes of the LDAP model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $objectClasses = [
|
||||
'inetOrgPerson',
|
||||
'organizationalPerson',
|
||||
'person',
|
||||
'top',
|
||||
'posixAccount',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getPhoto()
|
||||
{
|
||||
return $this->jpegPhoto && is_array($this->jpegPhoto) ? $this->jpegPhoto[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getDisplayName()
|
||||
{
|
||||
$nickname = $this->displayName ?: $this->uid;
|
||||
return is_array($nickname) ? $nickname[0] : $nickname;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LdapUser
|
||||
*/
|
||||
public static function static(): LdapUser
|
||||
{
|
||||
return new static;
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务是否打开
|
||||
* @return bool
|
||||
*/
|
||||
public static function isOpen(): bool
|
||||
{
|
||||
return Base::settingFind('thirdAccessSetting', 'ldap_open') === 'open';
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步本地是否打开
|
||||
* @return bool
|
||||
*/
|
||||
public static function isSyncLocal(): bool
|
||||
{
|
||||
return Base::settingFind('thirdAccessSetting', 'ldap_sync_local') === 'open';
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化配置
|
||||
* @return bool
|
||||
*/
|
||||
public static function initConfig()
|
||||
{
|
||||
if (is_bool(self::$init)) {
|
||||
return self::$init;
|
||||
}
|
||||
//
|
||||
$setting = Base::setting('thirdAccessSetting');
|
||||
if ($setting['ldap_open'] !== 'open') {
|
||||
return self::$init = false;
|
||||
}
|
||||
//
|
||||
$connection = Container::getDefaultConnection();
|
||||
try {
|
||||
$connection->setConfiguration([
|
||||
"hosts" => [$setting['ldap_host']],
|
||||
"port" => intval($setting['ldap_port']),
|
||||
"base_dn" => $setting['ldap_base_dn'],
|
||||
"username" => $setting['ldap_user_dn'],
|
||||
"password" => $setting['ldap_password'],
|
||||
]);
|
||||
return self::$init = true;
|
||||
} catch (ConfigurationException $e) {
|
||||
info($e->getMessage());
|
||||
return self::$init = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取
|
||||
* @param $username
|
||||
* @param $password
|
||||
* @return Model|null
|
||||
*/
|
||||
public static function userFirst($username, $password): ?Model
|
||||
{
|
||||
if (!self::initConfig()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return self::static()
|
||||
->where([
|
||||
'cn' => $username,
|
||||
'userPassword' => $password
|
||||
])->first();
|
||||
} catch (\Exception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录
|
||||
* @param $username
|
||||
* @param $password
|
||||
* @param User|null $user
|
||||
* @return User|mixed|null
|
||||
*/
|
||||
public static function userLogin($username, $password, $user = null)
|
||||
{
|
||||
if (!self::initConfig()) {
|
||||
return null;
|
||||
}
|
||||
$row = self::userFirst($username, $password);
|
||||
if (!$row) {
|
||||
return null;
|
||||
}
|
||||
if (empty($user)) {
|
||||
$user = User::reg($username, $password);
|
||||
}
|
||||
if ($user) {
|
||||
$userimg = $row->getPhoto();
|
||||
if ($userimg) {
|
||||
$path = "uploads/user/ldap/";
|
||||
$file = "{$path}{$user->userid}.jpeg";
|
||||
Base::makeDir(public_path($path));
|
||||
if (Base::saveContentImage(public_path($file), $userimg)) {
|
||||
$user->userimg = $file;
|
||||
}
|
||||
}
|
||||
$user->nickname = $row->getDisplayName();
|
||||
$user->save();
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步
|
||||
* @param User $user
|
||||
* @param $password
|
||||
* @return void
|
||||
*/
|
||||
public static function userSync(User $user, $password)
|
||||
{
|
||||
if ($user->isLdap()) {
|
||||
return;
|
||||
}
|
||||
//
|
||||
if (!self::initConfig()) {
|
||||
return;
|
||||
}
|
||||
//
|
||||
if (self::isSyncLocal()) {
|
||||
$row = self::userFirst($user->email, $password);
|
||||
if ($row) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$userimg = public_path($user->getRawOriginal('userimg'));
|
||||
if (file_exists($userimg)) {
|
||||
$userimg = file_get_contents($userimg);
|
||||
} else {
|
||||
$userimg = '';
|
||||
}
|
||||
self::static()->create([
|
||||
'cn' => $user->email,
|
||||
'gidNumber' => 0,
|
||||
'homeDirectory' => '/home/ldap/dootask/' . env("APP_NAME"),
|
||||
'sn' => $user->email,
|
||||
'uid' => $user->email,
|
||||
'uidNumber' => $user->userid,
|
||||
'userPassword' => $password,
|
||||
'displayName' => $user->nickname,
|
||||
'jpegPhoto' => $userimg,
|
||||
]);
|
||||
$user->identity = Base::arrayImplode(array_merge(array_diff($user->identity, ['ldap']), ['ldap']));
|
||||
$user->save();
|
||||
} catch (LdapRecordException $e) {
|
||||
info("[LDAP] sync fail: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
* @param $username
|
||||
* @param $array
|
||||
* @return void
|
||||
*/
|
||||
public static function userUpdate($username, $array)
|
||||
{
|
||||
if (empty($array)) {
|
||||
return;
|
||||
}
|
||||
if (!self::initConfig()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$row = self::static()
|
||||
->where([
|
||||
'cn' => $username,
|
||||
])->first();
|
||||
$row?->update($array);
|
||||
} catch (\Exception $e) {
|
||||
info("[LDAP] update fail: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param $username
|
||||
* @return void
|
||||
*/
|
||||
public static function userDelete($username)
|
||||
{
|
||||
if (!self::initConfig()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$row = self::static()
|
||||
->where([
|
||||
'cn' => $username,
|
||||
])->first();
|
||||
$row?->delete();
|
||||
} catch (\Exception $e) {
|
||||
info("[LDAP] delete fail: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,10 @@ use Illuminate\Support\Facades\DB;
|
||||
* @method static \Illuminate\Database\Eloquent\Model|object|static|null cancelHidden()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|static with($relations)
|
||||
* @method static \Illuminate\Database\Query\Builder|static select($columns = [])
|
||||
* @method static \Illuminate\Database\Query\Builder|static whereIn($column, $values, $boolean = 'and', $not = false)
|
||||
* @method static \Illuminate\Database\Query\Builder|static whereNotIn($column, $values, $boolean = 'and')
|
||||
* @method int change(array $array)
|
||||
* @method int remove()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class AbstractModel extends Model
|
||||
@@ -38,6 +41,42 @@ class AbstractModel extends Model
|
||||
|
||||
protected $appendattrs = [];
|
||||
|
||||
/**
|
||||
* 通过模型修改数据
|
||||
* @param AbstractModel $builder
|
||||
* @param $array
|
||||
* @return int
|
||||
*/
|
||||
protected function scopeChange($builder, $array)
|
||||
{
|
||||
$line = 0;
|
||||
$rows = $builder->get();
|
||||
foreach ($rows as $row) {
|
||||
$row->updateInstance($array);
|
||||
if ($row->save()) {
|
||||
$line++;
|
||||
}
|
||||
}
|
||||
return $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过模型删除数据
|
||||
* @param AbstractModel $builder
|
||||
* @return int
|
||||
*/
|
||||
protected function scopeRemove($builder)
|
||||
{
|
||||
$line = 0;
|
||||
$rows = $builder->get();
|
||||
foreach ($rows as $row) {
|
||||
if ($row->delete()) {
|
||||
$line++;
|
||||
}
|
||||
}
|
||||
return $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据忽略错误
|
||||
* @return bool
|
||||
|
||||
91
app/Models/Deleted.php
Normal file
91
app/Models/Deleted.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* App\Models\Deleted
|
||||
*
|
||||
* @property int $id
|
||||
* @property string|null $type 删除的数据类型(如:project、task、dialog)
|
||||
* @property int|null $did 删除的数据ID
|
||||
* @property int|null $userid 关系会员ID
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Deleted newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Deleted newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Deleted query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Deleted whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Deleted whereDid($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Deleted whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Deleted whereType($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Deleted whereUserid($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Deleted extends AbstractModel
|
||||
{
|
||||
const UPDATED_AT = null;
|
||||
|
||||
/**
|
||||
* 获取删除的ID
|
||||
* @param $type
|
||||
* @param $userid
|
||||
* @param $time
|
||||
* @return array
|
||||
*/
|
||||
public static function ids($type, $userid, $time): array
|
||||
{
|
||||
$builder = self::where([
|
||||
'type' => $type,
|
||||
'userid' => $userid
|
||||
])->orderByDesc('id');
|
||||
if (empty($time)) {
|
||||
$builder = $builder->take(50);
|
||||
} else {
|
||||
$builder = $builder->where('created_at', '>=', Carbon::parse($time))->take(500);
|
||||
}
|
||||
return $builder->pluck('did')->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 忘记(恢复或添加数据时删除记录)
|
||||
* @param $type
|
||||
* @param $id
|
||||
* @param $userid
|
||||
* @return void
|
||||
*/
|
||||
public static function forget($type, $id, $userid): void
|
||||
{
|
||||
if (is_array($userid)) {
|
||||
self::where([
|
||||
'type' => $type,
|
||||
'did' => $id,
|
||||
])->whereIn('userid', $userid)->delete();
|
||||
} else {
|
||||
self::where([
|
||||
'type' => $type,
|
||||
'did' => $id,
|
||||
'userid' => $userid,
|
||||
])->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录(删除数据时添加记录)
|
||||
* @param $type
|
||||
* @param $id
|
||||
* @param $userid
|
||||
* @return void
|
||||
*/
|
||||
public static function record($type, $id, $userid): void
|
||||
{
|
||||
$array = is_array($userid) ? $userid : [$userid];
|
||||
foreach ($array as $value) {
|
||||
self::updateInsert([
|
||||
'type' => $type,
|
||||
'did' => $id,
|
||||
'userid' => $value,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ use Request;
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File newQuery()
|
||||
* @method static \Illuminate\Database\Query\Builder|File onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File whereCid($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File whereCreatedAt($value)
|
||||
@@ -46,8 +46,8 @@ use Request;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File whereType($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File whereUserid($value)
|
||||
* @method static \Illuminate\Database\Query\Builder|File withTrashed()
|
||||
* @method static \Illuminate\Database\Query\Builder|File withoutTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File withTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|File withoutTrashed()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class File extends AbstractModel
|
||||
@@ -81,14 +81,14 @@ class File extends AbstractModel
|
||||
* 图片文件
|
||||
*/
|
||||
const imageExt = [
|
||||
'jpg', 'jpeg', 'png', 'gif', 'bmp'
|
||||
'jpg', 'jpeg', 'webp', 'png', 'gif', 'bmp'
|
||||
];
|
||||
|
||||
/**
|
||||
* 本地媒体文件
|
||||
*/
|
||||
const localExt = [
|
||||
'jpg', 'jpeg', 'png', 'gif', 'bmp', 'ico', 'raw',
|
||||
'jpg', 'jpeg', 'webp', 'png', 'gif', 'bmp', 'ico', 'raw',
|
||||
'tif', 'tiff',
|
||||
'mp3', 'wav', 'mp4', 'flv',
|
||||
'avi', 'mov', 'wmv', 'mkv', '3gp', 'rm',
|
||||
@@ -96,21 +96,18 @@ class File extends AbstractModel
|
||||
|
||||
/**
|
||||
* 是否有访问权限
|
||||
* @param $userid
|
||||
* @param array $userids
|
||||
* @return int -1:没有权限,0:访问权限,1:读写权限,1000:所有者或创建者
|
||||
*/
|
||||
public function getPermission($userid)
|
||||
public function getPermission(array $userids)
|
||||
{
|
||||
if ($userid == $this->userid || $userid == $this->created_id) {
|
||||
if (in_array($this->userid, $userids) || in_array($this->created_id, $userids)) {
|
||||
// ① 自己的文件夹 或 自己创建的文件夹
|
||||
return 1000;
|
||||
}
|
||||
$row = $this->getShareInfo();
|
||||
if ($row) {
|
||||
$fileUser = FileUser::whereFileId($row->id)->where(function ($query) use ($userid) {
|
||||
$query->where('userid', 0);
|
||||
$query->orWhere('userid', $userid);
|
||||
})->orderByDesc('permission')->first();
|
||||
$fileUser = FileUser::whereFileId($row->id)->whereIn('userid', $userids)->orderByDesc('permission')->first();
|
||||
if ($fileUser) {
|
||||
// ② 在指定共享成员内
|
||||
return $fileUser->permission;
|
||||
@@ -184,6 +181,7 @@ class File extends AbstractModel
|
||||
if ($this->share != $share) {
|
||||
AbstractModel::transaction(function () use ($share) {
|
||||
$this->share = $share;
|
||||
$this->pshare = $share ? $this->id : 0;
|
||||
$this->save();
|
||||
File::where("pids", "like", "%,{$this->id},%")->update(['pshare' => $share ? $this->id : 0]);
|
||||
if ($share === 0) {
|
||||
@@ -290,6 +288,50 @@ class File extends AbstractModel
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制删除文件
|
||||
* @return true
|
||||
*/
|
||||
public function forceDeleteFile()
|
||||
{
|
||||
AbstractModel::transaction(function () {
|
||||
$this->forceDelete();
|
||||
FileContent::withTrashed()
|
||||
->whereFid($this->id)
|
||||
->orderBy('id')
|
||||
->chunk(500, function ($contents) {
|
||||
/** @var FileContent $content */
|
||||
foreach ($contents as $content) {
|
||||
$content->forceDeleteContent();
|
||||
}
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件分享链接
|
||||
* @param $userid
|
||||
* @param $refresh
|
||||
* @return array
|
||||
*/
|
||||
public function getShareLink($userid, $refresh = false)
|
||||
{
|
||||
if ($this->type == 'folder') {
|
||||
throw new ApiException('文件夹不支持分享');
|
||||
}
|
||||
return FileLink::generateLink($this->id, $userid, $refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名称加后缀
|
||||
* @return string|null
|
||||
*/
|
||||
public function getNameAndExt()
|
||||
{
|
||||
return $this->ext ? "{$this->name}.{$this->ext}" : $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送消息
|
||||
* @param $action
|
||||
@@ -367,6 +409,23 @@ class File extends AbstractModel
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* code获取文件ID、名称
|
||||
* @param $code
|
||||
* @return File
|
||||
*/
|
||||
public static function code2IdName($code) {
|
||||
$arr = explode(",", base64_decode($code));
|
||||
if (empty($arr)) {
|
||||
return null;
|
||||
}
|
||||
$fileId = intval($arr[0]);
|
||||
if (empty($fileId)) {
|
||||
return null;
|
||||
}
|
||||
return File::select(['id', 'name'])->find($fileId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理返回图片地址
|
||||
@@ -388,19 +447,25 @@ class File extends AbstractModel
|
||||
|
||||
/**
|
||||
* 获取文件并检测权限
|
||||
* @param $id
|
||||
* @param int $limit 要求权限: 0-访问权限、1-读写权限、1000-所有者或创建者
|
||||
* @param $permission
|
||||
* @param int $id
|
||||
* @param User|array|int $user 要求权限的用户,如:[0, 1]
|
||||
* @param int $limit 要求权限: 0-访问权限、1-读写权限、1000-所有者或创建者
|
||||
* @param int $permission
|
||||
* @return File
|
||||
*/
|
||||
public static function permissionFind($id, $limit = 0, &$permission = -1)
|
||||
public static function permissionFind(int $id, $user, int $limit = 0, int &$permission = -1)
|
||||
{
|
||||
$file = File::find($id);
|
||||
if (empty($file)) {
|
||||
throw new ApiException('文件不存在或已被删除');
|
||||
}
|
||||
//
|
||||
$permission = $file->getPermission(User::userid());
|
||||
if ($user instanceof User) {
|
||||
$userids = $user->isTemp() ? [$user->userid] : [0, $user->userid];
|
||||
} else {
|
||||
$userids = is_array($user) ? $user : [$user];
|
||||
}
|
||||
$permission = $file->getPermission($userids);
|
||||
if ($permission < $limit) {
|
||||
$msg = match ($limit) {
|
||||
1000 => '仅限所有者或创建者操作',
|
||||
|
||||
@@ -5,7 +5,6 @@ namespace App\Models;
|
||||
|
||||
use App\Module\Base;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Response;
|
||||
|
||||
/**
|
||||
* App\Models\FileContent
|
||||
@@ -21,7 +20,7 @@ use Response;
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileContent newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileContent newQuery()
|
||||
* @method static \Illuminate\Database\Query\Builder|FileContent onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileContent onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileContent query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileContent whereContent($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileContent whereCreatedAt($value)
|
||||
@@ -32,14 +31,30 @@ use Response;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileContent whereText($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileContent whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileContent whereUserid($value)
|
||||
* @method static \Illuminate\Database\Query\Builder|FileContent withTrashed()
|
||||
* @method static \Illuminate\Database\Query\Builder|FileContent withoutTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileContent withTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|FileContent withoutTrashed()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class FileContent extends AbstractModel
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
* 强制删除文件内容
|
||||
* @return void
|
||||
*/
|
||||
public function forceDeleteContent()
|
||||
{
|
||||
$this->forceDelete();
|
||||
$content = Base::json2array($this->content ?: []);
|
||||
if (str_starts_with($content['url'], 'uploads/')) {
|
||||
$path = public_path($content['url']);
|
||||
if (file_exists($path)) {
|
||||
@unlink($path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转预览地址
|
||||
* @param array $array
|
||||
@@ -85,7 +100,7 @@ class FileContent extends AbstractModel
|
||||
* @param File $file
|
||||
* @param $content
|
||||
* @param $download
|
||||
* @return array|\Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||
* @return array|\Symfony\Component\HttpFoundation\StreamedResponse
|
||||
*/
|
||||
public static function formatContent($file, $content, $download = false)
|
||||
{
|
||||
@@ -93,9 +108,13 @@ class FileContent extends AbstractModel
|
||||
$content = Base::json2array($content ?: []);
|
||||
if (in_array($file->type, ['word', 'excel', 'ppt'])) {
|
||||
if (empty($content)) {
|
||||
return Response::download(public_path('assets/office/empty.' . str_replace(['word', 'excel', 'ppt'], ['docx', 'xlsx', 'pptx'], $file->type)), $name);
|
||||
$filePath = public_path('assets/office/empty.' . str_replace(['word', 'excel', 'ppt'], ['docx', 'xlsx', 'pptx'], $file->type));
|
||||
} else {
|
||||
$filePath = public_path($content['url']);
|
||||
}
|
||||
return Response::download(public_path($content['url']), $name);
|
||||
return Base::streamDownload(function() use ($filePath) {
|
||||
echo file_get_contents($filePath);
|
||||
}, $name);
|
||||
}
|
||||
if (empty($content)) {
|
||||
$content = match ($file->type) {
|
||||
@@ -124,7 +143,9 @@ class FileContent extends AbstractModel
|
||||
if ($download) {
|
||||
$filePath = public_path($path);
|
||||
if (isset($filePath)) {
|
||||
return Response::download($filePath, $name);
|
||||
return Base::streamDownload(function() use ($filePath) {
|
||||
echo file_get_contents($filePath);
|
||||
}, $name);
|
||||
} else {
|
||||
abort(403, "This file not support download.");
|
||||
}
|
||||
|
||||
@@ -51,12 +51,12 @@ class FileLink extends AbstractModel
|
||||
$fileLink = FileLink::createInstance([
|
||||
'file_id' => $fileId,
|
||||
'userid' => $userid,
|
||||
'code' => Base::generatePassword(64),
|
||||
'code' => base64_encode("{$fileId},{$userid}," . Base::generatePassword()),
|
||||
]);
|
||||
$fileLink->save();
|
||||
} else {
|
||||
if ($refresh == 'yes') {
|
||||
$fileLink->code = Base::generatePassword(64);
|
||||
$fileLink->code = base64_encode("{$fileId},{$userid}," . Base::generatePassword());
|
||||
$fileLink->save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,17 +28,17 @@ use Request;
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @property-read int $owner_userid
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectColumn[] $projectColumn
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProjectColumn> $projectColumn
|
||||
* @property-read int|null $project_column_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectLog[] $projectLog
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProjectLog> $projectLog
|
||||
* @property-read int|null $project_log_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectUser[] $projectUser
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProjectUser> $projectUser
|
||||
* @property-read int|null $project_user_count
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project allData($userid = null)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project authData($userid = null, $owner = null)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project newQuery()
|
||||
* @method static \Illuminate\Database\Query\Builder|Project onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project whereArchivedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project whereArchivedUserid($value)
|
||||
@@ -52,8 +52,8 @@ use Request;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project whereUserSimple($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project whereUserid($value)
|
||||
* @method static \Illuminate\Database\Query\Builder|Project withTrashed()
|
||||
* @method static \Illuminate\Database\Query\Builder|Project withoutTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project withTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project withoutTrashed()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Project extends AbstractModel
|
||||
@@ -211,7 +211,7 @@ class Project extends AbstractModel
|
||||
'important' => 1
|
||||
]);
|
||||
}
|
||||
WebSocketDialogUser::whereDialogId($this->dialog_id)->whereNotIn('userid', $userids)->whereImportant(1)->delete();
|
||||
WebSocketDialogUser::whereDialogId($this->dialog_id)->whereNotIn('userid', $userids)->whereImportant(1)->remove();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -251,8 +251,8 @@ class Project extends AbstractModel
|
||||
$this->archived_at = null;
|
||||
$this->archived_userid = User::userid();
|
||||
$this->addLog("项目取消归档");
|
||||
$this->pushMsg('add', $this);
|
||||
ProjectTask::whereProjectId($this->id)->whereArchivedFollow(1)->update([
|
||||
$this->pushMsg('recovery', $this);
|
||||
ProjectTask::whereProjectId($this->id)->whereArchivedFollow(1)->change([
|
||||
'archived_at' => null,
|
||||
'archived_follow' => 0
|
||||
]);
|
||||
@@ -262,7 +262,7 @@ class Project extends AbstractModel
|
||||
$this->archived_userid = User::userid();
|
||||
$this->addLog("项目归档");
|
||||
$this->pushMsg('archived');
|
||||
ProjectTask::whereProjectId($this->id)->whereArchivedAt(null)->update([
|
||||
ProjectTask::whereProjectId($this->id)->whereArchivedAt(null)->change([
|
||||
'archived_at' => $archived_at,
|
||||
'archived_follow' => 1
|
||||
]);
|
||||
@@ -441,7 +441,7 @@ class Project extends AbstractModel
|
||||
});
|
||||
//
|
||||
foreach ($upTaskList as $id => $value) {
|
||||
ProjectTask::whereFlowItemId($id)->update([
|
||||
ProjectTask::whereFlowItemId($id)->change([
|
||||
'flow_item_name' => $value
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -20,11 +20,11 @@ use Request;
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @property-read \App\Models\Project|null $project
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectTask[] $projectTask
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProjectTask> $projectTask
|
||||
* @property-read int|null $project_task_count
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn newQuery()
|
||||
* @method static \Illuminate\Database\Query\Builder|ProjectColumn onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn whereColor($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn whereCreatedAt($value)
|
||||
@@ -34,8 +34,8 @@ use Request;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn whereProjectId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn whereSort($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Query\Builder|ProjectColumn withTrashed()
|
||||
* @method static \Illuminate\Database\Query\Builder|ProjectColumn withoutTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn withTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectColumn withoutTrashed()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ProjectColumn extends AbstractModel
|
||||
|
||||
@@ -12,7 +12,7 @@ use App\Module\Base;
|
||||
* @property string|null $name 流程名称
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectFlowItem[] $projectFlowItem
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProjectFlowItem> $projectFlowItem
|
||||
* @property-read int|null $project_flow_item_count
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow newQuery()
|
||||
|
||||
@@ -81,7 +81,7 @@ class ProjectFlowItem extends AbstractModel
|
||||
*/
|
||||
public function deleteFlowItem()
|
||||
{
|
||||
ProjectTask::whereFlowItemId($this->id)->update([
|
||||
ProjectTask::whereFlowItemId($this->id)->change([
|
||||
'flow_item_id' => 0,
|
||||
'flow_item_name' => "",
|
||||
]);
|
||||
|
||||
@@ -52,18 +52,18 @@ use Request;
|
||||
* @property-read bool $today
|
||||
* @property-read \App\Models\Project|null $project
|
||||
* @property-read \App\Models\ProjectColumn|null $projectColumn
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectTaskFile[] $taskFile
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProjectTaskFile> $taskFile
|
||||
* @property-read int|null $task_file_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectTaskTag[] $taskTag
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProjectTaskTag> $taskTag
|
||||
* @property-read int|null $task_tag_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectTaskUser[] $taskUser
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProjectTaskUser> $taskUser
|
||||
* @property-read int|null $task_user_count
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask allData($userid = null)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask authData($userid = null, $owner = null)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask betweenTime($start, $end, $type = 'taskTime')
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask newQuery()
|
||||
* @method static \Illuminate\Database\Query\Builder|ProjectTask onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereArchivedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereArchivedFollow($value)
|
||||
@@ -92,8 +92,8 @@ use Request;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereStartAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereUserid($value)
|
||||
* @method static \Illuminate\Database\Query\Builder|ProjectTask withTrashed()
|
||||
* @method static \Illuminate\Database\Query\Builder|ProjectTask withoutTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask withTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask withoutTrashed()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ProjectTask extends AbstractModel
|
||||
@@ -530,8 +530,6 @@ class ProjectTask extends AbstractModel
|
||||
public function updateTask($data, &$updateMarking = [])
|
||||
{
|
||||
AbstractModel::transaction(function () use ($data, &$updateMarking) {
|
||||
// 判断版本
|
||||
Base::checkClientVersion('0.19.0');
|
||||
// 主任务
|
||||
$mainTask = $this->parent_id > 0 ? self::find($this->parent_id) : null;
|
||||
// 工作流
|
||||
@@ -628,7 +626,7 @@ class ProjectTask extends AbstractModel
|
||||
if ($this->complete_at) {
|
||||
throw new ApiException('任务已完成');
|
||||
}
|
||||
$this->completeTask(Carbon::now());
|
||||
$this->completeTask(Carbon::now(), isset($newFlowItem) ? $newFlowItem->name : null);
|
||||
} else {
|
||||
// 标记未完成
|
||||
if (!$this->complete_at) {
|
||||
@@ -655,7 +653,7 @@ class ProjectTask extends AbstractModel
|
||||
}
|
||||
// 负责人
|
||||
if (Arr::exists($data, 'owner')) {
|
||||
$count = $this->taskUser->where('owner', 1)->count();
|
||||
$older = $this->taskUser->where('owner', 1)->pluck('userid')->toArray();
|
||||
$array = [];
|
||||
$owner = is_array($data['owner']) ? $data['owner'] : [$data['owner']];
|
||||
if (count($owner) > 10) {
|
||||
@@ -674,16 +672,14 @@ class ProjectTask extends AbstractModel
|
||||
'owner' => 1,
|
||||
]);
|
||||
$array[] = $uid;
|
||||
if ($this->parent_id) {
|
||||
break; // 子任务只能是一个负责人
|
||||
}
|
||||
}
|
||||
if ($array) {
|
||||
if ($count == 0 && count($array) == 1 && $array[0] == User::userid()) {
|
||||
if (count($older) == 0 && count($array) == 1 && $array[0] == User::userid()) {
|
||||
$this->addLog("认领{任务}");
|
||||
} else {
|
||||
$this->addLog("修改{任务}负责人", ['userid' => $array]);
|
||||
}
|
||||
$this->taskPush(array_values(array_diff($array, $older)), 0);
|
||||
}
|
||||
$rows = ProjectTaskUser::whereTaskId($this->id)->whereOwner(1)->whereNotIn('userid', $array)->get();
|
||||
if ($rows->isNotEmpty()) {
|
||||
@@ -763,14 +759,13 @@ class ProjectTask extends AbstractModel
|
||||
});
|
||||
}
|
||||
$newStringAt = $this->start_at ? ($this->start_at->toDateTimeString() . '~' . $this->end_at->toDateTimeString()) : '';
|
||||
$this->addLog("修改{任务}时间" . ($desc ? "(备注:{$desc})" : ""), [
|
||||
$newDesc = $desc ? "(备注:{$desc})" : "";
|
||||
$this->addLog("修改{任务}时间" . $newDesc, [
|
||||
'change' => [$oldStringAt, $newStringAt]
|
||||
]);
|
||||
|
||||
// 修改计划时间需要重置任务邮件提醒日志
|
||||
ProjectTaskMailLog::whereTaskId($this->id)->delete();
|
||||
$this->taskPush(null, 3, $newDesc);
|
||||
}
|
||||
// 以下紧顶级任务可修改
|
||||
// 以下仅顶级任务可修改
|
||||
if ($this->parent_id === 0) {
|
||||
// 重复周期
|
||||
$loopAt = $this->loop_at;
|
||||
@@ -1047,7 +1042,7 @@ class ProjectTask extends AbstractModel
|
||||
'important' => 1
|
||||
]);
|
||||
}
|
||||
WebSocketDialogUser::whereDialogId($this->dialog_id)->whereNotIn('userid', $userids)->whereImportant(1)->delete();
|
||||
WebSocketDialogUser::whereDialogId($this->dialog_id)->whereNotIn('userid', $userids)->whereImportant(1)->remove();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1180,11 +1175,12 @@ class ProjectTask extends AbstractModel
|
||||
/**
|
||||
* 标记已完成、未完成
|
||||
* @param Carbon|null $complete_at 完成时间
|
||||
* @param String $complete_name 已完成名称(留空为:已完成)
|
||||
* @return bool
|
||||
*/
|
||||
public function completeTask($complete_at)
|
||||
public function completeTask($complete_at, $complete_name = null)
|
||||
{
|
||||
AbstractModel::transaction(function () use ($complete_at) {
|
||||
AbstractModel::transaction(function () use ($complete_at, $complete_name) {
|
||||
$addMsg = $this->parent_id == 0 && $this->dialog_id > 0;
|
||||
if ($complete_at === null) {
|
||||
// 标记未完成
|
||||
@@ -1192,7 +1188,7 @@ class ProjectTask extends AbstractModel
|
||||
$this->addLog("标记{任务}未完成");
|
||||
if ($addMsg) {
|
||||
WebSocketDialogMsg::sendMsg(null, $this->dialog_id, 'notice', [
|
||||
'notice' => '标记任务未完成'
|
||||
'notice' => "标记任务未完成"
|
||||
], 0, true, true);
|
||||
}
|
||||
} else {
|
||||
@@ -1205,11 +1201,14 @@ class ProjectTask extends AbstractModel
|
||||
if (!$this->hasOwner()) {
|
||||
throw new ApiException('请先领取任务');
|
||||
}
|
||||
if (empty($complete_name)) {
|
||||
$complete_name = '已完成';
|
||||
}
|
||||
$this->complete_at = $complete_at;
|
||||
$this->addLog("标记{任务}已完成");
|
||||
$this->addLog("标记{任务}{$complete_name}");
|
||||
if ($addMsg) {
|
||||
WebSocketDialogMsg::sendMsg(null, $this->dialog_id, 'notice', [
|
||||
'notice' => '标记任务已完成'
|
||||
'notice' => "标记任务{$complete_name}"
|
||||
], 0, true, true);
|
||||
}
|
||||
}
|
||||
@@ -1256,12 +1255,12 @@ class ProjectTask extends AbstractModel
|
||||
$this->archived_follow = 0;
|
||||
$this->addLog($logText, [], $userid);
|
||||
}
|
||||
$this->pushMsg('update', [
|
||||
$this->pushMsg($archived_at === null ? 'recovery' : 'archived', [
|
||||
'id' => $this->id,
|
||||
'archived_at' => $this->archived_at,
|
||||
'archived_userid' => $this->archived_userid,
|
||||
]);
|
||||
self::whereParentId($this->id)->update([
|
||||
self::whereParentId($this->id)->change([
|
||||
'archived_at' => $this->archived_at,
|
||||
'archived_userid' => $this->archived_userid,
|
||||
'archived_follow' => $this->archived_follow,
|
||||
@@ -1283,7 +1282,7 @@ class ProjectTask extends AbstractModel
|
||||
$dialog = WebSocketDialog::find($this->dialog_id);
|
||||
$dialog?->deleteDialog();
|
||||
}
|
||||
self::whereParentId($this->id)->delete();
|
||||
self::whereParentId($this->id)->remove();
|
||||
$this->deleted_userid = User::userid();
|
||||
$this->save();
|
||||
$this->addLog("删除{任务}");
|
||||
@@ -1300,12 +1299,12 @@ class ProjectTask extends AbstractModel
|
||||
* @param bool $pushMsg 是否推送
|
||||
* @return bool
|
||||
*/
|
||||
public function recoveryTask($pushMsg = true)
|
||||
public function restoreTask($pushMsg = true)
|
||||
{
|
||||
AbstractModel::transaction(function () {
|
||||
if ($this->dialog_id) {
|
||||
$dialog = WebSocketDialog::withTrashed()->find($this->dialog_id);
|
||||
$dialog?->recoveryDialog();
|
||||
$dialog?->restoreDialog();
|
||||
}
|
||||
self::whereParentId($this->id)->withTrashed()->restore();
|
||||
$this->addLog("还原{任务}");
|
||||
@@ -1431,6 +1430,62 @@ class ProjectTask extends AbstractModel
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务提醒
|
||||
* @param $userids
|
||||
* @param int $type 0-新任务、1-即将超时、2-已超时、3-修改时间
|
||||
* @param string $suffix 描述后缀
|
||||
* @return void
|
||||
*/
|
||||
public function taskPush($userids, int $type, string $suffix = "")
|
||||
{
|
||||
if ($userids === null) {
|
||||
$userids = $this->taskUser->pluck('userid')->toArray();
|
||||
}
|
||||
if (empty($userids)) {
|
||||
return;
|
||||
}
|
||||
$owners = $this->taskUser->pluck('owner', 'userid')->toArray();
|
||||
$receivers = User::whereIn('userid', $userids)->whereNull('disable_at')->get();
|
||||
if (empty($receivers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$botUser = User::botGetOrCreate('task-alert');
|
||||
if (empty($botUser)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$taskHtml = "<span class=\"mention task\" data-id=\"{$this->id}\">#{$this->name}</span>";
|
||||
$text = match ($type) {
|
||||
1 => "您的任务 {$taskHtml} 即将超时。",
|
||||
2 => "您的任务 {$taskHtml} 已经超时。",
|
||||
3 => "您的任务 {$taskHtml} 时间已修改。",
|
||||
default => "您有一个新任务 {$taskHtml}。",
|
||||
};
|
||||
|
||||
/** @var User $user */
|
||||
foreach ($receivers as $receiver) {
|
||||
$data = [
|
||||
'type' => $type,
|
||||
'userid' => $receiver->userid,
|
||||
'task_id' => $this->id,
|
||||
];
|
||||
if (in_array($type, [1, 2]) && ProjectTaskPushLog::where($data)->exists()) {
|
||||
continue;
|
||||
}
|
||||
//
|
||||
$replace = $owners[$receiver->userid] ? "您负责的任务" : "您协助的任务";
|
||||
$dialog = WebSocketDialog::checkUserDialog($botUser, $receiver->userid);
|
||||
if ($dialog) {
|
||||
ProjectTaskPushLog::createInstance($data)->save();
|
||||
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', [
|
||||
'text' => str_replace("您的任务", $replace, $text) . $suffix
|
||||
], $botUser->userid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务
|
||||
* @param $task_id
|
||||
|
||||
@@ -61,12 +61,12 @@ class ProjectTaskContent extends AbstractModel
|
||||
{
|
||||
$path = 'uploads/task/content/' . date("Ym") . '/' . $task_id . '/';
|
||||
//
|
||||
preg_match_all("/<img\s+src=\"data:image\/(png|jpg|jpeg);base64,(.*?)\"/s", $content, $matchs);
|
||||
preg_match_all("/<img\s+src=\"data:image\/(png|jpg|jpeg|webp);base64,(.*?)\"/s", $content, $matchs);
|
||||
foreach ($matchs[2] as $key => $text) {
|
||||
$tmpPath = $path . 'attached/';
|
||||
Base::makeDir(public_path($tmpPath));
|
||||
$tmpPath .= md5($text) . "." . $matchs[1][$key];
|
||||
if (file_put_contents(public_path($tmpPath), base64_decode($text))) {
|
||||
if (Base::saveContentImage(public_path($tmpPath), base64_decode($text))) {
|
||||
$paramet = getimagesize(public_path($tmpPath));
|
||||
$content = str_replace($matchs[0][$key], '<img src="{{RemoteURL}}' . $tmpPath . '" original-width="' . $paramet[0] . '" original-height="' . $paramet[1] . '"', $content);
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ class ProjectTaskFile extends AbstractModel
|
||||
if (!isset($this->appendattrs['width'])) {
|
||||
$width = -1;
|
||||
$height = -1;
|
||||
if (in_array($this->ext, ['jpg', 'jpeg', 'gif', 'png'])) {
|
||||
if (in_array($this->ext, ['jpg', 'jpeg', 'webp', 'gif', 'png'])) {
|
||||
$path = public_path($this->getRawOriginal('path'));
|
||||
[$width, $height] = Cache::remember("File::size-" . md5($path), now()->addDays(7), function () use ($path) {
|
||||
$width = -1;
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
|
||||
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
* App\Models\ProjectTaskMailLog
|
||||
*
|
||||
* @property int $id
|
||||
* @property int|null $userid 用户id
|
||||
* @property int|null $task_id 任务id
|
||||
* @property string|null $email 电子邮箱
|
||||
* @property int|null $type 提醒类型:0 任务开始提醒,1 距离到期提醒,2到期超时提醒
|
||||
* @property int|null $is_send 邮件发送是否成功:0否,1是
|
||||
* @property string|null $send_error 邮件发送错误详情
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog newQuery()
|
||||
* @method static \Illuminate\Database\Query\Builder|ProjectTaskMailLog onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereDeletedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereEmail($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereIsSend($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereSendError($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereTaskId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereType($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereUserid($value)
|
||||
* @method static \Illuminate\Database\Query\Builder|ProjectTaskMailLog withTrashed()
|
||||
* @method static \Illuminate\Database\Query\Builder|ProjectTaskMailLog withoutTrashed()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ProjectTaskMailLog extends AbstractModel
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
}
|
||||
@@ -16,18 +16,20 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @property int|null $type 提醒类型:0 任务开始提醒,1 距离到期提醒,2到期超时提醒
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog newQuery()
|
||||
* @method static \Illuminate\Database\Query\Builder|ProjectTaskPushLog onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog whereDeletedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog whereTaskId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog whereType($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog whereUserid($value)
|
||||
* @method static \Illuminate\Database\Query\Builder|ProjectTaskPushLog withTrashed()
|
||||
* @method static \Illuminate\Database\Query\Builder|ProjectTaskPushLog withoutTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog withTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskPushLog withoutTrashed()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ProjectTaskPushLog extends AbstractModel
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Module\Base;
|
||||
|
||||
/**
|
||||
* App\Models\ProjectUser
|
||||
*
|
||||
|
||||
@@ -23,10 +23,10 @@ use JetBrains\PhpStorm\Pure;
|
||||
* @property int $userid
|
||||
* @property string $content
|
||||
* @property string $sign 汇报唯一标识
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ReportReceive[] $Receives
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ReportReceive> $Receives
|
||||
* @property-read int|null $receives_count
|
||||
* @property-read mixed $receives
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\User[] $receivesUser
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\User> $receivesUser
|
||||
* @property-read int|null $receives_user_count
|
||||
* @property-read \App\Models\User|null $sendUser
|
||||
* @method static Builder|Report newModelQuery()
|
||||
|
||||
@@ -17,7 +17,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker newQuery()
|
||||
* @method static \Illuminate\Database\Query\Builder|TaskWorker onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker whereArgs($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker whereCreatedAt($value)
|
||||
@@ -27,8 +27,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker whereStartAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Query\Builder|TaskWorker withTrashed()
|
||||
* @method static \Illuminate\Database\Query\Builder|TaskWorker withoutTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker withTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|TaskWorker withoutTrashed()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class TaskWorker extends AbstractModel
|
||||
|
||||
@@ -155,12 +155,12 @@ class UmengAlias extends AbstractModel
|
||||
$builder->whereUserid($userid);
|
||||
}
|
||||
$builder
|
||||
->orderByDesc('id')
|
||||
->orderByDesc('updated_at')
|
||||
->chunkById(100, function ($datas) use ($array) {
|
||||
$uids = $datas->groupBy('userid');
|
||||
foreach ($uids as $uid => $rows) {
|
||||
$array['badge'] = WebSocketDialogMsgRead::whereUserid($uid)->whereReadAt(null)->count();
|
||||
$lists = $rows->groupBy('platform');
|
||||
$array['badge'] = WebSocketDialogMsgRead::whereUserid($uid)->whereSilence(0)->whereReadAt(null)->count();
|
||||
$lists = $rows->take(5)->groupBy('platform'); // 每个会员最多推送5个别名
|
||||
foreach ($lists as $platform => $list) {
|
||||
$alias = $list->pluck('alias')->implode(',');
|
||||
self::pushMsgToAlias($alias, $platform, $array);
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Models;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Module\Base;
|
||||
use App\Module\Doo;
|
||||
use Cache;
|
||||
use Carbon\Carbon;
|
||||
|
||||
@@ -33,6 +34,7 @@ use Carbon\Carbon;
|
||||
* @property string|null $created_ip 注册IP
|
||||
* @property string|null $disable_at 禁用时间(离职时间)
|
||||
* @property int|null $email_verity 邮箱是否已验证
|
||||
* @property int|null $bot 是否机器人
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Database\Factories\UserFactory factory(...$parameters)
|
||||
@@ -40,6 +42,7 @@ use Carbon\Carbon;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereAz($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereBot($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereChangepass($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedIp($value)
|
||||
@@ -77,20 +80,7 @@ class User extends AbstractModel
|
||||
public static $defaultAvatarMode = 'auto';
|
||||
|
||||
// 基本信息的字段
|
||||
public static $basicField = ['userid', 'email', 'nickname', 'profession', 'department', 'userimg', 'az', 'pinyin', 'line_at', 'disable_at'];
|
||||
|
||||
/**
|
||||
* 更新数据校验
|
||||
* @param array $param
|
||||
*/
|
||||
public function updateInstance(array $param)
|
||||
{
|
||||
parent::updateInstance($param);
|
||||
//
|
||||
if (isset($param['line_at']) && $this->userid) {
|
||||
Cache::put("User::online:" . $this->userid, time(), Carbon::now()->addSeconds(30));
|
||||
}
|
||||
}
|
||||
public static $basicField = ['userid', 'email', 'nickname', 'profession', 'department', 'userimg', 'bot', 'az', 'pinyin', 'line_at', 'disable_at'];
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
@@ -109,17 +99,7 @@ class User extends AbstractModel
|
||||
*/
|
||||
public function getUserimgAttribute($value)
|
||||
{
|
||||
if ($value && !str_contains($value, 'avatar/')) {
|
||||
// 自定义头像
|
||||
return Base::fillUrl($value);
|
||||
} else if (self::$defaultAvatarMode === 'auto') {
|
||||
// 自动生成头像
|
||||
return url("avatar/" . urlencode($this->nickname) . ".png");
|
||||
} else {
|
||||
// 系统默认头像
|
||||
$name = ($this->userid - 1) % 21 + 1;
|
||||
return url("images/avatar/default_{$name}.png");
|
||||
}
|
||||
return self::getAvatar($this->userid, $value, $this->email, $this->nickname);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,21 +137,40 @@ class User extends AbstractModel
|
||||
if (empty($this->department)) {
|
||||
return "";
|
||||
}
|
||||
$list = UserDepartment::select(['id', 'owner_userid', 'name'])->whereIn('id', $this->department)->take(10)->get();
|
||||
$key = "UserDepartment::" . md5(Cache::get("UserDepartment::rand") . '-' . implode(',' , $this->department));
|
||||
$list = Cache::remember($key, now()->addMonth(), function() {
|
||||
$list = UserDepartment::select(['id', 'owner_userid', 'name'])->whereIn('id', $this->department)->take(10)->get();
|
||||
return $list->toArray();
|
||||
});
|
||||
$array = [];
|
||||
foreach ($list as $item) {
|
||||
$array[] = $item->name . ($item->owner_userid === $this->userid ? '(M)' : '');
|
||||
$array[] = $item['name'] . ($item['owner_userid'] === $this->userid ? '(M)' : '');
|
||||
}
|
||||
return implode(', ', $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取机器人所有者
|
||||
* @return int|mixed
|
||||
*/
|
||||
public function getBotOwner()
|
||||
{
|
||||
if (!$this->bot) {
|
||||
return 0;
|
||||
}
|
||||
$key = "BotOwner::" . $this->userid;
|
||||
return Cache::remember($key, now()->addMonth(), function() {
|
||||
return intval(UserBot::whereBotId($this->userid)->value('userid'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否在线
|
||||
* @return bool
|
||||
*/
|
||||
public function getOnlineStatus()
|
||||
{
|
||||
$online = intval(Cache::get("User::online:" . $this->userid, 0));
|
||||
$online = $this->bot || Cache::get("User::online:" . $this->userid) === "on";
|
||||
if ($online) {
|
||||
return true;
|
||||
}
|
||||
@@ -179,9 +178,45 @@ class User extends AbstractModel
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否管理员
|
||||
* 返回是否LDAP用户
|
||||
* @return bool
|
||||
*/
|
||||
public function isLdap()
|
||||
{
|
||||
return in_array('ldap', $this->identity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回是否临时帐号
|
||||
* @return bool
|
||||
*/
|
||||
public function isTemp()
|
||||
{
|
||||
return in_array('temp', $this->identity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回是否禁用帐号(离职)
|
||||
* @return bool
|
||||
*/
|
||||
public function isDisable()
|
||||
{
|
||||
return in_array('disable', $this->identity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回是否管理员
|
||||
* @return bool
|
||||
*/
|
||||
public function isAdmin()
|
||||
{
|
||||
return in_array('admin', $this->identity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否管理员
|
||||
*/
|
||||
public function checkAdmin()
|
||||
{
|
||||
$this->identity('admin');
|
||||
}
|
||||
@@ -259,38 +294,40 @@ class User extends AbstractModel
|
||||
*/
|
||||
public static function reg($email, $password, $other = [])
|
||||
{
|
||||
//邮箱
|
||||
// 邮箱
|
||||
if (!Base::isEmail($email)) {
|
||||
throw new ApiException('请输入正确的邮箱地址');
|
||||
}
|
||||
if (User::email2userid($email) > 0) {
|
||||
$user = self::whereEmail($email)->first();
|
||||
if ($user) {
|
||||
$isRegVerify = Base::settingFind('emailSetting', 'reg_verify') === 'open';
|
||||
$user = self::whereUserid(User::email2userid($email))->first();
|
||||
if ($isRegVerify && $user->email_verity === 0) {
|
||||
UserEmailVerification::userEmailSend($user);
|
||||
throw new ApiException('您的帐号已注册过,请验证邮箱', ['code' => 'email']);
|
||||
}
|
||||
throw new ApiException('邮箱地址已存在');
|
||||
}
|
||||
//密码
|
||||
// 密码
|
||||
self::passwordPolicy($password);
|
||||
//开始注册
|
||||
$encrypt = Base::generatePassword(6);
|
||||
$inArray = [
|
||||
'encrypt' => $encrypt,
|
||||
'email' => $email,
|
||||
'password' => Base::md52($password, $encrypt),
|
||||
'created_ip' => Base::getIp(),
|
||||
];
|
||||
// 开始注册
|
||||
$user = Doo::userCreate($email, $password);
|
||||
if ($other) {
|
||||
$inArray = array_merge($inArray, $other);
|
||||
$user->updateInstance($other);
|
||||
}
|
||||
$user = User::createInstance($inArray);
|
||||
$user->az = Base::getFirstCharter($user->nickname);
|
||||
$user->pinyin = Base::cn2pinyin($user->nickname);
|
||||
$user->created_ip = Base::getIp();
|
||||
if ($user->save()) {
|
||||
$setting = Base::setting('system');
|
||||
$reg_identity = $setting['reg_identity'] ?: 'normal';
|
||||
$all_group_autoin = $setting['all_group_autoin'] ?: 'yes';
|
||||
// 注册临时身份
|
||||
if ($reg_identity === 'temp') {
|
||||
$user->identity = Base::arrayImplode(array_merge(array_diff($user->identity, ['temp']), ['temp']));
|
||||
$user->save();
|
||||
}
|
||||
// 加入全员群组
|
||||
if (Base::settingFind('system', 'all_group_autoin', 'yes') === 'yes') {
|
||||
if ($all_group_autoin === 'yes') {
|
||||
$dialog = WebSocketDialog::whereGroupType('all')->orderByDesc('id')->first();
|
||||
$dialog?->joinGroup($user->userid, 0);
|
||||
}
|
||||
@@ -298,73 +335,6 @@ class User extends AbstractModel
|
||||
return $user->find($user->userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 邮箱获取userid
|
||||
* @param $email
|
||||
* @return int
|
||||
*/
|
||||
public static function email2userid($email)
|
||||
{
|
||||
if (empty($email)) {
|
||||
return 0;
|
||||
}
|
||||
return intval(self::whereEmail($email)->value('userid'));
|
||||
}
|
||||
|
||||
/**
|
||||
* token获取会员userid
|
||||
* @return int
|
||||
*/
|
||||
public static function token2userid()
|
||||
{
|
||||
return self::authFind('userid', Base::getToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* token获取会员邮箱
|
||||
* @return int
|
||||
*/
|
||||
public static function token2email()
|
||||
{
|
||||
return self::authFind('email', Base::getToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* token获取encrypt
|
||||
* @return mixed|string
|
||||
*/
|
||||
public static function token2encrypt()
|
||||
{
|
||||
return self::authFind('encrypt', Base::getToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取token身份信息
|
||||
* @param $find
|
||||
* @param null $token
|
||||
* @return array|mixed|string
|
||||
*/
|
||||
public static function authFind($find, $token = null)
|
||||
{
|
||||
if ($token === null) {
|
||||
$token = Base::getToken();
|
||||
}
|
||||
list($userid, $email, $encrypt, $timestamp) = explode("#$", base64_decode($token) . "#$#$#$#$");
|
||||
$array = [
|
||||
'userid' => intval($userid),
|
||||
'email' => $email ?: '',
|
||||
'encrypt' => $encrypt ?: '',
|
||||
'timestamp' => intval($timestamp),
|
||||
];
|
||||
if (isset($array[$find])) {
|
||||
return $array[$find];
|
||||
}
|
||||
if ($find == 'all') {
|
||||
return $array;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取我的ID
|
||||
* @return int
|
||||
@@ -400,8 +370,7 @@ class User extends AbstractModel
|
||||
{
|
||||
$user = self::authInfo();
|
||||
if (!$user) {
|
||||
$authorization = Base::getToken();
|
||||
if ($authorization) {
|
||||
if (Base::headerOrInput('token')) {
|
||||
throw new ApiException('身份已失效,请重新登录', [], -1);
|
||||
} else {
|
||||
throw new ApiException('请登录后继续...', [], -1);
|
||||
@@ -426,57 +395,49 @@ class User extends AbstractModel
|
||||
if (isset($_A["__static_auth"])) {
|
||||
return $_A["__static_auth"];
|
||||
}
|
||||
$authorization = Base::getToken();
|
||||
if ($authorization) {
|
||||
$authInfo = self::authFind('all', $authorization);
|
||||
if ($authInfo['userid'] > 0) {
|
||||
$loginValid = floatval(Base::settingFind('system', 'loginValid')) ?: 720;
|
||||
$loginValid *= 3600;
|
||||
if ($authInfo['timestamp'] + $loginValid > time()) {
|
||||
$row = self::whereUserid($authInfo['userid'])->whereEmail($authInfo['email'])->whereEncrypt($authInfo['encrypt'])->first();
|
||||
if ($row) {
|
||||
$upArray = [];
|
||||
if (Base::getIp() && $row->line_ip != Base::getIp()) {
|
||||
$upArray['line_ip'] = Base::getIp();
|
||||
}
|
||||
if (Carbon::parse($row->line_at)->addSeconds(30)->lt(Carbon::now())) {
|
||||
$upArray['line_at'] = Carbon::now();
|
||||
}
|
||||
if ($upArray) {
|
||||
$row->updateInstance($upArray);
|
||||
$row->save();
|
||||
}
|
||||
return $_A["__static_auth"] = $row;
|
||||
}
|
||||
}
|
||||
if (Doo::userId() > 0
|
||||
&& !Doo::userExpired()
|
||||
&& $user = self::whereUserid(Doo::userId())->whereEmail(Doo::userEmail())->whereEncrypt(Doo::userEncrypt())->first()) {
|
||||
$upArray = [];
|
||||
if (Base::getIp() && $user->line_ip != Base::getIp()) {
|
||||
$upArray['line_ip'] = Base::getIp();
|
||||
}
|
||||
if (Carbon::parse($user->line_at)->addSeconds(30)->lt(Carbon::now())) {
|
||||
$upArray['line_at'] = Carbon::now();
|
||||
}
|
||||
if ($upArray) {
|
||||
$user->updateInstance($upArray);
|
||||
$user->save();
|
||||
}
|
||||
return $_A["__static_auth"] = $user;
|
||||
}
|
||||
return $_A["__static_auth"] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成token
|
||||
* 生成 token
|
||||
* @param self $userinfo
|
||||
* @param bool $refresh 获取新的token
|
||||
* @return string
|
||||
*/
|
||||
public static function token($userinfo)
|
||||
public static function generateToken($userinfo, $refresh = false)
|
||||
{
|
||||
$userinfo->token = base64_encode($userinfo->userid . '#$' . $userinfo->email . '#$' . $userinfo->encrypt . '#$' . time() . '#$' . Base::generatePassword(6));
|
||||
if (!$refresh) {
|
||||
if (Doo::userId() != $userinfo->userid
|
||||
|| Doo::userEmail() != $userinfo->email
|
||||
|| Doo::userEncrypt() != $userinfo->encrypt) {
|
||||
$refresh = true;
|
||||
}
|
||||
}
|
||||
if ($refresh) {
|
||||
$days = $userinfo->bot ? 0 : max(1, intval(Base::settingFind('system', 'token_valid_days', 15)));
|
||||
$token = Doo::tokenEncode($userinfo->userid, $userinfo->email, $userinfo->encrypt, $days);
|
||||
} else {
|
||||
$token = Doo::userToken();
|
||||
}
|
||||
unset($userinfo->encrypt);
|
||||
unset($userinfo->password);
|
||||
return $userinfo->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断用户权限(身份)
|
||||
* @param $identity
|
||||
* @param $userIdentity
|
||||
* @return bool
|
||||
*/
|
||||
public static function identityRaw($identity, $userIdentity)
|
||||
{
|
||||
$userIdentity = is_array($userIdentity) ? $userIdentity : explode(",", trim($userIdentity, ","));
|
||||
return $identity && in_array($identity, $userIdentity);
|
||||
return $userinfo->token = $token;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -510,8 +471,7 @@ class User extends AbstractModel
|
||||
*/
|
||||
public static function userid2nickname($userid)
|
||||
{
|
||||
$basic = self::userid2basic($userid);
|
||||
return $basic ? $basic->nickname : '';
|
||||
return self::userid2basic($userid)?->nickname ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -538,6 +498,42 @@ class User extends AbstractModel
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取头像
|
||||
* @param $userid
|
||||
* @param $userimg
|
||||
* @param $email
|
||||
* @param $nickname
|
||||
* @return string
|
||||
*/
|
||||
public static function getAvatar($userid, $userimg, $email, $nickname)
|
||||
{
|
||||
// 自定义头像
|
||||
if ($userimg && !str_contains($userimg, 'avatar/')) {
|
||||
return Base::fillUrl($userimg);
|
||||
}
|
||||
// 机器人头像
|
||||
switch ($email) {
|
||||
case 'system-msg@bot.system':
|
||||
return url("images/avatar/default_system.png");
|
||||
case 'task-alert@bot.system':
|
||||
return url("images/avatar/default_task.png");
|
||||
case 'check-in@bot.system':
|
||||
return url("images/avatar/default_checkin.png");
|
||||
case 'anon-msg@bot.system':
|
||||
return url("images/avatar/default_anon.png");
|
||||
case 'bot-manager@bot.system':
|
||||
return url("images/avatar/default_bot.png");
|
||||
}
|
||||
// 生成文字头像
|
||||
if (self::$defaultAvatarMode === 'auto') {
|
||||
return url("avatar/" . urlencode($nickname) . ".png");
|
||||
}
|
||||
// 系统默认头像
|
||||
$name = ($userid - 1) % 21 + 1;
|
||||
return url("images/avatar/default_{$name}.png");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测密码策略是否符合
|
||||
* @param $password
|
||||
@@ -568,4 +564,60 @@ class User extends AbstractModel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取机器人或创建
|
||||
* @param $key
|
||||
* @param $update
|
||||
* @param $userid
|
||||
* @return self|null
|
||||
*/
|
||||
public static function botGetOrCreate($key, $update = [], $userid = 0)
|
||||
{
|
||||
$email = "{$key}@bot.system";
|
||||
$botUser = self::whereEmail($email)->first();
|
||||
if (empty($botUser)) {
|
||||
$botUser = Doo::userCreate($email, Base::generatePassword(32));
|
||||
if (empty($botUser)) {
|
||||
return null;
|
||||
}
|
||||
$botUser->updateInstance([
|
||||
'created_ip' => Base::getIp(),
|
||||
]);
|
||||
$botUser->save();
|
||||
if ($userid > 0) {
|
||||
UserBot::createInstance([
|
||||
'userid' => $userid,
|
||||
'bot_id' => $botUser->userid,
|
||||
])->save();
|
||||
}
|
||||
//
|
||||
switch ($key) {
|
||||
case 'system-msg':
|
||||
$update['nickname'] = '系统消息';
|
||||
break;
|
||||
case 'task-alert':
|
||||
$update['nickname'] = '任务提醒';
|
||||
break;
|
||||
case 'check-in':
|
||||
$update['nickname'] = '签到打卡';
|
||||
break;
|
||||
case 'anon-msg':
|
||||
$update['nickname'] = '匿名消息';
|
||||
break;
|
||||
case 'bot-manager':
|
||||
$update['nickname'] = '机器人管理';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($update) {
|
||||
$botUser->updateInstance($update);
|
||||
if (isset($update['nickname'])) {
|
||||
$botUser->az = Base::getFirstCharter($botUser->nickname);
|
||||
$botUser->pinyin = Base::cn2pinyin($botUser->nickname);
|
||||
}
|
||||
$botUser->save();
|
||||
}
|
||||
return $botUser;
|
||||
}
|
||||
}
|
||||
|
||||
127
app/Models/UserBot.php
Normal file
127
app/Models/UserBot.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Module\Doo;
|
||||
use App\Module\Extranet;
|
||||
use Cache;
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* App\Models\UserBot
|
||||
*
|
||||
* @property int $id
|
||||
* @property int|null $userid 所属人ID
|
||||
* @property int|null $bot_id 机器人ID
|
||||
* @property int|null $clear_day 消息自动清理天数
|
||||
* @property string|null $clear_at 下一次清理时间
|
||||
* @property string|null $webhook_url 消息webhook地址
|
||||
* @property int|null $webhook_num 消息webhook请求次数
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserBot newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserBot newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserBot query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserBot whereBotId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserBot whereClearAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserBot whereClearDay($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserBot whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserBot whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserBot whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserBot whereUserid($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserBot whereWebhookNum($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserBot whereWebhookUrl($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class UserBot extends AbstractModel
|
||||
{
|
||||
|
||||
/**
|
||||
* 机器人菜单
|
||||
* @param $email
|
||||
* @return array|array[]
|
||||
*/
|
||||
public static function quickMsgs($email)
|
||||
{
|
||||
return match ($email) {
|
||||
'check-in@bot.system' => [
|
||||
[
|
||||
'key' => 'checkin',
|
||||
'label' => Doo::translate('我要签到')
|
||||
], [
|
||||
'key' => 'it',
|
||||
'label' => Doo::translate('IT资讯')
|
||||
], [
|
||||
'key' => '36ke',
|
||||
'label' => Doo::translate('36氪')
|
||||
], [
|
||||
'key' => '60s',
|
||||
'label' => Doo::translate('60s读世界')
|
||||
], [
|
||||
'key' => 'joke',
|
||||
'label' => Doo::translate('开心笑话')
|
||||
], [
|
||||
'key' => 'soup',
|
||||
'label' => Doo::translate('心灵鸡汤')
|
||||
]
|
||||
],
|
||||
'anon-msg@bot.system' => [
|
||||
[
|
||||
'key' => 'help',
|
||||
'label' => Doo::translate('使用说明')
|
||||
], [
|
||||
'key' => 'privacy',
|
||||
'label' => Doo::translate('隐私说明')
|
||||
],
|
||||
],
|
||||
'bot-manager@bot.system' => [
|
||||
[
|
||||
'key' => '/help',
|
||||
'label' => Doo::translate('帮助指令')
|
||||
], [
|
||||
'key' => '/api',
|
||||
'label' => Doo::translate('Api接口文档')
|
||||
], [
|
||||
'key' => '/list',
|
||||
'label' => Doo::translate('我的机器人')
|
||||
],
|
||||
],
|
||||
default => [],
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 签到机器人
|
||||
* @param $command
|
||||
* @param $userid
|
||||
* @return string
|
||||
*/
|
||||
public static function checkinBotQuickMsg($command, $userid)
|
||||
{
|
||||
if (Cache::get("UserBot::checkinBotQuickMsg:{$userid}") === "yes") {
|
||||
return "操作频繁!";
|
||||
}
|
||||
Cache::put("UserBot::checkinBotQuickMsg:{$userid}", "yes", Carbon::now()->addSecond());
|
||||
//
|
||||
$text = match ($command) {
|
||||
"checkin" => "暂未开放手动签到。",
|
||||
default => Extranet::checkinBotQuickMsg($command),
|
||||
};
|
||||
return $text ?: '维护中...';
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐私机器人
|
||||
* @param $command
|
||||
* @return string
|
||||
*/
|
||||
public static function anonBotQuickMsg($command)
|
||||
{
|
||||
return match ($command) {
|
||||
"help" => "使用说明:打开你想要发匿名消息的个人对话,点击输入框右边的 ⊕ 号,选择 <u>匿名消息</u> 即可输入你想要发送的匿名消息内容。",
|
||||
"privacy" => "匿名消息将通过 <u>匿名消息(机器人)</u> 发送给对方,不会记录你的身份信息。",
|
||||
default => '',
|
||||
};
|
||||
}
|
||||
}
|
||||
66
app/Models/UserCheckinMac.php
Normal file
66
app/Models/UserCheckinMac.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Module\Base;
|
||||
|
||||
/**
|
||||
* App\Models\UserCheckinMac
|
||||
*
|
||||
* @property int $id
|
||||
* @property int|null $userid 会员id
|
||||
* @property string|null $mac MAC地址
|
||||
* @property string|null $remark 备注
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinMac newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinMac newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinMac query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinMac whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinMac whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinMac whereMac($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinMac whereRemark($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinMac whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinMac whereUserid($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class UserCheckinMac extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* 保存mac地址
|
||||
* @param $userid
|
||||
* @param $array
|
||||
* @return mixed
|
||||
*/
|
||||
public static function saveMac($userid, $array)
|
||||
{
|
||||
return AbstractModel::transaction(function() use ($array, $userid) {
|
||||
$ids = [];
|
||||
$list = [];
|
||||
foreach ($array as $item) {
|
||||
if (self::whereMac($item['mac'])->where('userid', '!=', $userid)->exists()) {
|
||||
throw new ApiException("{$item['mac']} 已被其他成员设置");
|
||||
}
|
||||
$update = [];
|
||||
if ($item['remark']) {
|
||||
$update = [
|
||||
'remark' => $item['remark']
|
||||
];
|
||||
}
|
||||
$row = self::updateInsert([
|
||||
'userid' => $userid,
|
||||
'mac' => $item['mac']
|
||||
], $update);
|
||||
if ($row) {
|
||||
$ids[] = $row->id;
|
||||
$list[] = $row;
|
||||
}
|
||||
}
|
||||
self::whereUserid($userid)->whereNotIn('id', $ids)->delete();
|
||||
//
|
||||
return Base::retSuccess('修改成功', $list);
|
||||
});
|
||||
}
|
||||
}
|
||||
134
app/Models/UserCheckinRecord.php
Normal file
134
app/Models/UserCheckinRecord.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Module\Base;
|
||||
|
||||
/**
|
||||
* App\Models\UserCheckinRecord
|
||||
*
|
||||
* @property int $id
|
||||
* @property int|null $userid 会员id
|
||||
* @property string|null $mac MAC地址
|
||||
* @property string|null $date 签到日期
|
||||
* @property array $times 签到时间
|
||||
* @property int|null $report_time 上报的时间戳
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord whereDate($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord whereMac($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord whereReportTime($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord whereTimes($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|UserCheckinRecord whereUserid($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class UserCheckinRecord extends AbstractModel
|
||||
{
|
||||
|
||||
/**
|
||||
* 签到记录
|
||||
* @param $value
|
||||
* @return array
|
||||
*/
|
||||
public function getTimesAttribute($value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
return Base::json2array($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取签到时间
|
||||
* @param int $userid
|
||||
* @param array $betweenTimes
|
||||
* @return array
|
||||
*/
|
||||
public static function getTimes(int $userid, array $betweenTimes)
|
||||
{
|
||||
$array = [];
|
||||
$records = self::whereUserid($userid)->whereBetween('created_at', $betweenTimes)->orderBy('id')->get();
|
||||
/** @var self $record */
|
||||
foreach ($records as $record) {
|
||||
$times = array_map(function ($time) {
|
||||
return preg_replace("/(\d+):(\d+):\d+$/", "$1:$2", $time);
|
||||
}, $record->times);
|
||||
if (isset($array[$record->date])) {
|
||||
$array[$record->date] = array_merge($array[$record->date], $times);
|
||||
} else {
|
||||
$array[$record->date] = $times;
|
||||
}
|
||||
}
|
||||
//
|
||||
foreach ($array as $date => $times) {
|
||||
$times = array_values(array_filter(array_unique($times)));
|
||||
$inOrder = [];
|
||||
foreach ($times as $key => $time) {
|
||||
$inOrder[$key] = strtotime("2022-01-01 {$time}");
|
||||
}
|
||||
array_multisort($inOrder, SORT_ASC, $times);
|
||||
$array[$date] = $times;
|
||||
}
|
||||
//
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间收集
|
||||
* @param string $data
|
||||
* @param array $times
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public static function atCollect($data, $times)
|
||||
{
|
||||
$sameTimes = array_map(function($time) use ($data) {
|
||||
return [
|
||||
"datetime" => "{$data} {$time}",
|
||||
"timestamp" => strtotime("{$data} {$time}")
|
||||
];
|
||||
}, $times);
|
||||
return collect($sameTimes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 签到时段
|
||||
* @param array $times
|
||||
* @param int $diff 多长未签到算失效(秒)
|
||||
* @return array
|
||||
*/
|
||||
public static function atSection($times, $diff = 3600)
|
||||
{
|
||||
$start = "";
|
||||
$end = "";
|
||||
$array = [];
|
||||
foreach ($times as $time) {
|
||||
$time = preg_replace("/(\d+):(\d+):\d+$/", "$1:$2", $time);
|
||||
if (empty($start)) {
|
||||
$start = $time;
|
||||
continue;
|
||||
}
|
||||
if (empty($end)) {
|
||||
$end = $time;
|
||||
continue;
|
||||
}
|
||||
if (strtotime("2022-01-01 {$time}") - strtotime("2022-01-01 {$end}") > $diff) {
|
||||
$array[] = [$start, $end];
|
||||
$start = $time;
|
||||
$end = "";
|
||||
continue;
|
||||
}
|
||||
$end = $time;
|
||||
}
|
||||
if ($start) {
|
||||
$array[] = [$start, $end];
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
@@ -45,14 +45,7 @@ class UserDelete extends AbstractModel
|
||||
$value['nickname'] = Base::cardFormat($value['email']);
|
||||
}
|
||||
// 头像
|
||||
if ($value['userimg'] && !str_contains($value['userimg'], 'avatar/')) {
|
||||
$value['userimg'] = Base::fillUrl($value['userimg']);
|
||||
} else if (User::$defaultAvatarMode === 'auto') {
|
||||
$value['userimg'] = url("avatar/" . urlencode($value['nickname']) . ".png");
|
||||
} else {
|
||||
$name = ($value['userid'] - 1) % 21 + 1;
|
||||
$value['userimg'] = url("images/avatar/default_{$name}.png");
|
||||
}
|
||||
$value['userimg'] = User::getAvatar($value['userid'], $value['userimg'], $value['email'], $value['nickname']);
|
||||
// 部门
|
||||
$value['department'] = array_filter(is_array($value['department']) ? $value['department'] : Base::explodeInt($value['department']));
|
||||
}
|
||||
|
||||
@@ -127,7 +127,6 @@ class UserDepartment extends AbstractModel
|
||||
$dialog = WebSocketDialog::find($this->dialog_id);
|
||||
if ($dialog) {
|
||||
$dialog->deleteDialog();
|
||||
$dialog->pushMsg("groupDelete");
|
||||
}
|
||||
//
|
||||
$this->delete();
|
||||
|
||||
@@ -4,7 +4,9 @@ namespace App\Models;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Module\Base;
|
||||
use App\Module\Doo;
|
||||
use App\Tasks\PushTask;
|
||||
use Cache;
|
||||
use Carbon\Carbon;
|
||||
use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
@@ -16,17 +18,19 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @property string|null $type 对话类型
|
||||
* @property string|null $group_type 聊天室类型
|
||||
* @property string|null $name 对话名称
|
||||
* @property string $avatar 头像(群)
|
||||
* @property string|null $last_at 最后消息时间
|
||||
* @property int|null $owner_id 群主用户ID
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\WebSocketDialogUser[] $dialogUser
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\WebSocketDialogUser> $dialogUser
|
||||
* @property-read int|null $dialog_user_count
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog newQuery()
|
||||
* @method static \Illuminate\Database\Query\Builder|WebSocketDialog onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereAvatar($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereDeletedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereGroupType($value)
|
||||
@@ -36,14 +40,24 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereOwnerId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereType($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Query\Builder|WebSocketDialog withTrashed()
|
||||
* @method static \Illuminate\Database\Query\Builder|WebSocketDialog withoutTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog withTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog withoutTrashed()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class WebSocketDialog extends AbstractModel
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
* 头像地址
|
||||
* @param $value
|
||||
* @return string
|
||||
*/
|
||||
public function getAvatarAttribute($value)
|
||||
{
|
||||
return $value ? Base::fillUrl($value) : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
@@ -60,33 +74,42 @@ class WebSocketDialog extends AbstractModel
|
||||
*/
|
||||
public function formatData($userid, $hasData = false)
|
||||
{
|
||||
$dialogUserFun = function ($key, $default = null) use ($userid) {
|
||||
$data = Cache::remember("Dialog::formatData-{$this->id}-{$userid}", now()->addSeconds(10), function () use ($userid) {
|
||||
return WebSocketDialogUser::whereDialogId($this->id)->whereUserid($userid)->first()?->toArray();
|
||||
});
|
||||
return $data[$key] ?? $default;
|
||||
};
|
||||
//
|
||||
$time = Carbon::parse($this->user_at ?? $dialogUserFun('updated_at'));
|
||||
$this->top_at = $this->top_at ?? $dialogUserFun('top_at');
|
||||
$this->user_at = $time->toDateTimeString('millisecond');
|
||||
$this->user_ms = $time->valueOf();
|
||||
//
|
||||
if (isset($this->search_msg_id)) {
|
||||
// 最后消息 (搜索预览消息)
|
||||
$this->last_msg = WebSocketDialogMsg::whereDialogId($this->id)->find($this->search_msg_id);
|
||||
$this->last_at = $this->last_msg?->created_at;
|
||||
} else {
|
||||
// 最后消息
|
||||
$this->last_msg = WebSocketDialogMsg::whereDialogId($this->id)->orderByDesc('id')->first();
|
||||
// 未读信息
|
||||
$unreadBuilder = WebSocketDialogMsgRead::whereDialogId($this->id)->whereUserid($userid)->whereReadAt(null);
|
||||
$this->unread = $unreadBuilder->count();
|
||||
$this->mention = 0;
|
||||
$this->last_umid = 0;
|
||||
if ($this->unread > 0) {
|
||||
$this->mention = $unreadBuilder->clone()->whereMention(1)->count();
|
||||
$this->last_umid = intval($unreadBuilder->clone()->orderByDesc('msg_id')->value('msg_id'));
|
||||
}
|
||||
$this->mark_unread = $this->mark_unread ?? WebSocketDialogUser::whereDialogId($this->id)->whereUserid($userid)->value('mark_unread');
|
||||
$this->generateUnread($userid, $hasData);
|
||||
// 未读标记
|
||||
$this->mark_unread = $this->mark_unread ?? $dialogUserFun('mark_unread');
|
||||
// 是否免打扰
|
||||
$this->silence = $this->silence ?? $dialogUserFun('silence');
|
||||
// 对话人数
|
||||
$builder = WebSocketDialogUser::whereDialogId($this->id);
|
||||
$this->people = $builder->count();
|
||||
$this->people = WebSocketDialogUser::whereDialogId($this->id)->count();
|
||||
// 有待办
|
||||
$this->todo_num = WebSocketDialogMsgTodo::whereDialogId($this->id)->whereUserid($userid)->whereDoneAt(null)->count();
|
||||
// 最后消息
|
||||
$this->last_msg = WebSocketDialogMsg::whereDialogId($this->id)->orderByDesc('id')->first();
|
||||
}
|
||||
// 对方信息
|
||||
$this->pinyin = Base::cn2pinyin($this->name);
|
||||
$this->quick_msgs = [];
|
||||
$this->dialog_user = null;
|
||||
$this->group_info = null;
|
||||
$this->top_at = $this->top_at ?? WebSocketDialogUser::whereDialogId($this->id)->whereUserid($userid)->value('top_at');
|
||||
$this->bot = 0;
|
||||
switch ($this->type) {
|
||||
case "user":
|
||||
$dialog_user = WebSocketDialogUser::whereDialogId($this->id)->where('userid', '!=', $userid)->first();
|
||||
@@ -96,6 +119,8 @@ class WebSocketDialog extends AbstractModel
|
||||
$basic = User::userid2basic($dialog_user->userid);
|
||||
if ($basic) {
|
||||
$this->name = $basic->nickname;
|
||||
$this->bot = $basic->getBotOwner();
|
||||
$this->quick_msgs = UserBot::quickMsgs($basic->email);
|
||||
} else {
|
||||
$this->name = 'non-existent';
|
||||
$this->dialog_delete = 1;
|
||||
@@ -123,7 +148,7 @@ class WebSocketDialog extends AbstractModel
|
||||
}
|
||||
break;
|
||||
case 'all':
|
||||
$this->name = Base::Lang('全体成员');
|
||||
$this->name = Doo::translate('全体成员');
|
||||
$this->all_group_mute = Base::settingFind('system', 'all_group_mute');
|
||||
break;
|
||||
}
|
||||
@@ -136,7 +161,41 @@ class WebSocketDialog extends AbstractModel
|
||||
$this->has_file = $msgBuilder->clone()->whereMtype('file')->exists();
|
||||
$this->has_link = $msgBuilder->clone()->whereLink(1)->exists();
|
||||
}
|
||||
$this->pinyin = Base::cn2pinyin($this->name);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成未读数据
|
||||
* @param $userid
|
||||
* @param $positionData
|
||||
* @return $this
|
||||
*/
|
||||
public function generateUnread($userid, $positionData = false)
|
||||
{
|
||||
$builder = WebSocketDialogMsgRead::whereDialogId($this->id)->whereUserid($userid)->whereReadAt(null);
|
||||
$this->unread = $builder->count();
|
||||
$this->mention = $this->unread > 0 ? $builder->clone()->whereMention(1)->count() : 0;
|
||||
if ($positionData) {
|
||||
$array = [];
|
||||
// @我的消息
|
||||
if ($this->mention > 0
|
||||
&& $mention_id = intval($builder->clone()->whereMention(1)->orderByDesc('msg_id')->value('msg_id'))) {
|
||||
$array[] = [
|
||||
'msg_id' => $mention_id,
|
||||
'label' => Doo::translate('@我的消息'),
|
||||
];
|
||||
}
|
||||
// 最早一条未读消息
|
||||
if ($this->unread > 0
|
||||
&& $first_id = intval($builder->clone()->orderBy('msg_id')->value('msg_id'))) {
|
||||
$array[] = [
|
||||
'msg_id' => $first_id,
|
||||
'label' => '{UNREAD}'
|
||||
];
|
||||
}
|
||||
//
|
||||
$this->position_msgs = $array;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -199,8 +258,13 @@ class WebSocketDialog extends AbstractModel
|
||||
/** @var WebSocketDialogUser $item */
|
||||
foreach ($list as $item) {
|
||||
if ($checkDelete) {
|
||||
if ($type === 'remove' && !in_array(User::userid(), [$this->owner_id, $item->inviter])) {
|
||||
throw new ApiException('只有群主或邀请人可以移出成员');
|
||||
if ($type === 'remove') {
|
||||
// 移出时:如果是全员群仅允许管理员操作,其他群仅群主或邀请人可以操作
|
||||
if ($this->group_type === 'all') {
|
||||
User::auth("admin");
|
||||
} elseif (!in_array(User::userid(), [$this->owner_id, $item->inviter])) {
|
||||
throw new ApiException('只有群主或邀请人可以移出成员');
|
||||
}
|
||||
}
|
||||
if ($item->userid == $this->owner_id) {
|
||||
throw new ApiException('群主不可' . $typeDesc);
|
||||
@@ -246,6 +310,7 @@ class WebSocketDialog extends AbstractModel
|
||||
});
|
||||
$this->delete();
|
||||
});
|
||||
$this->pushMsg("groupDelete");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -253,9 +318,10 @@ class WebSocketDialog extends AbstractModel
|
||||
* 还原会话
|
||||
* @return bool
|
||||
*/
|
||||
public function recoveryDialog()
|
||||
public function restoreDialog()
|
||||
{
|
||||
$this->restore();
|
||||
$this->pushMsg("groupRestore");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -290,7 +356,7 @@ class WebSocketDialog extends AbstractModel
|
||||
case 'all':
|
||||
throw new ApiException('当前会话全员禁言');
|
||||
case 'user':
|
||||
if (!User::find($userid)?->isAdmin()) {
|
||||
if (!User::find($userid)?->checkAdmin()) {
|
||||
throw new ApiException('当前会话禁言');
|
||||
}
|
||||
}
|
||||
@@ -314,7 +380,7 @@ class WebSocketDialog extends AbstractModel
|
||||
$name = \DB::table('project_tasks')->where('dialog_id', $this->id)->value('name');
|
||||
break;
|
||||
case 'all':
|
||||
$name = Base::Lang('全体成员');
|
||||
$name = Doo::translate('全体成员');
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -352,6 +418,20 @@ class WebSocketDialog extends AbstractModel
|
||||
Task::deliver($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新对话最后消息时间
|
||||
* @return WebSocketDialogMsg|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|\Illuminate\Database\Query\Builder|object|null
|
||||
*/
|
||||
public function updateMsgLastAt()
|
||||
{
|
||||
$lastMsg = WebSocketDialogMsg::whereDialogId($this->id)->orderByDesc('id')->first();
|
||||
if ($lastMsg) {
|
||||
$this->last_at = $lastMsg->created_at;
|
||||
$this->save();
|
||||
}
|
||||
return $lastMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对话(同时检验对话身份)
|
||||
* @param $dialog_id
|
||||
@@ -422,37 +502,40 @@ class WebSocketDialog extends AbstractModel
|
||||
|
||||
/**
|
||||
* 获取会员对话(没有自动创建)
|
||||
* @param int $userid 会员ID
|
||||
* @param int $userid2 另一个会员ID
|
||||
* @param User $user 发起会话的会员
|
||||
* @param int $receiver 另一个会员ID
|
||||
* @return self|null
|
||||
*/
|
||||
public static function checkUserDialog($userid, $userid2)
|
||||
public static function checkUserDialog($user, $receiver)
|
||||
{
|
||||
if ($userid == $userid2) {
|
||||
$userid2 = 0;
|
||||
if ($user->userid == $receiver) {
|
||||
$receiver = 0;
|
||||
}
|
||||
$dialogUser = self::select(['web_socket_dialogs.*'])
|
||||
->join('web_socket_dialog_users as u1', 'web_socket_dialogs.id', '=', 'u1.dialog_id')
|
||||
->join('web_socket_dialog_users as u2', 'web_socket_dialogs.id', '=', 'u2.dialog_id')
|
||||
->where('u1.userid', $userid)
|
||||
->where('u2.userid', $userid2)
|
||||
->where('u1.userid', $user->userid)
|
||||
->where('u2.userid', $receiver)
|
||||
->where('web_socket_dialogs.type', 'user')
|
||||
->first();
|
||||
if ($dialogUser) {
|
||||
return $dialogUser;
|
||||
}
|
||||
return AbstractModel::transaction(function () use ($userid2, $userid) {
|
||||
if ($receiver > 0 && $user->isTemp()) {
|
||||
throw new ApiException('无法发起会话');
|
||||
}
|
||||
return AbstractModel::transaction(function () use ($receiver, $user) {
|
||||
$dialog = self::createInstance([
|
||||
'type' => 'user',
|
||||
]);
|
||||
$dialog->save();
|
||||
WebSocketDialogUser::createInstance([
|
||||
'dialog_id' => $dialog->id,
|
||||
'userid' => $userid,
|
||||
'userid' => $user->userid,
|
||||
])->save();
|
||||
WebSocketDialogUser::createInstance([
|
||||
'dialog_id' => $dialog->id,
|
||||
'userid' => $userid2,
|
||||
'userid' => $receiver,
|
||||
])->save();
|
||||
return $dialog;
|
||||
});
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use Carbon\Carbon;
|
||||
use App\Module\Base;
|
||||
use App\Tasks\PushTask;
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Tasks\WebSocketDialogMsgTask;
|
||||
use Carbon\Carbon;
|
||||
use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
@@ -38,7 +38,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @property-read \App\Models\WebSocketDialog|null $webSocketDialog
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg newQuery()
|
||||
* @method static \Illuminate\Database\Query\Builder|WebSocketDialogMsg onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg onlyTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereDeletedAt($value)
|
||||
@@ -60,8 +60,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereType($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereUserid($value)
|
||||
* @method static \Illuminate\Database\Query\Builder|WebSocketDialogMsg withTrashed()
|
||||
* @method static \Illuminate\Database\Query\Builder|WebSocketDialogMsg withoutTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg withTrashed()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg withoutTrashed()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class WebSocketDialogMsg extends AbstractModel
|
||||
@@ -125,7 +125,7 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
}
|
||||
$value = Base::json2array($value);
|
||||
if ($this->type === 'file') {
|
||||
$value['type'] = in_array($value['ext'], ['jpg', 'jpeg', 'png', 'gif']) ? 'img' : 'file';
|
||||
$value['type'] = in_array($value['ext'], ['jpg', 'jpeg', 'webp', 'png', 'gif']) ? 'img' : 'file';
|
||||
$value['path'] = Base::fillUrl($value['path']);
|
||||
$value['thumb'] = Base::fillUrl($value['thumb'] ?: Base::extIcon($value['ext']));
|
||||
} else if ($this->type === 'record') {
|
||||
@@ -254,7 +254,7 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
$dialog = WebSocketDialog::find($this->dialog_id);
|
||||
$dialog?->pushMsg('update', $resData);
|
||||
//
|
||||
return Base::retSuccess('sucess', $resData);
|
||||
return Base::retSuccess('success', $resData);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,12 +367,12 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
* 转发消息
|
||||
* @param array|int $dialogids
|
||||
* @param array|int $userids
|
||||
* @param int $sender 发送的会员ID
|
||||
* @param User $user 发送的会员
|
||||
* @return mixed
|
||||
*/
|
||||
public function forwardMsg($dialogids, $userids, $sender)
|
||||
public function forwardMsg($dialogids, $userids, $user)
|
||||
{
|
||||
return AbstractModel::transaction(function() use ($dialogids, $sender, $userids) {
|
||||
return AbstractModel::transaction(function() use ($dialogids, $user, $userids) {
|
||||
$originalMsg = Base::json2array($this->getRawOriginal('msg'));
|
||||
$msgs = [];
|
||||
$already = [];
|
||||
@@ -381,7 +381,7 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
$dialogids = [$dialogids];
|
||||
}
|
||||
foreach ($dialogids as $dialogid) {
|
||||
$res = self::sendMsg(null, $dialogid, $this->type, $originalMsg, $sender);
|
||||
$res = self::sendMsg(null, $dialogid, $this->type, $originalMsg, $user->userid);
|
||||
if (Base::isSuccess($res)) {
|
||||
$msgs[] = $res['data'];
|
||||
$already[] = $dialogid;
|
||||
@@ -396,9 +396,9 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
if (!User::whereUserid($userid)->exists()) {
|
||||
continue;
|
||||
}
|
||||
$dialog = WebSocketDialog::checkUserDialog($sender, $userid);
|
||||
$dialog = WebSocketDialog::checkUserDialog($user, $userid);
|
||||
if ($dialog && !in_array($dialog->id, $already)) {
|
||||
$res = self::sendMsg(null, $dialog->id, $this->type, $originalMsg, $sender);
|
||||
$res = self::sendMsg(null, $dialog->id, $this->type, $originalMsg, $user->userid);
|
||||
if (Base::isSuccess($res)) {
|
||||
$msgs[] = $res['data'];
|
||||
}
|
||||
@@ -413,9 +413,34 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
|
||||
/**
|
||||
* 删除消息
|
||||
* @param array|int $ids
|
||||
* @return void
|
||||
*/
|
||||
public function deleteMsg()
|
||||
public static function deleteMsgs($ids) {
|
||||
$ids = Base::arrayRetainInt(is_array($ids) ? $ids : [$ids], true);
|
||||
AbstractModel::transaction(function() use ($ids) {
|
||||
$dialogIds = WebSocketDialogMsg::select('dialog_id')->whereIn("id", $ids)->distinct()->get()->pluck('dialog_id');
|
||||
$replyIds = WebSocketDialogMsg::select('reply_id')->whereIn("id", $ids)->distinct()->get()->pluck('reply_id');
|
||||
//
|
||||
WebSocketDialogMsgRead::whereIn('msg_id', $ids)->whereNull('read_at')->delete(); // 未阅读记录不需要软删除,直接删除即可
|
||||
WebSocketDialogMsgTodo::whereIn('msg_id', $ids)->delete();
|
||||
self::whereIn('id', $ids)->delete();
|
||||
//
|
||||
$dialogDatas = WebSocketDialog::whereIn('id', $dialogIds)->get();
|
||||
foreach ($dialogDatas as $dialogData) {
|
||||
$dialogData->updateMsgLastAt();
|
||||
}
|
||||
foreach ($replyIds as $id) {
|
||||
self::whereId($id)->update(['reply_num' => self::whereReplyId($id)->count()]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回消息
|
||||
* @return void
|
||||
*/
|
||||
public function withdrawMsg()
|
||||
{
|
||||
$send_dt = Carbon::parse($this->created_at)->addDay();
|
||||
if ($send_dt->lt(Carbon::now())) {
|
||||
@@ -429,16 +454,13 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
self::whereId($this->reply_id)->decrement('reply_num');
|
||||
}
|
||||
//
|
||||
$last_msg = null;
|
||||
if ($this->webSocketDialog) {
|
||||
$last_msg = WebSocketDialogMsg::whereDialogId($this->dialog_id)->orderByDesc('id')->first();
|
||||
$this->webSocketDialog->last_at = $last_msg->created_at;
|
||||
$this->webSocketDialog->save();
|
||||
}
|
||||
//
|
||||
$dialog = WebSocketDialog::find($this->dialog_id);
|
||||
if ($dialog) {
|
||||
$userids = $dialog->dialogUser->pluck('userid')->toArray();
|
||||
$dialogData = $this->webSocketDialog;
|
||||
if ($dialogData) {
|
||||
foreach ($dialogData->dialogUser as $dialogUser) {
|
||||
$dialogUser->updated_at = Carbon::now();
|
||||
$dialogUser->save();
|
||||
}
|
||||
$userids = $dialogData->dialogUser->pluck('userid')->toArray();
|
||||
PushTask::push([
|
||||
'userid' => $userids,
|
||||
'msg' => [
|
||||
@@ -447,7 +469,7 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
'data' => [
|
||||
'id' => $this->id,
|
||||
'dialog_id' => $this->dialog_id,
|
||||
'last_msg' => $last_msg,
|
||||
'last_msg' => $dialogData->updateMsgLastAt(),
|
||||
'update_read' => $deleteRead ? 1 : 0
|
||||
],
|
||||
]
|
||||
@@ -541,8 +563,7 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
$text = preg_replace("/<img\s+class=\"browse\"[^>]*?>/", "[图片]", $text);
|
||||
if (!$preserveHtml) {
|
||||
$text = strip_tags($text);
|
||||
$text = str_replace(" ", " ", $text);
|
||||
$text = str_replace("&", "&", $text);
|
||||
$text = str_replace([" ", "&", "<", ">"], [" ", "&", "<", ">"], $text);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
@@ -556,13 +577,15 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
public static function formatMsg($text, $dialog_id)
|
||||
{
|
||||
@ini_set("pcre.backtrack_limit", 999999999);
|
||||
// 基础处理
|
||||
$text = preg_replace("/<(\/[a-zA-Z]+)\s*>/s", "<$1>", $text);
|
||||
// 图片 [:IMAGE:className:width:height:src:alt:]
|
||||
preg_match_all("/<img\s+src=\"data:image\/(png|jpg|jpeg|gif);base64,(.*?)\"(.*?)>(<\/img>)*/s", $text, $matchs);
|
||||
preg_match_all("/<img\s+src=\"data:image\/(png|jpg|jpeg|webp|gif);base64,(.*?)\"(.*?)>(<\/img>)*/s", $text, $matchs);
|
||||
foreach ($matchs[2] as $key => $base64) {
|
||||
$imagePath = "uploads/chat/" . date("Ym") . "/" . $dialog_id . "/";
|
||||
Base::makeDir(public_path($imagePath));
|
||||
$imagePath .= md5s($base64) . "." . $matchs[1][$key];
|
||||
if (file_put_contents(public_path($imagePath), base64_decode($base64))) {
|
||||
if (Base::saveContentImage(public_path($imagePath), base64_decode($base64))) {
|
||||
$imageSize = getimagesize(public_path($imagePath));
|
||||
if (Base::imgThumb(public_path($imagePath), public_path($imagePath) . "_thumb.jpg", 320, 0)) {
|
||||
$imagePath .= "_thumb.jpg";
|
||||
@@ -593,7 +616,7 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
$imageSize = getimagesize(public_path($imagePath));
|
||||
// 添加后缀
|
||||
if ($imageSize && !str_contains($imagePath, '.')) {
|
||||
preg_match("/^image\/(png|jpg|jpeg|gif)$/", $imageSize['mime'], $matchMine);
|
||||
preg_match("/^image\/(png|jpg|jpeg|webp|gif)$/", $imageSize['mime'], $matchMine);
|
||||
if ($matchMine) {
|
||||
$imageNewPath = $imagePath . "." . $matchMine[1];
|
||||
if (rename(public_path($imagePath), public_path($imageNewPath))) {
|
||||
@@ -616,8 +639,18 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
}
|
||||
}
|
||||
// 其他网络图片
|
||||
preg_match_all("/<img[^>]*?src=([\"'])(.*?\.(png|jpg|jpeg|gif))\\1[^>]*?>/is", $text, $matchs);
|
||||
$imageSaveLocal = Base::settingFind("system", "image_save_local");
|
||||
preg_match_all("/<img[^>]*?src=([\"'])(.*?\.(png|jpg|jpeg|webp|gif))\\1[^>]*?>/is", $text, $matchs);
|
||||
foreach ($matchs[2] as $key => $str) {
|
||||
if ($imageSaveLocal === 'close') {
|
||||
$imageSize = getimagesize($str);
|
||||
if ($imageSize === false) {
|
||||
$imageSize = ["auto", "auto"];
|
||||
}
|
||||
$imagePath = "base64-" . base64_encode($str);
|
||||
$text = str_replace($matchs[0][$key], "[:IMAGE:browse:{$imageSize[0]}:{$imageSize[1]}:{$imagePath}::]", $text);
|
||||
continue;
|
||||
}
|
||||
if (str_starts_with($str, "{{RemoteURL}}")) {
|
||||
$imagePath = Base::leftDelete($str, "{{RemoteURL}}");
|
||||
$imagePath = Base::rightDelete($imagePath, "_thumb.jpg");
|
||||
@@ -636,7 +669,7 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
$image = file_get_contents($str);
|
||||
if (empty($image)) {
|
||||
$text = str_replace($matchs[0][$key], "[:IMAGE:browse:90:90:images/other/imgerr.jpg::]", $text);
|
||||
} else if (file_put_contents(public_path($imagePath), $image)) {
|
||||
} else if (Base::saveContentImage(public_path($imagePath), $image)) {
|
||||
$imageSize = getimagesize(public_path($imagePath));
|
||||
if (Base::imgThumb(public_path($imagePath), public_path($imagePath) . "_thumb.jpg", 320, 0)) {
|
||||
$imagePath .= "_thumb.jpg";
|
||||
@@ -654,15 +687,15 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
$keyId = $matchId[1];
|
||||
if ($matchChar[1] === "~") {
|
||||
if (Base::isNumber($keyId)) {
|
||||
$file = File::permissionFind($keyId);
|
||||
$file = File::permissionFind($keyId, User::auth());
|
||||
if ($file->type == 'folder') {
|
||||
throw new ApiException('文件夹不支持分享');
|
||||
}
|
||||
$fileLink = FileLink::generateLink($file->id, User::userid());
|
||||
$fileLink = $file->getShareLink(User::userid());
|
||||
$keyId = $fileLink['code'];
|
||||
} else {
|
||||
preg_match("/\/single\/file\/(.*?)$/i", $keyId, $match);
|
||||
if ($match && strlen($match[1]) >= 32) {
|
||||
if ($match && strlen($match[1]) >= 8) {
|
||||
$keyId = $match[1];
|
||||
} else {
|
||||
throw new ApiException('文件分享错误');
|
||||
@@ -671,13 +704,24 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
}
|
||||
$text = str_replace($matchs[0][$key], "[:{$matchChar[1]}:{$keyId}:{$matchValye[1]}:]", $text);
|
||||
}
|
||||
// 处理快捷消息
|
||||
preg_match_all("/<span[^>]*?data-quick-key=([\"'])(.*?)\\1[^>]*?>(.*?)<\/span>/is", $text, $matchs);
|
||||
foreach ($matchs[0] as $key => $str) {
|
||||
$quickKey = $matchs[2][$key];
|
||||
$quickLabel = $matchs[3][$key];
|
||||
if ($quickKey && $quickLabel) {
|
||||
$quickKey = str_replace(":", "", $quickKey);
|
||||
$quickLabel = str_replace(":", "", $quickLabel);
|
||||
$text = str_replace($str, "[:QUICK:{$quickKey}:{$quickLabel}:]", $text);
|
||||
}
|
||||
}
|
||||
// 处理链接标签
|
||||
preg_match_all("/<a[^>]*?href=([\"'])(.*?)\\1[^>]*?>([^<]*?)<\/a>/is", $text, $matchs);
|
||||
preg_match_all("/<a[^>]*?href=([\"'])(.*?)\\1[^>]*?>(.*?)<\/a>/is", $text, $matchs);
|
||||
foreach ($matchs[0] as $key => $str) {
|
||||
$herf = $matchs[2][$key];
|
||||
$title = $matchs[3][$key] ?: $herf;
|
||||
preg_match("/\/single\/file\/(.*?)$/i", $title, $match);
|
||||
if ($match && strlen($match[1]) >= 32) {
|
||||
preg_match("/\/single\/file\/(.*?)$/i", strip_tags($title), $match);
|
||||
if ($match && strlen($match[1]) >= 8) {
|
||||
$file = File::select(['files.id', 'files.name', 'files.ext'])->join('file_links as L', 'files.id', '=', 'L.file_id')->where('L.code', $match[1])->first();
|
||||
if ($file && $file->name) {
|
||||
$name = $file->ext ? "{$file->name}.{$file->ext}" : $file->name;
|
||||
@@ -690,11 +734,11 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
$text = str_replace($str, "[:LINK:{$herf}:{$title}:]", $text);
|
||||
}
|
||||
// 文件分享链接
|
||||
preg_match_all("/(https*:\/\/)((\w|=|\?|\.|\/|&|-|:|\+|%|;|#)+)/i", $text, $matchs);
|
||||
preg_match_all("/(https*:\/\/)((\w|=|\?|\.|\/|&|-|:|\+|%|;|#|@|,|!)+)/i", $text, $matchs);
|
||||
if ($matchs) {
|
||||
foreach ($matchs[0] as $str) {
|
||||
preg_match("/\/single\/file\/(.*?)$/i", $str, $match);
|
||||
if ($match && strlen($match[1]) >= 32) {
|
||||
if ($match && strlen($match[1]) >= 8) {
|
||||
$file = File::select(['files.id', 'files.name', 'files.ext'])->join('file_links as L', 'files.id', '=', 'L.file_id')->where('L.code', $match[1])->first();
|
||||
if ($file && $file->name) {
|
||||
$name = $file->ext ? "{$file->name}.{$file->ext}" : $file->name;
|
||||
@@ -706,13 +750,18 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
// 过滤标签
|
||||
$text = strip_tags($text, '<blockquote> <strong> <pre> <ol> <ul> <li> <em> <p> <s> <u> <a>');
|
||||
$text = preg_replace("/\<(blockquote|strong|pre|ol|ul|li|em|p|s|u).*?\>/is", "<$1>", $text); // 不用去除a标签,上面已经处理过了
|
||||
$text = preg_replace("/\[:IMAGE:(.*?):(.*?):(.*?):(.*?):(.*?):\]/i", "<img class=\"$1\" width=\"$2\" height=\"$3\" src=\"{{RemoteURL}}$4\" alt=\"$5\"/>", $text);
|
||||
$text = preg_replace("/\[:@:(.*?):(.*?):\]/i", "<span class=\"mention user\" data-id=\"$1\">@$2</span>", $text);
|
||||
$text = preg_replace("/\[:#:(.*?):(.*?):\]/i", "<span class=\"mention task\" data-id=\"$1\">#$2</span>", $text);
|
||||
$text = preg_replace("/\[:~:(.*?):(.*?):\]/i", "<a class=\"mention file\" href=\"{{RemoteURL}}single/file/$1\" target=\"_blank\">~$2</a>", $text);
|
||||
$text = preg_replace_callback("/\[:LINK:(.*?):(.*?):\]/i", function (array $match) {
|
||||
return "<a href=\"" . base64_decode($match[1]) . "\" target=\"_blank\">" . base64_decode($match[2]) . "</a>";
|
||||
}, $text);
|
||||
$text = preg_replace_callback("/\[:IMAGE:(.*?):(.*?):(.*?):(.*?):(.*?):\]/i", function (array $match) {
|
||||
$wh = $match[2] === 'auto' ? "" : " width=\"{$match[2]}\" height=\"{$match[3]}\"";
|
||||
$src = str_starts_with($match[4], "base64-") ? base64_decode(substr($match[4], 7)) : "{{RemoteURL}}{$match[4]}";
|
||||
return "<img class=\"{$match[1]}\"{$wh} src=\"{$src}\" alt=\"{$match[5]}\"/>";
|
||||
}, $text);
|
||||
$text = preg_replace("/\[:@:(.*?):(.*?):\]/i", "<span class=\"mention user\" data-id=\"$1\">@$2</span>", $text);
|
||||
$text = preg_replace("/\[:#:(.*?):(.*?):\]/i", "<span class=\"mention task\" data-id=\"$1\">#$2</span>", $text);
|
||||
$text = preg_replace("/\[:~:(.*?):(.*?):\]/i", "<a class=\"mention file\" href=\"{{RemoteURL}}single/file/$1\" target=\"_blank\">~$2</a>", $text);
|
||||
$text = preg_replace("/\[:QUICK:(.*?):(.*?):\]/i", "<span data-quick-key=\"$1\">$2</span>", $text);
|
||||
return preg_replace("/^(<p><\/p>)+|(<p><\/p>)+$/i", "", $text);
|
||||
}
|
||||
|
||||
@@ -742,8 +791,15 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
if (str_contains($msg['text'], '<img ')) {
|
||||
$mtype = str_contains($msg['text'], '"emoticon"') ? 'emoticon' : 'image';
|
||||
}
|
||||
preg_match_all("/@([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,6})/i", $msg['text'], $matchs);
|
||||
foreach($matchs[0] as $key => $item) {
|
||||
$aiUser = User::whereEmail($matchs[1][$key])->whereDisableAt(null)->first();
|
||||
if ($aiUser) {
|
||||
$msg['text'] = str_replace($item, "<span class=\"mention user\" data-id=\"{$aiUser->userid}\">@{$aiUser->nickname}</span>", $msg['text']);
|
||||
}
|
||||
}
|
||||
} elseif ($type === 'file') {
|
||||
if (in_array($msg['ext'], ['jpg', 'jpeg', 'png', 'gif'])) {
|
||||
if (in_array($msg['ext'], ['jpg', 'jpeg', 'webp', 'png', 'gif'])) {
|
||||
$mtype = 'image';
|
||||
}
|
||||
}
|
||||
@@ -812,6 +868,7 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
$dialogMsg->send = 1;
|
||||
$dialogMsg->key = $dialogMsg->generateMsgKey();
|
||||
$dialogMsg->save();
|
||||
WebSocketDialogUser::whereDialogId($dialog->id)->change(['updated_at' => Carbon::now()->toDateTimeString('millisecond')]);
|
||||
});
|
||||
//
|
||||
$task = new WebSocketDialogMsgTask($dialogMsg->id);
|
||||
|
||||
@@ -12,6 +12,7 @@ use Carbon\Carbon;
|
||||
* @property int|null $msg_id 消息ID
|
||||
* @property int|null $userid 接收会员ID
|
||||
* @property int|null $mention 是否提及(被@)
|
||||
* @property int|null $silence 是否免打扰:0否,1是
|
||||
* @property int|null $email 是否发了邮件
|
||||
* @property int|null $after 在阅读之后才添加的记录
|
||||
* @property string|null $read_at 阅读时间
|
||||
@@ -26,6 +27,7 @@ use Carbon\Carbon;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereMention($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereMsgId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereReadAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereSilence($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsgRead whereUserid($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* App\Models\WebSocketDialogUser
|
||||
*
|
||||
@@ -10,10 +12,12 @@ namespace App\Models;
|
||||
* @property int|null $userid 会员ID
|
||||
* @property string|null $top_at 置顶时间
|
||||
* @property int|null $mark_unread 是否标记为未读:0否,1是
|
||||
* @property int|null $silence 是否免打扰:0否,1是
|
||||
* @property int|null $inviter 邀请人
|
||||
* @property int|null $important 是否不可移出(项目、任务、部门人员)
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property-read \App\Models\WebSocketDialog|null $webSocketDialog
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser query()
|
||||
@@ -23,6 +27,7 @@ namespace App\Models;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereImportant($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereInviter($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereMarkUnread($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereSilence($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereTopAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereUserid($value)
|
||||
@@ -30,5 +35,13 @@ namespace App\Models;
|
||||
*/
|
||||
class WebSocketDialogUser extends AbstractModel
|
||||
{
|
||||
protected $dateFormat = 'Y-m-d H:i:s.v';
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function webSocketDialog(): \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
{
|
||||
return $this->hasOne(WebSocketDialog::class, 'id', 'dialog_id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,10 @@ use App\Exceptions\ApiException;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Tmp;
|
||||
use Cache;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Overtrue\Pinyin\Pinyin;
|
||||
use Redirect;
|
||||
use Request;
|
||||
use Response;
|
||||
use Storage;
|
||||
use Validator;
|
||||
|
||||
@@ -76,6 +75,16 @@ class Base
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果header没有则通过input读取
|
||||
* @param $key
|
||||
* @return mixed|string
|
||||
*/
|
||||
public static function headerOrInput($key)
|
||||
{
|
||||
return Base::nullShow(Request::header($key), Request::input($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本号
|
||||
* @return string
|
||||
@@ -94,7 +103,7 @@ class Base
|
||||
{
|
||||
global $_A;
|
||||
if (!isset($_A["__static_client_version"])) {
|
||||
$_A["__static_client_version"] = Request::header('version') ?: '0.0.1';
|
||||
$_A["__static_client_version"] = self::headerOrInput('version') ?: '0.0.1';
|
||||
}
|
||||
return $_A["__static_client_version"];
|
||||
}
|
||||
@@ -106,11 +115,21 @@ class Base
|
||||
*/
|
||||
public static function checkClientVersion($min)
|
||||
{
|
||||
if (version_compare(Base::getClientVersion(), $min, '<')) {
|
||||
if (!self::judgeClientVersion($min)) {
|
||||
throw new ApiException('当前版本 (v' . Base::getClientVersion() . ') 过低,最低版本要求 (v' . $min . ')。');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断客户端版本
|
||||
* @param $min
|
||||
* @return bool
|
||||
*/
|
||||
public static function judgeClientVersion($min)
|
||||
{
|
||||
return !version_compare(Base::getClientVersion(), $min, '<');
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否域名格式
|
||||
* @param $domain
|
||||
@@ -907,6 +926,27 @@ class Base
|
||||
return array_values($array);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组拼接字符串(前后也加上)
|
||||
* @param $glue
|
||||
* @param $pieces
|
||||
* @param $around
|
||||
* @return string
|
||||
*/
|
||||
public static function arrayImplode($glue = "", $pieces = null, $around = true)
|
||||
{
|
||||
if ($pieces == null) {
|
||||
$pieces = $glue;
|
||||
$glue = ',';
|
||||
}
|
||||
$pieces = array_values(array_filter(array_unique($pieces)));
|
||||
$string = implode($glue, $pieces);
|
||||
if ($around && $string) {
|
||||
$string = ",{$string},";
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否二维数组
|
||||
* @param $array
|
||||
@@ -1022,6 +1062,20 @@ class Base
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 正则判断是否MAC地址
|
||||
* @param $str
|
||||
* @return bool
|
||||
*/
|
||||
public static function isMac($str)
|
||||
{
|
||||
if (preg_match("/^[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}$/", $str)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断身份证是否正确
|
||||
* @param $id
|
||||
@@ -1256,6 +1310,27 @@ class Base
|
||||
return $setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间转毫秒时间戳
|
||||
* @param $time
|
||||
* @return float|int
|
||||
*/
|
||||
public static function strtotimeM($time)
|
||||
{
|
||||
if (str_contains($time, '.')) {
|
||||
list($t, $m) = explode(".", $time);
|
||||
if (is_string($t)) {
|
||||
$t = strtotime($t);
|
||||
}
|
||||
$time = $t . str_pad($m, 3, "0", STR_PAD_LEFT);
|
||||
}
|
||||
if (is_numeric($time)) {
|
||||
return (int) str_pad($time, 13, "0");
|
||||
} else {
|
||||
return strtotime($time) * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设置值
|
||||
* @param $setname
|
||||
@@ -1383,54 +1458,6 @@ class Base
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 国际化(替换国际语言)
|
||||
* @param $val
|
||||
* @return mixed
|
||||
*/
|
||||
public static function Lang($val)
|
||||
{
|
||||
$repArray = [];
|
||||
if (is_array($val)) {
|
||||
if (self::strExists($val[0], '%') && count($val) > 1) {
|
||||
$repArray = array_slice($val, 1);
|
||||
}
|
||||
$val = $val[0];
|
||||
}
|
||||
$data = self::langData();
|
||||
if (isset($data[$val]) && $data[$val] !== null) {
|
||||
$val = $data[$val];
|
||||
}
|
||||
if ($repArray) {
|
||||
foreach ($repArray as $item) {
|
||||
$val = self::strReplaceLimit('%', $item, $val, 1);
|
||||
}
|
||||
}
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载语言数据
|
||||
* @param bool $refresh
|
||||
* @return array
|
||||
*/
|
||||
public static function langData($refresh = false)
|
||||
{
|
||||
global $_A;
|
||||
if (!isset($_A["__static_langdata"]) || $refresh === true) {
|
||||
$_A["__static_langdata"] = [];
|
||||
$language = trim(Request::header('language'));
|
||||
$langpath = resource_path('lang/' . $language . '/general.php');
|
||||
if (file_exists($langpath)) {
|
||||
$data = include $langpath;
|
||||
if (is_array($data)) {
|
||||
$_A["__static_langdata"] = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $_A["__static_langdata"];
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON返回
|
||||
* @param $param
|
||||
@@ -1438,10 +1465,8 @@ class Base
|
||||
*/
|
||||
public static function jsonEcho($param)
|
||||
{
|
||||
global $_GPC;
|
||||
//
|
||||
$json = json_encode($param);
|
||||
$callback = $_GPC['callback'];
|
||||
$callback = Request::input('callback');
|
||||
if ($callback) {
|
||||
return $callback . '(' . $json . ')';
|
||||
} else {
|
||||
@@ -1458,11 +1483,11 @@ class Base
|
||||
*/
|
||||
public static function retSuccess($msg, $data = [], $ret = 1)
|
||||
{
|
||||
return array(
|
||||
return [
|
||||
'ret' => $ret,
|
||||
'msg' => self::Lang($msg),
|
||||
'msg' => Doo::translate($msg),
|
||||
'data' => $data
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1474,11 +1499,11 @@ class Base
|
||||
*/
|
||||
public static function retError($msg, $data = [], $ret = 0)
|
||||
{
|
||||
return array(
|
||||
return [
|
||||
'ret' => $ret,
|
||||
'msg' => self::Lang($msg),
|
||||
'msg' => Doo::translate($msg),
|
||||
'data' => $data
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1814,116 +1839,11 @@ class Base
|
||||
$onlineip = '0,0,0,0';
|
||||
}
|
||||
preg_match("/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/", $onlineip, $match);
|
||||
$_A["__static_ip"] = $match[0] ?: 'unknown';
|
||||
$_A["__static_ip"] = $match ? ($match[0] ?: 'unknown') : '';
|
||||
}
|
||||
return $_A["__static_ip"];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取IP地址经纬度
|
||||
* @param string $ip
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function getIpGcj02($ip = '')
|
||||
{
|
||||
if (empty($ip)) {
|
||||
$ip = self::getIp();
|
||||
}
|
||||
$cacheKey = "getIpPoint::" . md5($ip);
|
||||
$result = Cache::rememberForever($cacheKey, function () use ($ip) {
|
||||
return Ihttp::ihttp_request("https://www.ifreesite.com/ipaddress/address.php?q=" . $ip, [], [], 12);
|
||||
});
|
||||
if (Base::isError($result)) {
|
||||
Cache::forget($cacheKey);
|
||||
return $result;
|
||||
}
|
||||
$data = $result['data'];
|
||||
$lastPos = strrpos($data, ',');
|
||||
$long = floatval(Base::getMiddle(substr($data, $lastPos + 1), null, ')'));
|
||||
$lat = floatval(Base::getMiddle(substr($data, strrpos(substr($data, 0, $lastPos), ',') + 1), null, ','));
|
||||
return Base::retSuccess("success", [
|
||||
'long' => $long,
|
||||
'lat' => $lat,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 百度接口:根据ip获取经纬度
|
||||
* @param string $ip
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function getIpGcj02ByBaidu($ip = ''): array
|
||||
{
|
||||
if (empty($ip)) {
|
||||
$ip = self::getIp();
|
||||
}
|
||||
|
||||
$cacheKey = "getIpPoint::" . md5($ip);
|
||||
$result = Cache::rememberForever($cacheKey, function () use ($ip) {
|
||||
$ak = Config::get('app.baidu_app_key');
|
||||
$url = 'http://api.map.baidu.com/location/ip?ak=' . $ak . '&ip=' . $ip . '&coor=bd09ll';
|
||||
return Ihttp::ihttp_request($url, [], [], 12);
|
||||
});
|
||||
|
||||
if (Base::isError($result)) {
|
||||
Cache::forget($cacheKey);
|
||||
return $result;
|
||||
}
|
||||
$data = json_decode($result['data'], true);
|
||||
|
||||
// x坐标纬度, y坐标经度
|
||||
$long = Arr::get($data, 'content.point.x');
|
||||
$lat = Arr::get($data, 'content.point.y');
|
||||
return Base::retSuccess("success", [
|
||||
'long' => $long,
|
||||
'lat' => $lat,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取IP地址详情
|
||||
* @param string $ip
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function getIpInfo($ip = '')
|
||||
{
|
||||
if (empty($ip)) {
|
||||
$ip = self::getIp();
|
||||
}
|
||||
$cacheKey = "getIpInfo::" . md5($ip);
|
||||
$result = Cache::rememberForever($cacheKey, function () use ($ip) {
|
||||
return Ihttp::ihttp_request("http://ip.taobao.com/service/getIpInfo.php?accessKey=alibaba-inc&ip=" . $ip, [], [], 12);
|
||||
});
|
||||
if (Base::isError($result)) {
|
||||
Cache::forget($cacheKey);
|
||||
return $result;
|
||||
}
|
||||
$data = json_decode($result['data'], true);
|
||||
if (!is_array($data) || intval($data['code']) != 0) {
|
||||
Cache::forget($cacheKey);
|
||||
return Base::retError("error ip: -1");
|
||||
}
|
||||
$data = $data['data'];
|
||||
if (!is_array($data) || !isset($data['country'])) {
|
||||
return Base::retError("error ip: -2");
|
||||
}
|
||||
$data['text'] = $data['country'];
|
||||
$data['textSmall'] = $data['country'];
|
||||
if ($data['region'] && $data['region'] != $data['country'] && $data['region'] != "XX") {
|
||||
$data['text'] .= " " . $data['region'];
|
||||
$data['textSmall'] = $data['region'];
|
||||
}
|
||||
if ($data['city'] && $data['city'] != $data['region'] && $data['city'] != "XX") {
|
||||
$data['text'] .= " " . $data['city'];
|
||||
$data['textSmall'] .= " " . $data['city'];
|
||||
}
|
||||
if ($data['county'] && $data['county'] != $data['city'] && $data['county'] != "XX") {
|
||||
$data['text'] .= " " . $data['county'];
|
||||
$data['textSmall'] .= " " . $data['county'];
|
||||
}
|
||||
return Base::retSuccess("success", $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是中国IP:-1错误、1是、0否
|
||||
* @param string $ip
|
||||
@@ -2013,60 +1933,6 @@ class Base
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* php://input 字符串解析到变量并获取指定值
|
||||
* @param $key
|
||||
* @return array
|
||||
*/
|
||||
public static function getContentsParse($key)
|
||||
{
|
||||
parse_str(Request::getContent(), $input);
|
||||
if ($key) {
|
||||
$input = $input[$key] ?? array();
|
||||
}
|
||||
return is_array($input) ? $input : array($input);
|
||||
}
|
||||
|
||||
/**
|
||||
* php://input 字符串解析到变量并获取指定值
|
||||
* @param $key
|
||||
* @param null $default
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function getContentValue($key, $default = null)
|
||||
{
|
||||
global $_A;
|
||||
if (!isset($_A["__static_input_content"])) {
|
||||
parse_str(Request::getContent(), $input);
|
||||
$_A["__static_input_content"] = $input;
|
||||
}
|
||||
return $_A["__static_input_content"][$key] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param null $default
|
||||
* @return array|mixed|string|null
|
||||
*/
|
||||
public static function getPostValue($key, $default = null)
|
||||
{
|
||||
$value = self::getContentValue($key, $default);
|
||||
if (!isset($value)) {
|
||||
$value = Request::post($key, $default);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param null $default
|
||||
* @return int
|
||||
*/
|
||||
public static function getPostInt($key, $default = null)
|
||||
{
|
||||
return intval(self::getPostValue($key, $default));
|
||||
}
|
||||
|
||||
/**
|
||||
* 多维 array_values
|
||||
* @param $array
|
||||
@@ -2099,29 +1965,6 @@ class Base
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取tonken
|
||||
* @return string
|
||||
*/
|
||||
public static function getToken()
|
||||
{
|
||||
global $_A;
|
||||
if (!isset($_A["__static_token"])) {
|
||||
$_A["__static_token"] = Base::nullShow(Request::header('token'), Request::input('token'));
|
||||
}
|
||||
return $_A["__static_token"];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置tonken
|
||||
* @param $token
|
||||
*/
|
||||
public static function setToken($token)
|
||||
{
|
||||
global $_A;
|
||||
$_A["__static_token"] = $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否微信
|
||||
* @return bool
|
||||
@@ -2147,6 +1990,27 @@ class Base
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台类型
|
||||
* @return string
|
||||
*/
|
||||
public static function platform()
|
||||
{
|
||||
$platform = strtolower(trim(Request::header('platform')));
|
||||
if (in_array($platform, ['android', 'ios', 'win', 'mac', 'web'])) {
|
||||
return $platform;
|
||||
}
|
||||
$agent = strtolower(Request::server('HTTP_USER_AGENT'));
|
||||
if (str_contains($agent, 'android')) {
|
||||
$platform = 'android';
|
||||
} elseif (str_contains($agent, 'iphone') || str_contains($agent, 'ipad')) {
|
||||
$platform = 'ios';
|
||||
} else {
|
||||
$platform = 'unknown';
|
||||
}
|
||||
return $platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回根据距离sql排序语句
|
||||
* @param $lat
|
||||
@@ -2213,7 +2077,7 @@ class Base
|
||||
|
||||
/**
|
||||
* image64图片保存
|
||||
* @param array $param [ image64=带前缀的base64, path=>文件路径, fileName=>文件名称, scale=>[压缩原图宽,高, 压缩方式], autoThumb=>false不要自动生成缩略图 ]
|
||||
* @param array $param [ image64=带前缀的base64, path=>文件路径, fileName=>文件名称, scale=>[压缩原图宽,高, 压缩方式], autoThumb=>false不要自动生成缩略图, 'compress'=>是否压缩图片(默认true) ]
|
||||
* @return array [name=>文件名, size=>文件大小(单位KB),file=>绝对地址, path=>相对地址, url=>全路径地址, ext=>文件后缀名]
|
||||
*/
|
||||
public static function image64save($param)
|
||||
@@ -2221,7 +2085,7 @@ class Base
|
||||
$imgBase64 = $param['image64'];
|
||||
if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $imgBase64, $res)) {
|
||||
$extension = $res[2];
|
||||
if (!in_array($extension, ['png', 'jpg', 'jpeg', 'gif'])) {
|
||||
if (!in_array($extension, ['png', 'jpg', 'jpeg', 'webp', 'gif'])) {
|
||||
return Base::retError('图片格式错误');
|
||||
}
|
||||
$scaleName = "";
|
||||
@@ -2292,6 +2156,11 @@ class Base
|
||||
}
|
||||
}
|
||||
}
|
||||
// 压缩图片
|
||||
if ($param['compress'] !== false) {
|
||||
ImgCompress::compress($array['file']);
|
||||
$array['size'] = Base::twoFloat(filesize($array['file']) / 1024, true);
|
||||
}
|
||||
//生成缩略图
|
||||
$array['thumb'] = $array['path'];
|
||||
if ($extension === 'gif' && !isset($param['autoThumb'])) {
|
||||
@@ -2311,12 +2180,13 @@ class Base
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param array $param [ type=[文件类型], file=>Request::file, path=>文件路径, fileName=>文件名称, scale=>[压缩原图宽,高, 压缩方式], size=>限制大小KB, autoThumb=>false不要自动生成缩略图 ]
|
||||
* @param array $param [ type=[文件类型], file=>Request::file, path=>文件路径, fileName=>文件名称, scale=>[压缩原图宽,高, 压缩方式], size=>限制大小KB, autoThumb=>false不要自动生成缩略图, chmod=>权限(默认0644), 'compress'=>是否压缩图片(默认true) ]
|
||||
* @return array [name=>原文件名, size=>文件大小(单位KB),file=>绝对地址, path=>相对地址, url=>全路径地址, ext=>文件后缀名]
|
||||
*/
|
||||
public static function upload($param)
|
||||
{
|
||||
$file = $param['file'];
|
||||
$chmod = $param['chmod'] ?: 0644;
|
||||
if (empty($file)) {
|
||||
return Base::retError("您没有选择要上传的文件");
|
||||
}
|
||||
@@ -2327,11 +2197,8 @@ class Base
|
||||
case 'png':
|
||||
$type = ['png'];
|
||||
break;
|
||||
case 'png+jpg':
|
||||
$type = ['jpg', 'jpeg', 'png'];
|
||||
break;
|
||||
case 'image':
|
||||
$type = ['jpg', 'jpeg', 'gif', 'png'];
|
||||
$type = ['jpg', 'jpeg', 'webp', 'gif', 'png'];
|
||||
break;
|
||||
case 'video':
|
||||
$type = ['rm', 'rmvb', 'wmv', 'avi', 'mpg', 'mpeg', 'mp4'];
|
||||
@@ -2349,7 +2216,7 @@ class Base
|
||||
$type = ['zip'];
|
||||
break;
|
||||
case 'file':
|
||||
$type = ['jpg', 'jpeg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'esp', 'pdf', 'rar', 'zip', 'gz', 'ai', 'avi', 'bmp', 'cdr', 'eps', 'mov', 'mp3', 'mp4', 'pr', 'psd', 'svg', 'tif'];
|
||||
$type = ['jpg', 'jpeg', 'webp', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'esp', 'pdf', 'rar', 'zip', 'gz', 'ai', 'avi', 'bmp', 'cdr', 'eps', 'mov', 'mp3', 'mp4', 'pr', 'psd', 'svg', 'tif'];
|
||||
break;
|
||||
case 'firmware':
|
||||
$type = ['img', 'tar', 'bin'];
|
||||
@@ -2361,30 +2228,7 @@ class Base
|
||||
$type = ['yml', 'yaml', 'dmg', 'pkg', 'blockmap', 'zip', 'exe', 'msi'];
|
||||
break;
|
||||
case 'more':
|
||||
$type = [
|
||||
'text', 'md', 'markdown',
|
||||
'drawio',
|
||||
'mind',
|
||||
'docx', 'wps', 'doc', 'xls', 'xlsx', 'ppt', 'pptx',
|
||||
'jpg', 'jpeg', 'png', 'gif', 'bmp', 'ico', 'raw', 'svg',
|
||||
'rar', 'zip', 'jar', '7-zip', 'tar', 'gzip', '7z', 'gz', 'apk', 'dmg',
|
||||
'tif', 'tiff',
|
||||
'dwg', 'dxf',
|
||||
'ofd',
|
||||
'pdf',
|
||||
'txt',
|
||||
'htaccess', 'htgroups', 'htpasswd', 'conf', 'bat', 'cmd', 'cpp', 'c', 'cc', 'cxx', 'h', 'hh', 'hpp', 'ino', 'cs', 'css',
|
||||
'dockerfile', 'go', 'golang', 'html', 'htm', 'xhtml', 'vue', 'we', 'wpy', 'java', 'js', 'jsm', 'jsx', 'json', 'jsp', 'less', 'lua', 'makefile', 'gnumakefile',
|
||||
'ocamlmakefile', 'make', 'mysql', 'nginx', 'ini', 'cfg', 'prefs', 'm', 'mm', 'pl', 'pm', 'p6', 'pl6', 'pm6', 'pgsql', 'php',
|
||||
'inc', 'phtml', 'shtml', 'php3', 'php4', 'php5', 'phps', 'phpt', 'aw', 'ctp', 'module', 'ps1', 'py', 'r', 'rb', 'ru', 'gemspec', 'rake', 'guardfile', 'rakefile',
|
||||
'gemfile', 'rs', 'sass', 'scss', 'sh', 'bash', 'bashrc', 'sql', 'sqlserver', 'swift', 'ts', 'typescript', 'str', 'vbs', 'vb', 'v', 'vh', 'sv', 'svh', 'xml',
|
||||
'rdf', 'rss', 'wsdl', 'xslt', 'atom', 'mathml', 'mml', 'xul', 'xbl', 'xaml', 'yaml', 'yml',
|
||||
'asp', 'properties', 'gitignore', 'log', 'bas', 'prg', 'python', 'ftl', 'aspx', 'plist',
|
||||
'mp3', 'wav', 'mp4', 'flv',
|
||||
'avi', 'mov', 'wmv', 'mkv', '3gp', 'rm',
|
||||
'xmind',
|
||||
'rp',
|
||||
];
|
||||
$type = []; // 不限制上传文件类型
|
||||
break;
|
||||
default:
|
||||
return Base::retError('错误的类型参数');
|
||||
@@ -2416,26 +2260,32 @@ class Base
|
||||
}
|
||||
}
|
||||
}
|
||||
$fileName = md5_file($file) . '.' . $extension;
|
||||
$scaleName = md5_file($file) . $scaleName . '.' . $extension;
|
||||
$fileName = md5_file($file);
|
||||
$scaleName = md5_file($file) . $scaleName;
|
||||
if ($extension) {
|
||||
$fileName = $fileName . '.' . $extension;
|
||||
$scaleName = $scaleName . '.' . $extension;
|
||||
}
|
||||
}
|
||||
//
|
||||
$file->move(public_path($param['path']), $fileName);
|
||||
//
|
||||
$path = $param['path'] . $fileName;
|
||||
$array = [
|
||||
"name" => $file->getClientOriginalName(), //原文件名
|
||||
"size" => Base::twoFloat($fileSize / 1024, true), //大小KB
|
||||
"file" => public_path($param['path'] . $fileName), //文件的完整路径 "D:\www....KzZ.jpg"
|
||||
"path" => $param['path'] . $fileName, //相对路径 "uploads/pic....KzZ.jpg"
|
||||
"url" => Base::fillUrl($param['path'] . $fileName), //完整的URL "https://.....hhsKzZ.jpg"
|
||||
"thumb" => '', //缩略图(预览图) "https://.....hhsKzZ.jpg_thumb.jpg"
|
||||
"width" => -1, //图片宽度
|
||||
"height" => -1, //图片高度
|
||||
"ext" => $extension, //文件后缀名
|
||||
"name" => $file->getClientOriginalName(), //原文件名
|
||||
"size" => Base::twoFloat($fileSize / 1024, true), //大小KB
|
||||
"file" => public_path($path), //文件的完整路径 "D:\www....KzZ.jpg"
|
||||
"path" => $path, //相对路径 "uploads/pic....KzZ.jpg"
|
||||
"url" => Base::fillUrl($path), //完整的URL "https://.....hhsKzZ.jpg"
|
||||
"thumb" => '', //缩略图(预览图) "https://.....hhsKzZ.jpg_thumb.jpg"
|
||||
"width" => -1, //图片宽度
|
||||
"height" => -1, //图片高度
|
||||
"ext" => $extension, //文件后缀名
|
||||
];
|
||||
if (!is_file($array['file'])) {
|
||||
return Base::retError('上传失败');
|
||||
}
|
||||
@chmod($array['file'], $chmod);
|
||||
//iOS照片颠倒处理
|
||||
if (in_array($extension, ['jpg', 'jpeg']) && function_exists('exif_read_data')) {
|
||||
$data = imagecreatefromstring(file_get_contents($array['file']));
|
||||
@@ -2453,7 +2303,7 @@ class Base
|
||||
}
|
||||
}
|
||||
//
|
||||
if (in_array($extension, ['jpg', 'jpeg', 'gif', 'png'])) {
|
||||
if (in_array($extension, ['jpg', 'jpeg', 'webp', 'gif', 'png'])) {
|
||||
//图片尺寸
|
||||
$paramet = getimagesize($array['file']);
|
||||
$array['width'] = $paramet[0];
|
||||
@@ -2502,6 +2352,11 @@ class Base
|
||||
}
|
||||
$array['thumb'] = Base::fillUrl($array['thumb']);
|
||||
}
|
||||
// 压缩图片
|
||||
if ($param['compress'] !== false) {
|
||||
ImgCompress::compress($array['file']);
|
||||
$array['size'] = Base::twoFloat(filesize($array['file']) / 1024, true);
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('success', $array);
|
||||
} else {
|
||||
@@ -2559,7 +2414,7 @@ class Base
|
||||
$dst_img = $src_img;
|
||||
}
|
||||
$st = pathinfo($src_img, PATHINFO_EXTENSION);
|
||||
if (!in_array(strtolower($st), array('jpg', 'jpeg', 'png', 'gif', 'bmp'))) {
|
||||
if (!in_array(strtolower($st), array('jpg', 'jpeg', 'webp', 'png', 'gif', 'bmp'))) {
|
||||
return false;
|
||||
}
|
||||
$ot = pathinfo($dst_img, PATHINFO_EXTENSION);
|
||||
@@ -2653,7 +2508,7 @@ class Base
|
||||
}
|
||||
|
||||
$src = $createfun($src_img);
|
||||
$dst = imagecreatetruecolor($width ? $width : $dst_w, $height ? $height : $dst_h);
|
||||
$dst = imagecreatetruecolor($width ?: $dst_w, $height ?: $dst_h);
|
||||
try {
|
||||
$white = imagecolorallocate($dst, 255, 255, 255);
|
||||
imagefill($dst, 0, 0, $white);
|
||||
@@ -3100,4 +2955,34 @@ class Base
|
||||
throw new ApiException($validator->errors()->first());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 流下载,解决没有后缀无法下载的问题
|
||||
* @param $callback
|
||||
* @param $name
|
||||
* @return mixed
|
||||
*/
|
||||
public static function streamDownload($callback, $name = null) {
|
||||
if ($name && !str_contains($name, '.')) {
|
||||
$name .= ".";
|
||||
}
|
||||
return Response::streamDownload($callback, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存图片到文件(同时压缩)
|
||||
* @param $path
|
||||
* @param $content
|
||||
* @param $compress
|
||||
* @return bool
|
||||
*/
|
||||
public static function saveContentImage($path, $content, $compress = true) {
|
||||
if (file_put_contents($path, $content)) {
|
||||
if ($compress) {
|
||||
ImgCompress::compress($path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,12 @@ use PhpOffice\PhpSpreadsheet\Writer\Exception;
|
||||
|
||||
class BillExport implements WithHeadings, WithEvents, FromCollection, WithTitle, WithStrictNullComparison
|
||||
{
|
||||
public $title;
|
||||
public $headings = [];
|
||||
public $data = [];
|
||||
public $typeLists = [];
|
||||
public $typeNumber = 0;
|
||||
protected $title;
|
||||
protected $headings = [];
|
||||
protected $data = [];
|
||||
protected $typeLists = [];
|
||||
protected $typeNumber = 0;
|
||||
protected $styles = [];
|
||||
|
||||
public function __construct($title, array $data)
|
||||
{
|
||||
@@ -57,15 +58,19 @@ class BillExport implements WithHeadings, WithEvents, FromCollection, WithTitle,
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setStyles(array $styles)
|
||||
{
|
||||
$this->styles = $styles;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function store($fileName = '') {
|
||||
if (empty($fileName)) {
|
||||
$fileName = date("YmdHis") . '.xls';
|
||||
}
|
||||
try {
|
||||
return Excel::store($this, $fileName);
|
||||
} catch (Exception $e) {
|
||||
return "导出错误:" . $e->getMessage();
|
||||
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
|
||||
} catch (Exception|\PhpOffice\PhpSpreadsheet\Exception $e) {
|
||||
return "导出错误:" . $e->getMessage();
|
||||
}
|
||||
}
|
||||
@@ -76,9 +81,7 @@ class BillExport implements WithHeadings, WithEvents, FromCollection, WithTitle,
|
||||
}
|
||||
try {
|
||||
return Excel::download($this, $fileName);
|
||||
} catch (Exception $e) {
|
||||
return "导出错误:" . $e->getMessage();
|
||||
} catch (\PhpOffice\PhpSpreadsheet\Exception $e) {
|
||||
} catch (Exception|\PhpOffice\PhpSpreadsheet\Exception $e) {
|
||||
return "导出错误:" . $e->getMessage();
|
||||
}
|
||||
}
|
||||
@@ -118,6 +121,11 @@ class BillExport implements WithHeadings, WithEvents, FromCollection, WithTitle,
|
||||
{
|
||||
return [
|
||||
AfterSheet::Class => function (AfterSheet $event) {
|
||||
if ($this->styles) {
|
||||
foreach ($this->styles as $cell => $style) {
|
||||
$event->sheet->getDelegate()->getStyle($cell)->applyFromArray($style);
|
||||
}
|
||||
}
|
||||
$count = count($this->data);
|
||||
foreach ($this->typeLists AS $cell => $typeList) {
|
||||
if ($cell && $typeList) {
|
||||
|
||||
24
app/Module/BillMultipleExport.php
Normal file
24
app/Module/BillMultipleExport.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Module;
|
||||
|
||||
|
||||
use Maatwebsite\Excel\Concerns\Exportable;
|
||||
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
|
||||
|
||||
class BillMultipleExport implements WithMultipleSheets
|
||||
{
|
||||
use Exportable;
|
||||
|
||||
protected $data = [];
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function sheets(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
}
|
||||
419
app/Module/Doo.php
Normal file
419
app/Module/Doo.php
Normal file
@@ -0,0 +1,419 @@
|
||||
<?php
|
||||
|
||||
namespace App\Module;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Models\User;
|
||||
use Cache;
|
||||
use Carbon\Carbon;
|
||||
use FFI;
|
||||
|
||||
class Doo
|
||||
{
|
||||
private static $doo;
|
||||
private static $passphrase = "LYHevk5n";
|
||||
|
||||
/**
|
||||
* char转为字符串
|
||||
* @param $text
|
||||
* @return string
|
||||
*/
|
||||
private static function string($text): string
|
||||
{
|
||||
return FFI::string($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 装载
|
||||
* @param $token
|
||||
* @param $language
|
||||
*/
|
||||
public static function load($token = null, $language = null)
|
||||
{
|
||||
self::$doo = FFI::cdef(<<<EOF
|
||||
void initialize(char* work, char* token, char* lang);
|
||||
char* license();
|
||||
char* licenseDecode(char* license);
|
||||
char* licenseSave(char* license);
|
||||
int userId();
|
||||
char* userExpiredAt();
|
||||
char* userEmail();
|
||||
char* userEncrypt();
|
||||
char* userToken();
|
||||
char* userCreate(char* email, char* password);
|
||||
char* tokenEncode(int userid, char* email, char* encrypt, int days);
|
||||
char* tokenDecode(char* val);
|
||||
char* translate(char* val, char* val);
|
||||
char* md5s(char* text, char* password);
|
||||
char* macs();
|
||||
char* dooSN();
|
||||
char* pgpGenerateKeyPair(char* name, char* email, char* passphrase);
|
||||
char* pgpEncrypt(char* plainText, char* publicKey);
|
||||
char* pgpDecrypt(char* cipherText, char* privateKey, char* passphrase);
|
||||
EOF, "/usr/lib/doo/doo.so");
|
||||
$token = $token ?: Base::headerOrInput('token');
|
||||
$language = $language ?: Base::headerOrInput('language');
|
||||
self::$doo->initialize("/var/www", $token, $language);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实例
|
||||
* @param $token
|
||||
* @param $language
|
||||
* @return mixed
|
||||
*/
|
||||
public static function doo($token = null, $language = null)
|
||||
{
|
||||
if (self::$doo == null) {
|
||||
self::load($token, $language);
|
||||
}
|
||||
return self::$doo;
|
||||
}
|
||||
|
||||
/**
|
||||
* License
|
||||
* @return array
|
||||
*/
|
||||
public static function license(): array
|
||||
{
|
||||
$array = Base::json2array(self::string(self::doo()->license()));
|
||||
|
||||
$ips = explode(",", $array['ip']);
|
||||
$array['ip'] = [];
|
||||
foreach ($ips as $ip) {
|
||||
if (Base::is_ipv4($ip)) {
|
||||
$array['ip'][] = $ip;
|
||||
}
|
||||
}
|
||||
|
||||
$domains = explode(",", $array['domain']);
|
||||
$array['domain'] = [];
|
||||
foreach ($domains as $domain) {
|
||||
if (Base::is_domain($domain)) {
|
||||
$array['domain'][] = $domain;
|
||||
}
|
||||
}
|
||||
|
||||
$macs = explode(",", $array['mac']);
|
||||
$array['mac'] = [];
|
||||
foreach ($macs as $mac) {
|
||||
if (Base::isMac($mac)) {
|
||||
$array['mac'][] = $mac;
|
||||
}
|
||||
}
|
||||
|
||||
$emails = explode(",", $array['email']);
|
||||
$array['email'] = [];
|
||||
foreach ($emails as $email) {
|
||||
if (Base::isEmail($email)) {
|
||||
$array['email'][] = $email;
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取License原文
|
||||
* @return string
|
||||
*/
|
||||
public static function licenseContent(): string
|
||||
{
|
||||
if (env("SYSTEM_LICENSE") == 'hidden') {
|
||||
return '';
|
||||
}
|
||||
$paths = [
|
||||
config_path("LICENSE"),
|
||||
config_path("license"),
|
||||
app_path("LICENSE"),
|
||||
app_path("license"),
|
||||
];
|
||||
$content = "";
|
||||
foreach ($paths as $path) {
|
||||
if (file_exists($path)) {
|
||||
$content = file_get_contents($path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析License
|
||||
* @param $license
|
||||
* @return array
|
||||
*/
|
||||
public static function licenseDecode($license): array
|
||||
{
|
||||
return Base::json2array(self::string(self::doo()->licenseDecode($license)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存License
|
||||
* @param $license
|
||||
*/
|
||||
public static function licenseSave($license): void
|
||||
{
|
||||
$res = self::string(self::doo()->licenseSave($license));
|
||||
if ($res != 'success') {
|
||||
throw new ApiException($res ?: 'LICENSE 保存失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会员ID(来自请求的token)
|
||||
* @return int
|
||||
*/
|
||||
public static function userId(): int
|
||||
{
|
||||
return intval(self::doo()->userId());
|
||||
}
|
||||
|
||||
/**
|
||||
* token是否过期(来自请求的token)
|
||||
* @return bool
|
||||
*/
|
||||
public static function userExpired(): bool
|
||||
{
|
||||
$expiredAt = self::userExpiredAt();
|
||||
return $expiredAt && Carbon::parse($expiredAt)->isBefore(Carbon::now());
|
||||
}
|
||||
|
||||
/**
|
||||
* token过期时间(来自请求的token)
|
||||
* @return string
|
||||
*/
|
||||
public static function userExpiredAt(): string
|
||||
{
|
||||
$expiredAt = self::string(self::doo()->userExpiredAt());
|
||||
return $expiredAt === 'forever' ? '' : $expiredAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会员邮箱地址(来自请求的token)
|
||||
* @return string
|
||||
*/
|
||||
public static function userEmail(): string
|
||||
{
|
||||
return self::string(self::doo()->userEmail());
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会员Encrypt(来自请求的token)
|
||||
* @return string
|
||||
*/
|
||||
public static function userEncrypt(): string
|
||||
{
|
||||
return self::string(self::doo()->userEncrypt());
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前会员token(来自请求的token)
|
||||
* @return string
|
||||
*/
|
||||
public static function userToken(): string
|
||||
{
|
||||
return self::string(self::doo()->userToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建帐号
|
||||
* @param $email
|
||||
* @param $password
|
||||
* @return User|null
|
||||
*/
|
||||
public static function userCreate($email, $password): User|null
|
||||
{
|
||||
$data = Base::json2array(self::string(self::doo()->userCreate($email, $password)));
|
||||
if (Base::isError($data)) {
|
||||
throw new ApiException($data['msg'] ?: '注册失败');
|
||||
}
|
||||
$user = User::whereEmail($email)->first();
|
||||
if (empty($user)) {
|
||||
throw new ApiException('注册失败');
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成token(编码token)
|
||||
* @param $userid
|
||||
* @param $email
|
||||
* @param $encrypt
|
||||
* @param int $days 有效时间(天)
|
||||
* @return string
|
||||
*/
|
||||
public static function tokenEncode($userid, $email, $encrypt, int $days = 15): string
|
||||
{
|
||||
return self::string(self::doo()->tokenEncode($userid, $email, $encrypt, $days));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码token
|
||||
* @param $token
|
||||
* @return array
|
||||
*/
|
||||
public static function tokenDecode($token): array
|
||||
{
|
||||
return Base::json2array(self::string(self::doo()->tokenDecode($token)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译
|
||||
* @param $text
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
public static function translate($text, string $type = ""): string
|
||||
{
|
||||
return self::string(self::doo()->translate($text, $type));
|
||||
}
|
||||
|
||||
/**
|
||||
* md5防破解
|
||||
* @param $text
|
||||
* @param string $password
|
||||
* @return string
|
||||
*/
|
||||
public static function md5s($text, string $password = ""): string
|
||||
{
|
||||
return self::string(self::doo()->md5s($text, $password));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取php容器mac地址组
|
||||
* @return array
|
||||
*/
|
||||
public static function macs(): array
|
||||
{
|
||||
$macs = explode(",", self::string(self::doo()->macs()));
|
||||
$array = [];
|
||||
foreach ($macs as $mac) {
|
||||
if (Base::isMac($mac)) {
|
||||
$array[] = $mac;
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前SN
|
||||
* @return string
|
||||
*/
|
||||
public static function dooSN(): string
|
||||
{
|
||||
return self::string(self::doo()->dooSN());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成PGP密钥对
|
||||
* @param $name
|
||||
* @param $email
|
||||
* @param string $passphrase
|
||||
* @return array
|
||||
*/
|
||||
public static function pgpGenerateKeyPair($name, $email, string $passphrase = ""): array
|
||||
{
|
||||
return Base::json2array(self::string(self::doo()->pgpGenerateKeyPair($name, $email, $passphrase)));
|
||||
}
|
||||
|
||||
/**
|
||||
* PGP加密
|
||||
* @param $plaintext
|
||||
* @param $publicKey
|
||||
* @return string
|
||||
*/
|
||||
public static function pgpEncrypt($plaintext, $publicKey): string
|
||||
{
|
||||
if (strlen($publicKey) < 50) {
|
||||
$keyCache = Base::json2array(Cache::get("KeyPair::" . $publicKey));
|
||||
$publicKey = $keyCache['public_key'];
|
||||
}
|
||||
return self::string(self::doo()->pgpEncrypt($plaintext, $publicKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* PGP解密
|
||||
* @param $encryptedText
|
||||
* @param $privateKey
|
||||
* @param null $passphrase
|
||||
* @return string
|
||||
*/
|
||||
public static function pgpDecrypt($encryptedText, $privateKey, $passphrase = null): string
|
||||
{
|
||||
if (strlen($privateKey) < 50) {
|
||||
$keyCache = Base::json2array(Cache::get("KeyPair::" . $privateKey));
|
||||
$privateKey = $keyCache['private_key'];
|
||||
$passphrase = $keyCache['passphrase'];
|
||||
}
|
||||
return self::string(self::doo()->pgpDecrypt($encryptedText, $privateKey, $passphrase));
|
||||
}
|
||||
|
||||
/**
|
||||
* PGP加密API
|
||||
* @param $plaintext
|
||||
* @param $publicKey
|
||||
* @return string
|
||||
*/
|
||||
public static function pgpEncryptApi($plaintext, $publicKey): string
|
||||
{
|
||||
$content = Base::array2json($plaintext);
|
||||
$content = self::pgpEncrypt($content, $publicKey);
|
||||
return preg_replace("/\s*-----(BEGIN|END) PGP MESSAGE-----\s*/i", "", $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* PGP解密API
|
||||
* @param $encryptedText
|
||||
* @param null $privateKey
|
||||
* @param null $passphrase
|
||||
* @return array
|
||||
*/
|
||||
public static function pgpDecryptApi($encryptedText, $privateKey, $passphrase = null): array
|
||||
{
|
||||
$content = "-----BEGIN PGP MESSAGE-----\n\n" . $encryptedText . "\n-----END PGP MESSAGE-----";
|
||||
$content = self::pgpDecrypt($content, $privateKey, $passphrase);
|
||||
return Base::json2array($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析PGP参数
|
||||
* @param $string
|
||||
* @return string[]
|
||||
*/
|
||||
public static function pgpParseStr($string): array
|
||||
{
|
||||
$array = [
|
||||
'encrypt_type' => '',
|
||||
'encrypt_id' => '',
|
||||
'client_type' => '',
|
||||
'client_key' => '',
|
||||
];
|
||||
$string = str_replace(";", "&", $string);
|
||||
parse_str($string, $params);
|
||||
foreach ($params as $key => $value) {
|
||||
$key = strtolower(trim($key));
|
||||
if ($key) {
|
||||
$array[$key] = trim($value);
|
||||
}
|
||||
}
|
||||
if ($array['client_type'] === 'pgp' && $array['client_key']) {
|
||||
$array['client_key'] = self::pgpPublicFormat($array['client_key']);
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 还原公钥格式
|
||||
* @param $key
|
||||
* @return string
|
||||
*/
|
||||
public static function pgpPublicFormat($key): string
|
||||
{
|
||||
$key = str_replace(["-", "_", "$"], ["+", "/", "\n"], $key);
|
||||
if (!str_contains($key, '-----BEGIN PGP PUBLIC KEY BLOCK-----')) {
|
||||
$key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n" . $key . "\n-----END PGP PUBLIC KEY BLOCK-----";
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
321
app/Module/Extranet.php
Normal file
321
app/Module/Extranet.php
Normal file
@@ -0,0 +1,321 @@
|
||||
<?php
|
||||
|
||||
namespace App\Module;
|
||||
|
||||
use Cache;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* 外网资源请求
|
||||
*/
|
||||
class Extranet
|
||||
{
|
||||
/**
|
||||
* 获取IP地址经纬度
|
||||
* @param string $ip
|
||||
* @return array
|
||||
*/
|
||||
public static function getIpGcj02(string $ip = ''): array
|
||||
{
|
||||
if (empty($ip)) {
|
||||
$ip = Base::getIp();
|
||||
}
|
||||
$cacheKey = "getIpPoint::" . md5($ip);
|
||||
$result = Cache::rememberForever($cacheKey, function () use ($ip) {
|
||||
return Ihttp::ihttp_request("https://www.ifreesite.com/ipaddress/address.php?q=" . $ip, [], [], 12);
|
||||
});
|
||||
if (Base::isError($result)) {
|
||||
Cache::forget($cacheKey);
|
||||
return $result;
|
||||
}
|
||||
$data = $result['data'];
|
||||
$lastPos = strrpos($data, ',');
|
||||
$long = floatval(Base::getMiddle(substr($data, $lastPos + 1), null, ')'));
|
||||
$lat = floatval(Base::getMiddle(substr($data, strrpos(substr($data, 0, $lastPos), ',') + 1), null, ','));
|
||||
return Base::retSuccess("success", [
|
||||
'long' => $long,
|
||||
'lat' => $lat,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 百度接口:根据ip获取经纬度
|
||||
* @param string $ip
|
||||
* @return array
|
||||
*/
|
||||
public static function getIpGcj02ByBaidu(string $ip = ''): array
|
||||
{
|
||||
if (empty($ip)) {
|
||||
$ip = Base::getIp();
|
||||
}
|
||||
|
||||
$cacheKey = "getIpPoint::" . md5($ip);
|
||||
$result = Cache::rememberForever($cacheKey, function () use ($ip) {
|
||||
$ak = Config::get('app.baidu_app_key');
|
||||
$url = 'http://api.map.baidu.com/location/ip?ak=' . $ak . '&ip=' . $ip . '&coor=bd09ll';
|
||||
return Ihttp::ihttp_request($url, [], [], 12);
|
||||
});
|
||||
|
||||
if (Base::isError($result)) {
|
||||
Cache::forget($cacheKey);
|
||||
return $result;
|
||||
}
|
||||
$data = json_decode($result['data'], true);
|
||||
|
||||
// x坐标纬度, y坐标经度
|
||||
$long = Arr::get($data, 'content.point.x');
|
||||
$lat = Arr::get($data, 'content.point.y');
|
||||
return Base::retSuccess("success", [
|
||||
'long' => $long,
|
||||
'lat' => $lat,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取IP地址详情
|
||||
* @param string $ip
|
||||
* @return array
|
||||
*/
|
||||
public static function getIpInfo(string $ip = ''): array
|
||||
{
|
||||
if (empty($ip)) {
|
||||
$ip = Base::getIp();
|
||||
}
|
||||
$cacheKey = "getIpInfo::" . md5($ip);
|
||||
$result = Cache::rememberForever($cacheKey, function () use ($ip) {
|
||||
return Ihttp::ihttp_request("http://ip.taobao.com/service/getIpInfo.php?accessKey=alibaba-inc&ip=" . $ip, [], [], 12);
|
||||
});
|
||||
if (Base::isError($result)) {
|
||||
Cache::forget($cacheKey);
|
||||
return $result;
|
||||
}
|
||||
$data = json_decode($result['data'], true);
|
||||
if (!is_array($data) || intval($data['code']) != 0) {
|
||||
Cache::forget($cacheKey);
|
||||
return Base::retError("error ip: -1");
|
||||
}
|
||||
$data = $data['data'];
|
||||
if (!is_array($data) || !isset($data['country'])) {
|
||||
return Base::retError("error ip: -2");
|
||||
}
|
||||
$data['text'] = $data['country'];
|
||||
$data['textSmall'] = $data['country'];
|
||||
if ($data['region'] && $data['region'] != $data['country'] && $data['region'] != "XX") {
|
||||
$data['text'] .= " " . $data['region'];
|
||||
$data['textSmall'] = $data['region'];
|
||||
}
|
||||
if ($data['city'] && $data['city'] != $data['region'] && $data['city'] != "XX") {
|
||||
$data['text'] .= " " . $data['city'];
|
||||
$data['textSmall'] .= " " . $data['city'];
|
||||
}
|
||||
if ($data['county'] && $data['county'] != $data['city'] && $data['county'] != "XX") {
|
||||
$data['text'] .= " " . $data['county'];
|
||||
$data['textSmall'] .= " " . $data['county'];
|
||||
}
|
||||
return Base::retSuccess("success", $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否工作日
|
||||
* @param string $Ymd 年月日(如:20220102)
|
||||
* @return int
|
||||
* 0: 工作日
|
||||
* 1: 非工作日
|
||||
* 2: 获取不到远程数据的非工作日(周六、日)
|
||||
* 所以可以用>0来判断是否工作日
|
||||
*/
|
||||
public static function isHoliday(string $Ymd): int
|
||||
{
|
||||
$time = strtotime($Ymd . " 00:00:00");
|
||||
$holidayKey = "holiday::" . date("Ym", $time);
|
||||
$holidayData = Cache::remember($holidayKey, now()->addMonth(), function () use ($time) {
|
||||
$apiMonth = date("Ym", $time);
|
||||
$apiResult = Ihttp::ihttp_request("https://api.apihubs.cn/holiday/get?field=date&month={$apiMonth}&workday=2&size=31", [], [], 20);
|
||||
if (Base::isError($apiResult)) {
|
||||
info('[holiday] get error');
|
||||
return [];
|
||||
}
|
||||
$apiResult = Base::json2array($apiResult['data']);
|
||||
if ($apiResult['code'] !== 0) {
|
||||
info('[holiday] result error');
|
||||
return [];
|
||||
}
|
||||
return array_map(function ($item) {
|
||||
return $item['date'];
|
||||
}, $apiResult['data']['list']);
|
||||
});
|
||||
if (empty($holidayData)) {
|
||||
Cache::forget($holidayKey);
|
||||
return in_array(date("w", $time), [0, 6]) ? 2 : 0;
|
||||
}
|
||||
return in_array($Ymd, $holidayData) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drawio 图标搜索
|
||||
* @param $query
|
||||
* @param $page
|
||||
* @param $size
|
||||
* @return array
|
||||
*/
|
||||
public static function drawioIconSearch($query, $page, $size): array
|
||||
{
|
||||
$result = self::curl("https://app.diagrams.net/iconSearch?q={$query}&p={$page}&c={$size}", 15 * 86400);
|
||||
if ($result = Base::json2array($result)) {
|
||||
return $result;
|
||||
}
|
||||
return [
|
||||
'icons' => [],
|
||||
'total_count' => 0
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机笑话接口
|
||||
* @return array
|
||||
*/
|
||||
public static function randJoke(): array
|
||||
{
|
||||
$jokeKey = env("JUKE_KEY_JOKE");
|
||||
if ($jokeKey) {
|
||||
$data = self::curl("http://v.juhe.cn/joke/randJoke.php?key=" . $jokeKey);
|
||||
$data = Base::json2array($data);
|
||||
if ($data['reason'] === 'success') {
|
||||
return $data['result'];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 心灵鸡汤
|
||||
* @return string
|
||||
*/
|
||||
public static function soups(): string
|
||||
{
|
||||
$soupKey = env("JUKE_KEY_SOUP");
|
||||
if ($soupKey) {
|
||||
$data = self::curl("https://apis.juhe.cn/fapig/soup/query?key=" . $soupKey);
|
||||
$data = Base::json2array($data);
|
||||
if ($data['reason'] === 'success' && $text = trim($data['result']['text'])) {
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 签到机器人网络内容
|
||||
* @param $type
|
||||
* @return string
|
||||
*/
|
||||
public static function checkinBotQuickMsg($type): string
|
||||
{
|
||||
$text = "维护中...";
|
||||
switch ($type) {
|
||||
case "it":
|
||||
$data = self::curl('http://vvhan.api.hitosea.com/api/hotlist?type=itNews', 3600);
|
||||
if ($data = Base::json2array($data)) {
|
||||
$i = 1;
|
||||
$array = array_map(function ($item) use (&$i) {
|
||||
if ($item['title'] && $item['desc']) {
|
||||
return "<p>" . ($i++) . ". <strong><a href='{$item['mobilUrl']}' target='_blank'>{$item['title']}</a></strong></p><p>{$item['desc']}</p>";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}, $data['data']);
|
||||
$array = array_values(array_filter($array));
|
||||
if ($array) {
|
||||
array_unshift($array, "<p><strong>{$data['title']}</strong>({$data['update_time']})</p>");
|
||||
$text = implode("<p> </p>", $array);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "36ke":
|
||||
$data = self::curl('http://vvhan.api.hitosea.com/api/hotlist?type=36Ke', 3600);
|
||||
if ($data = Base::json2array($data)) {
|
||||
$i = 1;
|
||||
$array = array_map(function ($item) use (&$i) {
|
||||
if ($item['title'] && $item['desc']) {
|
||||
return "<p>" . ($i++) . ". <strong><a href='{$item['mobilUrl']}' target='_blank'>{$item['title']}</a></strong></p><p>{$item['desc']}</p>";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}, $data['data']);
|
||||
$array = array_values(array_filter($array));
|
||||
if ($array) {
|
||||
array_unshift($array, "<p><strong>{$data['title']}</strong>({$data['update_time']})</p>");
|
||||
$text = implode("<p> </p>", $array);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "60s":
|
||||
$data = self::curl('http://vvhan.api.hitosea.com/api/60s?type=json', 3600);
|
||||
if ($data = Base::json2array($data)) {
|
||||
$i = 1;
|
||||
$array = array_map(function ($item) use (&$i) {
|
||||
if ($item) {
|
||||
return "<p>" . ($i++) . ". {$item}</p>";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}, $data['data']);
|
||||
$array = array_values(array_filter($array));
|
||||
if ($array) {
|
||||
array_unshift($array, "<p><strong>{$data['name']}</strong>({$data['time'][0]})</p>");
|
||||
$text = implode("<p> </p>", $array);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "joke":
|
||||
$text = "笑话被掏空";
|
||||
$data = self::curl('http://vvhan.api.hitosea.com/api/joke?type=json', 5);
|
||||
if ($data = Base::json2array($data)) {
|
||||
if ($data = trim($data['joke'])) {
|
||||
$text = "开心笑话:{$data}";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "soup":
|
||||
$text = "鸡汤分完了";
|
||||
$data = self::curl('https://api.ayfre.com/jt/?type=bot', 5);
|
||||
if ($data) {
|
||||
$text = "心灵鸡汤:{$data}";
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @param int $cacheSecond 缓存时间(秒),如果结果为空则缓存有效30秒
|
||||
* @param int $timeout
|
||||
* @return string
|
||||
*/
|
||||
private static function curl($url, int $cacheSecond = 0, int $timeout = 15): string
|
||||
{
|
||||
if ($cacheSecond > 0) {
|
||||
$key = "curlCache::" . md5($url);
|
||||
$content = Cache::remember($key, Carbon::now()->addSeconds($cacheSecond), function () use ($cacheSecond, $key, $timeout, $url) {
|
||||
$result = Ihttp::ihttp_request($url, [], [], $timeout);
|
||||
$content = Base::isSuccess($result) ? trim($result['data']) : '';
|
||||
if (empty($content) && $cacheSecond > 30) {
|
||||
Cache::put($key, "", Carbon::now()->addSeconds(30));
|
||||
}
|
||||
return $content;
|
||||
});
|
||||
} else {
|
||||
$result = Ihttp::ihttp_request($url, [], [], $timeout);
|
||||
$content = Base::isSuccess($result) ? trim($result['data']) : '';
|
||||
}
|
||||
//
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
170
app/Module/ImgCompress.php
Normal file
170
app/Module/ImgCompress.php
Normal file
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
namespace App\Module;
|
||||
|
||||
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
|
||||
|
||||
/**
|
||||
* 图片压缩类:通过缩放来压缩。
|
||||
* 如果要保持源图比例,把参数$percent保持为1即可。
|
||||
* 即使原比例压缩,也可大幅度缩小。数码相机4M图片。也可以缩为700KB左右。如果缩小比例,则体积会更小。
|
||||
*
|
||||
* 结果:可保存、可直接显示。
|
||||
*/
|
||||
class ImgCompress
|
||||
{
|
||||
private $src;
|
||||
private $image;
|
||||
private $imageinfo;
|
||||
private $percent;
|
||||
|
||||
/**
|
||||
* 图片压缩
|
||||
* @param string $src 源图
|
||||
* @param float $percent 压缩比例
|
||||
*/
|
||||
public function __construct($src, $percent = 1)
|
||||
{
|
||||
$this->src = $src;
|
||||
$this->percent = $percent;
|
||||
}
|
||||
|
||||
/** 高清压缩图片
|
||||
* @param string $saveName 提供图片名(可不带扩展名,用源图扩展名)用于保存。或不提供文件名直接显示
|
||||
*/
|
||||
public function compressImg($saveName = '')
|
||||
{
|
||||
if (!$this->_openImage()) { //打开图片
|
||||
return;
|
||||
}
|
||||
if (!empty($saveName)) $this->_saveImage($saveName); //保存
|
||||
else $this->_showImage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部:打开图片
|
||||
*/
|
||||
private function _openImage()
|
||||
{
|
||||
list($width, $height, $type, $attr) = getimagesize($this->src);
|
||||
$this->imageinfo = array(
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'type' => image_type_to_extension($type, false),
|
||||
'attr' => $attr
|
||||
);
|
||||
$fun = "imagecreatefrom" . $this->imageinfo['type'];
|
||||
if (!function_exists($fun)) {
|
||||
return false;
|
||||
}
|
||||
$this->image = $fun($this->src);
|
||||
$this->_thumpImage();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部:操作图片
|
||||
*/
|
||||
private function _thumpImage()
|
||||
{
|
||||
$new_width = $this->imageinfo['width'] * $this->percent;
|
||||
$new_height = $this->imageinfo['height'] * $this->percent;
|
||||
$image_thump = imagecreatetruecolor($new_width, $new_height);
|
||||
//将原图复制带图片载体上面,并且按照一定比例压缩,极大的保持了清晰度
|
||||
imagecopyresampled($image_thump, $this->image, 0, 0, 0, 0, $new_width, $new_height, $this->imageinfo['width'], $this->imageinfo['height']);
|
||||
imagedestroy($this->image);
|
||||
$this->image = $image_thump;
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出图片:保存图片则用saveImage()
|
||||
*/
|
||||
private function _showImage()
|
||||
{
|
||||
header('Content-Type: image/' . $this->imageinfo['type']);
|
||||
$funcs = "image" . $this->imageinfo['type'];
|
||||
$funcs($this->image);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存图片到硬盘:
|
||||
* @param string $dstImgName 1、可指定字符串不带后缀的名称,使用源图扩展名 。2、直接指定目标图片名带扩展名。
|
||||
*/
|
||||
private function _saveImage($dstImgName)
|
||||
{
|
||||
if (empty($dstImgName)) return false;
|
||||
if (str_contains($dstImgName, '.')) {
|
||||
$dstName = $dstImgName;
|
||||
} else {
|
||||
$allowImgs = ['.jpg', '.jpeg', '.webp', '.png', '.bmp', '.wbmp', '.gif']; //如果目标图片名有后缀就用目标图片扩展名 后缀,如果没有,则用源图的扩展名
|
||||
$dstExt = strrchr($dstImgName, ".");
|
||||
$sourseExt = strrchr($this->src, ".");
|
||||
if (!empty($dstExt)) $dstExt = strtolower($dstExt);
|
||||
if (!empty($sourseExt)) $sourseExt = strtolower($sourseExt);
|
||||
//有指定目标名扩展名
|
||||
if (!empty($dstExt) && in_array($dstExt, $allowImgs)) {
|
||||
$dstName = $dstImgName;
|
||||
} elseif (!empty($sourseExt) && in_array($sourseExt, $allowImgs)) {
|
||||
$dstName = $dstImgName . $sourseExt;
|
||||
} else {
|
||||
$dstName = $dstImgName . $this->imageinfo['type'];
|
||||
}
|
||||
}
|
||||
$funcs = "image" . $this->imageinfo['type'];
|
||||
if (!function_exists($funcs)) {
|
||||
return false;
|
||||
}
|
||||
$funcs($this->image, $dstName);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁图片
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->image) {
|
||||
imagedestroy($this->image);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩图片静态方法
|
||||
* @param string $src 原图地址
|
||||
* @param float $percent 压缩比例
|
||||
* @param float $minSize 最小压缩大小,小于这个不压缩,单位KB
|
||||
* @return void
|
||||
*/
|
||||
public static function compress(string $src, float $percent = 1, float $minSize = 10): void
|
||||
{
|
||||
if (Base::settingFind("system", "image_compress") === 'close') {
|
||||
return;
|
||||
}
|
||||
if (!file_exists($src)) {
|
||||
return;
|
||||
}
|
||||
$allowImgs = ['.jpg', '.jpeg', '.webp', '.png', '.bmp', '.wbmp'];
|
||||
if (!in_array(strrchr($src, "."), $allowImgs)) {
|
||||
return;
|
||||
}
|
||||
$size = filesize($src);
|
||||
if ($minSize > 0 && $size < $minSize * 1024) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$img = new ImgCompress($src, $percent);
|
||||
$tmp = $src . '.compress.tmp';
|
||||
$img->compressImg($tmp);
|
||||
if (file_exists($tmp)) {
|
||||
if (filesize($tmp) > $size) {
|
||||
unlink($tmp);
|
||||
return;
|
||||
}
|
||||
unlink($src);
|
||||
rename($tmp, $src);
|
||||
}
|
||||
} catch (\Exception) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
app/Module/TimeRange.php
Normal file
44
app/Module/TimeRange.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Module;
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
class TimeRange
|
||||
{
|
||||
public ?Carbon $updated;
|
||||
public ?Carbon $deleted;
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
*/
|
||||
public function __construct($data)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
$range = explode("-", str_replace([",", "|"], "-", $data['timerange']));
|
||||
if ($data['updated_at'] || $data['at_after']) {
|
||||
$range[0] = $data['updated_at'] ?: $data['at_after'];
|
||||
}
|
||||
if ($data['deleted_at']) {
|
||||
$range[1] = $data['deleted_at'];
|
||||
}
|
||||
} else {
|
||||
$range = explode("-", str_replace([",", "|"], "-", $data));
|
||||
}
|
||||
//
|
||||
$updated = Base::isNumber($range[0]) ? intval($range[0]) : trim($range[0]);
|
||||
$deleted = Base::isNumber($range[1]) ? intval($range[1]) : trim($range[1]);
|
||||
//
|
||||
$this->updated = $updated ? Carbon::parse($updated) : null;
|
||||
$this->deleted = $deleted ? Carbon::parse($deleted) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @return TimeRange
|
||||
*/
|
||||
public static function parse($data): TimeRange
|
||||
{
|
||||
return new self($data);
|
||||
}
|
||||
}
|
||||
81
app/Observers/ProjectObserver.php
Normal file
81
app/Observers/ProjectObserver.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Models\Deleted;
|
||||
use App\Models\Project;
|
||||
use App\Models\ProjectUser;
|
||||
|
||||
class ProjectObserver
|
||||
{
|
||||
/**
|
||||
* Handle the Project "created" event.
|
||||
*
|
||||
* @param \App\Models\Project $project
|
||||
* @return void
|
||||
*/
|
||||
public function created(Project $project)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the Project "updated" event.
|
||||
*
|
||||
* @param \App\Models\Project $project
|
||||
* @return void
|
||||
*/
|
||||
public function updated(Project $project)
|
||||
{
|
||||
if ($project->isDirty('archived_at')) {
|
||||
$userids = $this->userids($project);
|
||||
if ($project->archived_at) {
|
||||
Deleted::record('project', $project->id, $userids);
|
||||
} else {
|
||||
Deleted::forget('project', $project->id, $userids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the Project "deleted" event.
|
||||
*
|
||||
* @param \App\Models\Project $project
|
||||
* @return void
|
||||
*/
|
||||
public function deleted(Project $project)
|
||||
{
|
||||
Deleted::record('project', $project->id, $this->userids($project));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the Project "restored" event.
|
||||
*
|
||||
* @param \App\Models\Project $project
|
||||
* @return void
|
||||
*/
|
||||
public function restored(Project $project)
|
||||
{
|
||||
Deleted::forget('project', $project->id, $this->userids($project));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the Project "force deleted" event.
|
||||
*
|
||||
* @param \App\Models\Project $project
|
||||
* @return void
|
||||
*/
|
||||
public function forceDeleted(Project $project)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Project $project
|
||||
* @return array
|
||||
*/
|
||||
private function userids(Project $project)
|
||||
{
|
||||
return ProjectUser::whereProjectId($project->id)->pluck('userid')->toArray();
|
||||
}
|
||||
}
|
||||
81
app/Observers/ProjectTaskObserver.php
Normal file
81
app/Observers/ProjectTaskObserver.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Models\Deleted;
|
||||
use App\Models\ProjectTask;
|
||||
use App\Models\ProjectTaskUser;
|
||||
|
||||
class ProjectTaskObserver
|
||||
{
|
||||
/**
|
||||
* Handle the ProjectTask "created" event.
|
||||
*
|
||||
* @param \App\Models\ProjectTask $projectTask
|
||||
* @return void
|
||||
*/
|
||||
public function created(ProjectTask $projectTask)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProjectTask "updated" event.
|
||||
*
|
||||
* @param \App\Models\ProjectTask $projectTask
|
||||
* @return void
|
||||
*/
|
||||
public function updated(ProjectTask $projectTask)
|
||||
{
|
||||
if ($projectTask->isDirty('archived_at')) {
|
||||
$userids = $this->userids($projectTask);
|
||||
if ($projectTask->archived_at) {
|
||||
Deleted::record('projectTask', $projectTask->id, $userids);
|
||||
} else {
|
||||
Deleted::forget('projectTask', $projectTask->id, $userids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProjectTask "deleted" event.
|
||||
*
|
||||
* @param \App\Models\ProjectTask $projectTask
|
||||
* @return void
|
||||
*/
|
||||
public function deleted(ProjectTask $projectTask)
|
||||
{
|
||||
Deleted::record('projectTask', $projectTask->id, $this->userids($projectTask));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProjectTask "restored" event.
|
||||
*
|
||||
* @param \App\Models\ProjectTask $projectTask
|
||||
* @return void
|
||||
*/
|
||||
public function restored(ProjectTask $projectTask)
|
||||
{
|
||||
Deleted::forget('projectTask', $projectTask->id, $this->userids($projectTask));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProjectTask "force deleted" event.
|
||||
*
|
||||
* @param \App\Models\ProjectTask $projectTask
|
||||
* @return void
|
||||
*/
|
||||
public function forceDeleted(ProjectTask $projectTask)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ProjectTask $projectTask
|
||||
* @return array
|
||||
*/
|
||||
private function userids(ProjectTask $projectTask)
|
||||
{
|
||||
return ProjectTaskUser::whereTaskId($projectTask->id)->pluck('userid')->toArray();
|
||||
}
|
||||
}
|
||||
67
app/Observers/ProjectTaskUserObserver.php
Normal file
67
app/Observers/ProjectTaskUserObserver.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Models\Deleted;
|
||||
use App\Models\ProjectTaskUser;
|
||||
use App\Models\ProjectUser;
|
||||
|
||||
class ProjectTaskUserObserver
|
||||
{
|
||||
/**
|
||||
* Handle the ProjectTaskUser "created" event.
|
||||
*
|
||||
* @param \App\Models\ProjectTaskUser $projectTaskUser
|
||||
* @return void
|
||||
*/
|
||||
public function created(ProjectTaskUser $projectTaskUser)
|
||||
{
|
||||
Deleted::forget('projectTask', $projectTaskUser->task_id, $projectTaskUser->userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProjectTaskUser "updated" event.
|
||||
*
|
||||
* @param \App\Models\ProjectTaskUser $projectTaskUser
|
||||
* @return void
|
||||
*/
|
||||
public function updated(ProjectTaskUser $projectTaskUser)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProjectTaskUser "deleted" event.
|
||||
*
|
||||
* @param \App\Models\ProjectTaskUser $projectTaskUser
|
||||
* @return void
|
||||
*/
|
||||
public function deleted(ProjectTaskUser $projectTaskUser)
|
||||
{
|
||||
if (!ProjectUser::whereProjectId($projectTaskUser->project_id)->whereUserid($projectTaskUser->userid)->exists()) {
|
||||
Deleted::record('projectTask', $projectTaskUser->task_id, $projectTaskUser->userid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProjectTaskUser "restored" event.
|
||||
*
|
||||
* @param \App\Models\ProjectTaskUser $projectTaskUser
|
||||
* @return void
|
||||
*/
|
||||
public function restored(ProjectTaskUser $projectTaskUser)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProjectTaskUser "force deleted" event.
|
||||
*
|
||||
* @param \App\Models\ProjectTaskUser $projectTaskUser
|
||||
* @return void
|
||||
*/
|
||||
public function forceDeleted(ProjectTaskUser $projectTaskUser)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
64
app/Observers/ProjectUserObserver.php
Normal file
64
app/Observers/ProjectUserObserver.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Models\Deleted;
|
||||
use App\Models\ProjectUser;
|
||||
|
||||
class ProjectUserObserver
|
||||
{
|
||||
/**
|
||||
* Handle the ProjectUser "created" event.
|
||||
*
|
||||
* @param \App\Models\ProjectUser $projectUser
|
||||
* @return void
|
||||
*/
|
||||
public function created(ProjectUser $projectUser)
|
||||
{
|
||||
Deleted::forget('project', $projectUser->project_id, $projectUser->userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProjectUser "updated" event.
|
||||
*
|
||||
* @param \App\Models\ProjectUser $projectUser
|
||||
* @return void
|
||||
*/
|
||||
public function updated(ProjectUser $projectUser)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProjectUser "deleted" event.
|
||||
*
|
||||
* @param \App\Models\ProjectUser $projectUser
|
||||
* @return void
|
||||
*/
|
||||
public function deleted(ProjectUser $projectUser)
|
||||
{
|
||||
Deleted::record('project', $projectUser->project_id, $projectUser->userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProjectUser "restored" event.
|
||||
*
|
||||
* @param \App\Models\ProjectUser $projectUser
|
||||
* @return void
|
||||
*/
|
||||
public function restored(ProjectUser $projectUser)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ProjectUser "force deleted" event.
|
||||
*
|
||||
* @param \App\Models\ProjectUser $projectUser
|
||||
* @return void
|
||||
*/
|
||||
public function forceDeleted(ProjectUser $projectUser)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
74
app/Observers/WebSocketDialogObserver.php
Normal file
74
app/Observers/WebSocketDialogObserver.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Models\Deleted;
|
||||
use App\Models\WebSocketDialog;
|
||||
use App\Models\WebSocketDialogUser;
|
||||
|
||||
class WebSocketDialogObserver
|
||||
{
|
||||
/**
|
||||
* Handle the WebSocketDialog "created" event.
|
||||
*
|
||||
* @param \App\Models\WebSocketDialog $webSocketDialog
|
||||
* @return void
|
||||
*/
|
||||
public function created(WebSocketDialog $webSocketDialog)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the WebSocketDialog "updated" event.
|
||||
*
|
||||
* @param \App\Models\WebSocketDialog $webSocketDialog
|
||||
* @return void
|
||||
*/
|
||||
public function updated(WebSocketDialog $webSocketDialog)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the WebSocketDialog "deleted" event.
|
||||
*
|
||||
* @param \App\Models\WebSocketDialog $webSocketDialog
|
||||
* @return void
|
||||
*/
|
||||
public function deleted(WebSocketDialog $webSocketDialog)
|
||||
{
|
||||
Deleted::record('dialog', $webSocketDialog->id, $this->userids($webSocketDialog));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the WebSocketDialog "restored" event.
|
||||
*
|
||||
* @param \App\Models\WebSocketDialog $webSocketDialog
|
||||
* @return void
|
||||
*/
|
||||
public function restored(WebSocketDialog $webSocketDialog)
|
||||
{
|
||||
Deleted::forget('dialog', $webSocketDialog->id, $this->userids($webSocketDialog));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the WebSocketDialog "force deleted" event.
|
||||
*
|
||||
* @param \App\Models\WebSocketDialog $webSocketDialog
|
||||
* @return void
|
||||
*/
|
||||
public function forceDeleted(WebSocketDialog $webSocketDialog)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebSocketDialog $webSocketDialog
|
||||
* @return array
|
||||
*/
|
||||
private function userids(WebSocketDialog $webSocketDialog)
|
||||
{
|
||||
return WebSocketDialogUser::whereDialogId($webSocketDialog->id)->pluck('userid')->toArray();
|
||||
}
|
||||
}
|
||||
64
app/Observers/WebSocketDialogUserObserver.php
Normal file
64
app/Observers/WebSocketDialogUserObserver.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Models\Deleted;
|
||||
use App\Models\WebSocketDialogUser;
|
||||
|
||||
class WebSocketDialogUserObserver
|
||||
{
|
||||
/**
|
||||
* Handle the WebSocketDialogUser "created" event.
|
||||
*
|
||||
* @param \App\Models\WebSocketDialogUser $webSocketDialogUser
|
||||
* @return void
|
||||
*/
|
||||
public function created(WebSocketDialogUser $webSocketDialogUser)
|
||||
{
|
||||
Deleted::forget('dialog', $webSocketDialogUser->dialog_id, $webSocketDialogUser->userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the WebSocketDialogUser "updated" event.
|
||||
*
|
||||
* @param \App\Models\WebSocketDialogUser $webSocketDialogUser
|
||||
* @return void
|
||||
*/
|
||||
public function updated(WebSocketDialogUser $webSocketDialogUser)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the WebSocketDialogUser "deleted" event.
|
||||
*
|
||||
* @param \App\Models\WebSocketDialogUser $webSocketDialogUser
|
||||
* @return void
|
||||
*/
|
||||
public function deleted(WebSocketDialogUser $webSocketDialogUser)
|
||||
{
|
||||
Deleted::record('dialog', $webSocketDialogUser->dialog_id, $webSocketDialogUser->userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the WebSocketDialogUser "restored" event.
|
||||
*
|
||||
* @param \App\Models\WebSocketDialogUser $webSocketDialogUser
|
||||
* @return void
|
||||
*/
|
||||
public function restored(WebSocketDialogUser $webSocketDialogUser)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the WebSocketDialogUser "force deleted" event.
|
||||
*
|
||||
* @param \App\Models\WebSocketDialogUser $webSocketDialogUser
|
||||
* @return void
|
||||
*/
|
||||
public function forceDeleted(WebSocketDialogUser $webSocketDialogUser)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,21 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\ProjectTask;
|
||||
use App\Models\ProjectTaskUser;
|
||||
use App\Models\ProjectUser;
|
||||
use App\Models\WebSocketDialog;
|
||||
use App\Models\WebSocketDialogUser;
|
||||
use App\Observers\ProjectObserver;
|
||||
use App\Observers\ProjectTaskObserver;
|
||||
use App\Observers\ProjectTaskUserObserver;
|
||||
use App\Observers\ProjectUserObserver;
|
||||
use App\Observers\WebSocketDialogObserver;
|
||||
use App\Observers\WebSocketDialogUserObserver;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
@@ -27,6 +38,11 @@ class EventServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
Project::observe(ProjectObserver::class);
|
||||
ProjectTask::observe(ProjectTaskObserver::class);
|
||||
ProjectTaskUser::observe(ProjectTaskUserObserver::class);
|
||||
ProjectUser::observe(ProjectUserObserver::class);
|
||||
WebSocketDialog::observe(WebSocketDialogObserver::class);
|
||||
WebSocketDialogUser::observe(WebSocketDialogUserObserver::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Models\User;
|
||||
use App\Models\WebSocket;
|
||||
use App\Models\WebSocketDialogMsg;
|
||||
use App\Module\Base;
|
||||
use App\Module\Doo;
|
||||
use App\Tasks\LineTask;
|
||||
use App\Tasks\PushTask;
|
||||
use Cache;
|
||||
@@ -39,55 +40,45 @@ class WebSocketService implements WebSocketHandlerInterface
|
||||
*/
|
||||
public function onOpen(Server $server, Request $request)
|
||||
{
|
||||
global $_A;
|
||||
$_A = [
|
||||
'__static_langdata' => [],
|
||||
];
|
||||
$fd = $request->fd;
|
||||
$data = Base::newTrim($request->get);
|
||||
$action = $data['action'];
|
||||
$get = Base::newTrim($request->get);
|
||||
$action = $get['action'];
|
||||
Cache::forget("User::encrypt:" . $fd);
|
||||
switch ($action) {
|
||||
/**
|
||||
* 网页访问
|
||||
*/
|
||||
case 'web':
|
||||
{
|
||||
// 判断token参数
|
||||
$token = $data['token'];
|
||||
$cacheKey = "ws::token:" . md5($token);
|
||||
$userid = Cache::remember($cacheKey, now()->addSeconds(1), function () use ($token) {
|
||||
$authInfo = User::authFind('all', $token);
|
||||
if ($authInfo['userid'] > 0) {
|
||||
if (User::whereUserid($authInfo['userid'])->whereEmail($authInfo['email'])->whereEncrypt($authInfo['encrypt'])->exists()) {
|
||||
return $authInfo['userid'];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
if (empty($userid)) {
|
||||
Cache::forget($cacheKey);
|
||||
Doo::load($get['token'], $get['language']);
|
||||
//
|
||||
if (Doo::userId() > 0
|
||||
&& !Doo::userExpired()
|
||||
&& $user = User::whereUserid(Doo::userId())->whereEmail(Doo::userEmail())->whereEncrypt(Doo::userEncrypt())->first()) {
|
||||
// 保存用户
|
||||
$this->saveUser($fd, $user->userid);
|
||||
// 发送open事件
|
||||
$server->push($fd, Base::array2json([
|
||||
'type' => 'open',
|
||||
'data' => [
|
||||
'fd' => $fd,
|
||||
],
|
||||
]));
|
||||
// 通知上线
|
||||
Task::deliver(new LineTask($user->userid, true));
|
||||
// 推送离线时收到的消息
|
||||
Task::deliver(new PushTask("RETRY::" . $user->userid));
|
||||
} else {
|
||||
// 用户不存在
|
||||
$server->push($fd, Base::array2json([
|
||||
'type' => 'error',
|
||||
'data' => [
|
||||
'error' => '成员不存在'
|
||||
'error' => 'No member'
|
||||
],
|
||||
]));
|
||||
$server->close($fd);
|
||||
$this->deleteUser($fd);
|
||||
return;
|
||||
}
|
||||
// 保存用户、发送open事件
|
||||
$this->saveUser($fd, $userid);
|
||||
$server->push($fd, Base::array2json([
|
||||
'type' => 'open',
|
||||
'data' => [
|
||||
'fd' => $fd,
|
||||
],
|
||||
]));
|
||||
// 通知上线
|
||||
Task::deliver(new LineTask($userid, true));
|
||||
// 推送离线时收到的消息
|
||||
Task::deliver(new PushTask("RETRY::" . $userid));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -103,11 +94,6 @@ class WebSocketService implements WebSocketHandlerInterface
|
||||
*/
|
||||
public function onMessage(Server $server, Frame $frame)
|
||||
{
|
||||
global $_A;
|
||||
$_A = [
|
||||
'__static_langdata' => [],
|
||||
];
|
||||
//
|
||||
$msg = Base::json2array($frame->data);
|
||||
$type = $msg['type']; // 消息类型
|
||||
$msgId = $msg['msgId']; // 消息ID(用于回调)
|
||||
@@ -153,6 +139,16 @@ class WebSocketService implements WebSocketHandlerInterface
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
/**
|
||||
* 加密参数
|
||||
*/
|
||||
case 'encrypt':
|
||||
if ($data['type'] === 'pgp') {
|
||||
$data['key'] = Doo::pgpPublicFormat($data['key']);
|
||||
}
|
||||
Cache::put("User::encrypt:" . $frame->fd, Base::array2json($data), Carbon::now()->addDay());
|
||||
return;
|
||||
}
|
||||
//
|
||||
if ($msgId) {
|
||||
@@ -191,13 +187,15 @@ class WebSocketService implements WebSocketHandlerInterface
|
||||
*/
|
||||
private function saveUser($fd, $userid)
|
||||
{
|
||||
Cache::put("User::fd:" . $fd, "on", Carbon::now()->addDay());
|
||||
Cache::put("User::online:" . $userid, "on", Carbon::now()->addDay());
|
||||
//
|
||||
WebSocket::updateInsert([
|
||||
'key' => md5($fd . '@' . $userid)
|
||||
], [
|
||||
'fd' => $fd,
|
||||
'userid' => $userid,
|
||||
]);
|
||||
Cache::put("User::online:" . $userid, time(), Carbon::now()->addSeconds(30));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,6 +204,8 @@ class WebSocketService implements WebSocketHandlerInterface
|
||||
*/
|
||||
private function deleteUser($fd)
|
||||
{
|
||||
Cache::forget("User::fd:" . $fd);
|
||||
//
|
||||
$array = [];
|
||||
WebSocket::whereFd($fd)->chunk(10, function($list) use (&$array) {
|
||||
/** @var WebSocket $item */
|
||||
@@ -216,6 +216,7 @@ class WebSocketService implements WebSocketHandlerInterface
|
||||
User::whereUserid($item->userid)->update([
|
||||
'line_at' => Carbon::now()
|
||||
]);
|
||||
Cache::forget("User::online:" . $item->userid);
|
||||
}
|
||||
if ($item->path && str_starts_with($item->path, "/single/file/")) {
|
||||
$array[$item->path] = $item->path;
|
||||
|
||||
@@ -3,18 +3,13 @@
|
||||
namespace App\Tasks;
|
||||
|
||||
use App\Models\ProjectTask;
|
||||
use App\Models\ProjectTaskPushLog;
|
||||
use App\Models\User;
|
||||
use App\Module\Base;
|
||||
use Carbon\Carbon;
|
||||
use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||
|
||||
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
|
||||
|
||||
class AppPushTask extends AbstractTask
|
||||
{
|
||||
protected $endArray = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
@@ -23,110 +18,35 @@ class AppPushTask extends AbstractTask
|
||||
public function start()
|
||||
{
|
||||
$setting = Base::setting('appPushSetting');
|
||||
$pushTask = $setting['push'] === 'open' && $setting['push_task'] !== 'close';
|
||||
if (!$pushTask) {
|
||||
if ($setting['push'] !== 'open') {
|
||||
return;
|
||||
}
|
||||
$start = intval($setting['task_start_minute']);
|
||||
$hours = floatval($setting['task_remind_hours']);
|
||||
$hours2 = floatval($setting['task_remind_hours2']);
|
||||
if ($start > -1) {
|
||||
ProjectTask::whereNull("complete_at")
|
||||
->whereNull("archived_at")
|
||||
->whereBetween("start_at", [
|
||||
Carbon::now()->subMinutes($start + 10),
|
||||
Carbon::now()->subMinutes($start)
|
||||
])->chunkById(100, function ($tasks) {
|
||||
/** @var ProjectTask $task */
|
||||
foreach ($tasks as $task) {
|
||||
$this->taskPush($task, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($hours > -1) {
|
||||
ProjectTask::whereNull("complete_at")
|
||||
->whereNull("archived_at")
|
||||
->whereBetween("end_at", [
|
||||
Carbon::now()->addMinutes($hours * 60),
|
||||
Carbon::now()->addMinutes($hours * 60 + 10)
|
||||
])->chunkById(100, function ($tasks) {
|
||||
/** @var ProjectTask $task */
|
||||
foreach ($tasks as $task) {
|
||||
$this->taskPush($task, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($hours2 > -1) {
|
||||
ProjectTask::whereNull("complete_at")
|
||||
->whereNull("archived_at")
|
||||
->whereBetween("end_at", [
|
||||
Carbon::now()->subMinutes($hours2 * 60 + 10),
|
||||
Carbon::now()->subMinutes($hours2 * 60)
|
||||
])->chunkById(100, function ($tasks) {
|
||||
/** @var ProjectTask $task */
|
||||
foreach ($tasks as $task) {
|
||||
$this->taskPush($task, 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
ProjectTask::whereNull("complete_at")
|
||||
->whereNull("archived_at")
|
||||
->whereBetween("end_at", [
|
||||
Carbon::now()->addMinutes(60),
|
||||
Carbon::now()->addMinutes(60 + 10)
|
||||
])->chunkById(100, function ($tasks) {
|
||||
/** @var ProjectTask $task */
|
||||
foreach ($tasks as $task) {
|
||||
$task->taskPush(null, 1);
|
||||
}
|
||||
});
|
||||
ProjectTask::whereNull("complete_at")
|
||||
->whereNull("archived_at")
|
||||
->whereBetween("end_at", [
|
||||
Carbon::now()->subMinutes(60 + 10),
|
||||
Carbon::now()->subMinutes(60)
|
||||
])->chunkById(100, function ($tasks) {
|
||||
/** @var ProjectTask $task */
|
||||
foreach ($tasks as $task) {
|
||||
$task->taskPush(null, 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function end()
|
||||
{
|
||||
foreach ($this->endArray as $task) {
|
||||
Task::deliver($task);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务过期前、超期后提醒
|
||||
* @param ProjectTask $task
|
||||
* @param int $type
|
||||
* @return void
|
||||
*/
|
||||
private function taskPush(ProjectTask $task, int $type)
|
||||
{
|
||||
$userids = $task->taskUser->where('owner', 1)->pluck('userid')->toArray();
|
||||
if (empty($userids)) {
|
||||
return;
|
||||
}
|
||||
$users = User::whereIn('userid', $userids)->whereNull('disable_at')->get();
|
||||
if (empty($users)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$setting = Base::setting('appPushSetting');
|
||||
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$data = [
|
||||
'type' => $type,
|
||||
'userid' => $user->userid,
|
||||
'task_id' => $task->id,
|
||||
];
|
||||
$pushLog = ProjectTaskPushLog::where($data)->exists();
|
||||
if ($pushLog) {
|
||||
continue;
|
||||
}
|
||||
$title = match ($type) {
|
||||
1 => "任务提醒",
|
||||
2 => "任务过期提醒",
|
||||
default => "任务开始提醒",
|
||||
};
|
||||
$body = view('push.task', [
|
||||
'type' => str_replace([0, 1, 2], ['start', 'before', 'after'], $type),
|
||||
'user' => $user,
|
||||
'task' => $task,
|
||||
'setting' => $setting,
|
||||
])->render();
|
||||
$this->endArray[] = new PushUmengMsg($data['userid'], [
|
||||
'title' => $title,
|
||||
'body' => $body,
|
||||
'description' => "TID:{$data['task_id']}",
|
||||
'seconds' => 3600,
|
||||
'badge' => 1,
|
||||
]);
|
||||
ProjectTaskPushLog::createInstance($data)->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
386
app/Tasks/BotReceiveMsgTask.php
Normal file
386
app/Tasks/BotReceiveMsgTask.php
Normal file
@@ -0,0 +1,386 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tasks;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\UserBot;
|
||||
use App\Models\WebSocketDialog;
|
||||
use App\Models\WebSocketDialogMsg;
|
||||
use App\Module\Base;
|
||||
use App\Module\Doo;
|
||||
use App\Module\Ihttp;
|
||||
use Carbon\Carbon;
|
||||
|
||||
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
|
||||
|
||||
|
||||
/**
|
||||
* 推送会话消息
|
||||
* Class BotReceiveMsgTask
|
||||
* @package App\Tasks
|
||||
*/
|
||||
class BotReceiveMsgTask extends AbstractTask
|
||||
{
|
||||
protected $userid;
|
||||
protected $msgId;
|
||||
|
||||
public function __construct($userid, $msgId)
|
||||
{
|
||||
parent::__construct(...func_get_args());
|
||||
$this->userid = $userid;
|
||||
$this->msgId = $msgId;
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
$botUser = User::whereUserid($this->userid)->whereBot(1)->first();
|
||||
if (empty($botUser)) {
|
||||
return;
|
||||
}
|
||||
$msg = WebSocketDialogMsg::find($this->msgId);
|
||||
if (empty($msg)) {
|
||||
return;
|
||||
}
|
||||
$msg->readSuccess($botUser->userid);
|
||||
//
|
||||
$dialog = WebSocketDialog::find($msg->dialog_id);
|
||||
if (empty($dialog)) {
|
||||
return;
|
||||
}
|
||||
if ($dialog->type !== 'user') {
|
||||
return;
|
||||
}
|
||||
$this->botManagerReceive($msg, $botUser);
|
||||
}
|
||||
|
||||
public function end()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 机器人管理处理消息
|
||||
* @param WebSocketDialogMsg $msg
|
||||
* @param User $botUser
|
||||
* @return void
|
||||
*/
|
||||
private function botManagerReceive(WebSocketDialogMsg $msg, User $botUser)
|
||||
{
|
||||
if ($msg->type !== 'text') {
|
||||
return;
|
||||
}
|
||||
$original = $msg->msg['text'];
|
||||
if (preg_match("/<span[^>]*?data-quick-key=([\"'])(.*?)\\1[^>]*?>(.*?)<\/span>/is", $original, $match)) {
|
||||
$command = $match[2];
|
||||
} else {
|
||||
$command = trim(strip_tags($original));
|
||||
}
|
||||
// 签到机器人
|
||||
if ($botUser->email === 'check-in@bot.system') {
|
||||
$text = UserBot::checkinBotQuickMsg($command, $msg->userid);
|
||||
if ($text) {
|
||||
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'text', ['text' => $text], $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
|
||||
}
|
||||
}
|
||||
// 隐私机器人
|
||||
if ($botUser->email === 'anon-msg@bot.system') {
|
||||
$text = UserBot::anonBotQuickMsg($command);
|
||||
if ($text) {
|
||||
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'text', ['text' => $text], $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
|
||||
}
|
||||
}
|
||||
// 管理机器人
|
||||
if (str_starts_with($command, '/')) {
|
||||
if ($botUser->email === 'bot-manager@bot.system') {
|
||||
$isManager = true;
|
||||
} elseif (UserBot::whereBotId($botUser->userid)->whereUserid($msg->userid)->exists()) {
|
||||
$isManager = false;
|
||||
} else {
|
||||
$text = "非常抱歉,我不是你的机器人,无法完成你的指令。";
|
||||
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'text', ['text' => $text], $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
|
||||
return;
|
||||
}
|
||||
//
|
||||
$array = Base::newTrim(explode(" ", "{$command} "));
|
||||
$type = $array[0];
|
||||
$data = [];
|
||||
$notice = "";
|
||||
if (!$isManager && in_array($type, ['/list', '/newbot'])) {
|
||||
return; // 这些操作仅支持【机器人管理】机器人
|
||||
}
|
||||
switch ($type) {
|
||||
/**
|
||||
* 列表
|
||||
*/
|
||||
case '/list':
|
||||
$data = User::select([
|
||||
'users.*',
|
||||
'user_bots.clear_day',
|
||||
'user_bots.clear_at',
|
||||
'user_bots.webhook_url',
|
||||
'user_bots.webhook_num'
|
||||
])
|
||||
->join('user_bots', 'users.userid', '=', 'user_bots.bot_id')
|
||||
->where('users.bot', 1)
|
||||
->where('user_bots.userid', $msg->userid)
|
||||
->take(50)
|
||||
->orderByDesc('id')
|
||||
->get();
|
||||
if ($data->isEmpty()) {
|
||||
$type = "notice";
|
||||
$notice = "您没有创建机器人。";
|
||||
}
|
||||
break;
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
case '/info':
|
||||
$botId = $isManager ? $array[1] : $botUser->userid;
|
||||
$data = $this->botManagerOne($botId, $msg->userid);
|
||||
if (!$data) {
|
||||
$type = "notice";
|
||||
$notice = "机器人不存在。";
|
||||
}
|
||||
break;
|
||||
|
||||
/**
|
||||
* 创建
|
||||
*/
|
||||
case '/newbot':
|
||||
if (User::select(['users.*'])
|
||||
->join('user_bots', 'users.userid', '=', 'user_bots.bot_id')
|
||||
->where('users.bot', 1)
|
||||
->where('user_bots.userid', $msg->userid)
|
||||
->count() >= 50) {
|
||||
$type = "notice";
|
||||
$notice = "超过最大创建数量。";
|
||||
break;
|
||||
}
|
||||
if (strlen($array[1]) < 2 || strlen($array[1]) > 20) {
|
||||
$type = "notice";
|
||||
$notice = "机器人名称由2-20个字符组成。";
|
||||
break;
|
||||
}
|
||||
$data = User::botGetOrCreate("user-" . Base::generatePassword(), [
|
||||
'nickname' => $array[1]
|
||||
], $msg->userid);
|
||||
if (empty($data)) {
|
||||
$type = "notice";
|
||||
$notice = "创建失败。";
|
||||
break;
|
||||
}
|
||||
$dialog = WebSocketDialog::checkUserDialog($data, $msg->userid);
|
||||
if ($dialog) {
|
||||
$text = "<p>您好,我是机器人:{$data->nickname},我的机器人ID是:{$data->userid},</p><p>你可以发送 <u><b>/help</b></u> 查看我支持什么命令。</p>";
|
||||
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $data->userid); // todo 未能在任务end事件来发送任务
|
||||
}
|
||||
break;
|
||||
|
||||
/**
|
||||
* 修改名字
|
||||
*/
|
||||
case '/setname':
|
||||
$botId = $isManager ? $array[1] : $botUser->userid;
|
||||
$nameString = $isManager ? $array[2] : $array[1];
|
||||
if (strlen($nameString) < 2 || strlen($nameString) > 20) {
|
||||
$type = "notice";
|
||||
$notice = "机器人名称由2-20个字符组成。";
|
||||
break;
|
||||
}
|
||||
$data = $this->botManagerOne($botId, $msg->userid);
|
||||
if ($data) {
|
||||
$data->nickname = $nameString;
|
||||
$data->az = Base::getFirstCharter($nameString);
|
||||
$data->pinyin = Base::cn2pinyin($nameString);
|
||||
$data->save();
|
||||
} else {
|
||||
$type = "notice";
|
||||
$notice = "机器人不存在。";
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
case '/deletebot':
|
||||
$botId = $isManager ? $array[1] : $botUser->userid;
|
||||
$data = $this->botManagerOne($botId, $msg->userid);
|
||||
if ($data) {
|
||||
$data->deleteUser('delete bot');
|
||||
} else {
|
||||
$type = "notice";
|
||||
$notice = "机器人不存在。";
|
||||
}
|
||||
break;
|
||||
|
||||
/**
|
||||
* 获取Token
|
||||
*/
|
||||
case '/token':
|
||||
$botId = $isManager ? $array[1] : $botUser->userid;
|
||||
$data = $this->botManagerOne($botId, $msg->userid);
|
||||
if ($data) {
|
||||
User::generateToken($data);
|
||||
} else {
|
||||
$type = "notice";
|
||||
$notice = "机器人不存在。";
|
||||
}
|
||||
break;
|
||||
|
||||
/**
|
||||
* 更新Token
|
||||
*/
|
||||
case '/revoke':
|
||||
$botId = $isManager ? $array[1] : $botUser->userid;
|
||||
$data = $this->botManagerOne($botId, $msg->userid);
|
||||
if ($data) {
|
||||
$data->encrypt = Base::generatePassword(6);
|
||||
$data->password = Doo::md5s(Base::generatePassword(32), $data->encrypt);
|
||||
$data->save();
|
||||
} else {
|
||||
$type = "notice";
|
||||
$notice = "机器人不存在。";
|
||||
}
|
||||
break;
|
||||
|
||||
/**
|
||||
* 设置保留消息时间
|
||||
*/
|
||||
case '/clearday':
|
||||
$botId = $isManager ? $array[1] : $botUser->userid;
|
||||
$clearDay = $isManager ? $array[2] : $array[1];
|
||||
$data = $this->botManagerOne($botId, $msg->userid);
|
||||
if ($data) {
|
||||
$userBot = UserBot::whereBotId($botId)->whereUserid($msg->userid)->first();
|
||||
if ($userBot) {
|
||||
$userBot->clear_day = min(intval($clearDay) ?: 30, 999);
|
||||
$userBot->clear_at = Carbon::now()->addDays($userBot->clear_day);
|
||||
$userBot->save();
|
||||
}
|
||||
$data->clear_day = $userBot->clear_day;
|
||||
$data->clear_at = $userBot->clear_at; // 这两个参数只是作为输出,所以不保存
|
||||
} else {
|
||||
$type = "notice";
|
||||
$notice = "机器人不存在。";
|
||||
}
|
||||
break;
|
||||
|
||||
/**
|
||||
* 设置webhook
|
||||
*/
|
||||
case '/webhook':
|
||||
$botId = $isManager ? $array[1] : $botUser->userid;
|
||||
$webhookUrl = $isManager ? $array[2] : $array[1];
|
||||
$data = $this->botManagerOne($botId, $msg->userid);
|
||||
if (strlen($webhookUrl) > 255) {
|
||||
$type = "notice";
|
||||
$notice = "webhook地址最长仅支持255个字符。";
|
||||
} elseif ($data) {
|
||||
$userBot = UserBot::whereBotId($botId)->whereUserid($msg->userid)->first();
|
||||
if ($userBot) {
|
||||
$userBot->webhook_url = $webhookUrl ?: "";
|
||||
$userBot->webhook_num = 0;
|
||||
$userBot->save();
|
||||
}
|
||||
$data->webhook_url = $userBot->webhook_url ?: '-';
|
||||
$data->webhook_num = $userBot->webhook_num; // 这两个参数只是作为输出,所以不保存
|
||||
} else {
|
||||
$type = "notice";
|
||||
$notice = "机器人不存在。";
|
||||
}
|
||||
break;
|
||||
|
||||
/**
|
||||
* 会话搜索
|
||||
*/
|
||||
case '/dialog':
|
||||
$botId = $isManager ? $array[1] : $botUser->userid;
|
||||
$nameKey = $isManager ? $array[2] : $array[1];
|
||||
$data = $this->botManagerOne($botId, $msg->userid);
|
||||
if ($data) {
|
||||
$list = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.updated_at as user_at'])
|
||||
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
|
||||
->where('web_socket_dialogs.name', 'LIKE', "%{$nameKey}%")
|
||||
->where('u.userid', $data->userid)
|
||||
->orderByDesc('u.top_at')
|
||||
->orderByDesc('web_socket_dialogs.last_at')
|
||||
->take(20)
|
||||
->get();
|
||||
if ($list->isEmpty()) {
|
||||
$type = "notice";
|
||||
$notice = "没有搜索到相关会话。";
|
||||
} else {
|
||||
$list->transform(function (WebSocketDialog $item) use ($data) {
|
||||
return $item->formatData($data->userid);
|
||||
});
|
||||
$data->list = $list; // 这个参数只是作为输出,所以不保存
|
||||
}
|
||||
} else {
|
||||
$type = "notice";
|
||||
$notice = "机器人不存在。";
|
||||
}
|
||||
break;
|
||||
}
|
||||
//
|
||||
$text = view('push.bot', [
|
||||
'type' => $type,
|
||||
'data' => $data,
|
||||
'notice' => $notice,
|
||||
'manager' => $isManager,
|
||||
'version' => Base::getVersion()
|
||||
])->render();
|
||||
if (!$isManager) {
|
||||
$text = preg_replace("/\s*\{机器人ID\}/", "", $text);
|
||||
}
|
||||
$text = preg_replace("/^\x20+/", "", $text);
|
||||
$text = preg_replace("/\n\x20+/", "\n", $text);
|
||||
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'text', ['text' => $text], $botUser->userid, false, false, true); // todo 未能在任务end事件来发送任务
|
||||
return;
|
||||
}
|
||||
// 推送Webhook
|
||||
if ($command) {
|
||||
$userBot = UserBot::whereBotId($botUser->userid)->first();
|
||||
if ($userBot && preg_match("/^https*:\/\//", $userBot->webhook_url)) {
|
||||
Ihttp::ihttp_post($userBot->webhook_url, [
|
||||
'text' => $command,
|
||||
'token' => User::generateToken($botUser),
|
||||
'dialog_id' => $msg->dialog_id,
|
||||
'msg_id' => $msg->id,
|
||||
'msg_uid' => $msg->userid,
|
||||
'bot_uid' => $botUser->userid,
|
||||
'version' => Base::getVersion(),
|
||||
], 10);
|
||||
$userBot->webhook_num++;
|
||||
$userBot->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $botId
|
||||
* @param $userid
|
||||
* @return User
|
||||
*/
|
||||
private function botManagerOne($botId, $userid)
|
||||
{
|
||||
$botId = intval($botId);
|
||||
$userid = intval($userid);
|
||||
if ($botId > 0) {
|
||||
return User::select([
|
||||
'users.*',
|
||||
'user_bots.clear_day',
|
||||
'user_bots.clear_at',
|
||||
'user_bots.webhook_url',
|
||||
'user_bots.webhook_num'
|
||||
])
|
||||
->join('user_bots', 'users.userid', '=', 'user_bots.bot_id')
|
||||
->where('users.bot', 1)
|
||||
->where('user_bots.bot_id', $botId)
|
||||
->where('user_bots.userid', $userid)
|
||||
->first();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
95
app/Tasks/CheckinRemindTask.php
Normal file
95
app/Tasks/CheckinRemindTask.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tasks;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\UserCheckinRecord;
|
||||
use App\Models\WebSocketDialog;
|
||||
use App\Models\WebSocketDialogMsg;
|
||||
use App\Module\Base;
|
||||
use App\Module\Extranet;
|
||||
use Cache;
|
||||
use Carbon\Carbon;
|
||||
|
||||
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
|
||||
|
||||
class CheckinRemindTask extends AbstractTask
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
$setting = Base::setting('checkinSetting');
|
||||
if ($setting['open'] !== 'open') {
|
||||
return;
|
||||
}
|
||||
// 判断非工作日
|
||||
if (Extranet::isHoliday(date("Ymd")) > 0) {
|
||||
return;
|
||||
}
|
||||
//
|
||||
$times = $setting['time'] ? Base::json2array($setting['time']) : ['09:00', '18:00'];
|
||||
$remindin = (intval($setting['remindin']) ?: 5) * 60;
|
||||
$remindexceed = (intval($setting['remindexceed']) ?: 10) * 60;
|
||||
//
|
||||
$nowDate = date("Y-m-d");
|
||||
$timeStart = strtotime("{$nowDate} {$times[0]}");
|
||||
//
|
||||
if ($remindin > 0) {
|
||||
$timeRemindin = $timeStart - $remindin;
|
||||
if ($timeRemindin <= Base::time() && Base::time() <= $timeStart) {
|
||||
// 签到打卡提醒
|
||||
$this->remind('in');
|
||||
}
|
||||
}
|
||||
if ($remindexceed > 0) {
|
||||
$timeRemindexceed = $timeStart + $remindexceed;
|
||||
if ($timeRemindexceed <= Base::time() && Base::time() <= $timeRemindexceed + 300) {
|
||||
// 签到缺卡提醒
|
||||
$this->remind('exceed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function end()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private function remind($type)
|
||||
{
|
||||
if (Cache::get("CheckinRemindTask:remind-" . $type) == date("Ymd")) {
|
||||
return;
|
||||
}
|
||||
Cache::put("CheckinRemindTask:remind-" . $type, date("Ymd"), Carbon::now()->addDay());
|
||||
//
|
||||
$botUser = User::botGetOrCreate('check-in');
|
||||
if (!$botUser) {
|
||||
return;
|
||||
}
|
||||
// 提醒对象:3天内有签到的成员(在职)
|
||||
User::whereBot(0)->whereNull('disable_at')->chunk(100, function ($users) use ($type, $botUser) {
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
if (UserCheckinRecord::whereUserid($user->userid)->where('date', date("Y-m-d"))->exists()) {
|
||||
continue; // 已打卡
|
||||
}
|
||||
if (!UserCheckinRecord::whereUserid($user->userid)->where('created_at', '>', Carbon::now()->subDays(3))->exists()) {
|
||||
continue; // 3天内没有打卡
|
||||
}
|
||||
$dialog = WebSocketDialog::checkUserDialog($botUser, $user->userid);
|
||||
if ($dialog) {
|
||||
if ($type === 'exceed') {
|
||||
$text = "<p><strong style='color:red'>缺卡提醒:</strong>上班时间到了,你还没有打卡哦~</p>";
|
||||
} else {
|
||||
$text = "<p><strong>打卡提醒:</strong>快到上班时间了,别忘了打卡哦~</p>";
|
||||
}
|
||||
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
44
app/Tasks/DeleteBotMsgTask.php
Normal file
44
app/Tasks/DeleteBotMsgTask.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tasks;
|
||||
|
||||
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
|
||||
|
||||
use App\Models\UserBot;
|
||||
use App\Models\WebSocketDialogMsg;
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* 删除机器人消息
|
||||
*/
|
||||
class DeleteBotMsgTask extends AbstractTask
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
$bots = UserBot::where('clear_at', '<=', Carbon::now())->take(100)->get();
|
||||
foreach ($bots as $bot) {
|
||||
WebSocketDialogMsg::whereUserid($bot->bot_id)
|
||||
->where('created_at', '<=', Carbon::now()->subDays($bot->clear_day))
|
||||
->orderBy('id')
|
||||
->chunk(1000, function ($msgs) {
|
||||
$ids = $msgs->pluck('id')->toArray();
|
||||
if ($ids) {
|
||||
WebSocketDialogMsg::deleteMsgs($ids);
|
||||
}
|
||||
});
|
||||
$bot->clear_at = Carbon::now()->addDays($bot->clear_day);
|
||||
$bot->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function end()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Tasks;
|
||||
|
||||
use App\Models\File;
|
||||
use App\Models\TaskWorker;
|
||||
use App\Models\Tmp;
|
||||
use App\Models\WebSocketTmpMsg;
|
||||
@@ -17,7 +18,11 @@ class DeleteTmpTask extends AbstractTask
|
||||
protected $data;
|
||||
protected $hours; // 多久后删除,单位小时
|
||||
|
||||
public function __construct(string $data, int $hours)
|
||||
/**
|
||||
* @param string $data
|
||||
* @param int $hours
|
||||
*/
|
||||
public function __construct(string $data, int $hours = 24)
|
||||
{
|
||||
parent::__construct(...func_get_args());
|
||||
$this->data = $data;
|
||||
@@ -28,13 +33,14 @@ class DeleteTmpTask extends AbstractTask
|
||||
{
|
||||
switch ($this->data) {
|
||||
/**
|
||||
* 表pre_wg_tmp_msgs
|
||||
* 表pre_tmp_msgs
|
||||
*/
|
||||
case 'wg_tmp_msgs':
|
||||
{
|
||||
WebSocketTmpMsg::where('created_at', '<', Carbon::now()->subHours($this->hours)->toDateTimeString())
|
||||
WebSocketTmpMsg::where('created_at', '<', Carbon::now()->subHours($this->hours))
|
||||
->orderBy('id')
|
||||
->chunk(500, function ($msgs) {
|
||||
/** @var WebSocketTmpMsg $msg */
|
||||
foreach ($msgs as $msg) {
|
||||
$msg->delete();
|
||||
}
|
||||
@@ -43,13 +49,14 @@ class DeleteTmpTask extends AbstractTask
|
||||
break;
|
||||
|
||||
/**
|
||||
* 表pre_wg_tmp
|
||||
* 表pre_tmp
|
||||
*/
|
||||
case 'tmp':
|
||||
{
|
||||
Tmp::where('created_at', '<', Carbon::now()->subHours($this->hours)->toDateTimeString())
|
||||
Tmp::where('created_at', '<', Carbon::now()->subHours($this->hours))
|
||||
->orderBy('id')
|
||||
->chunk(2000, function ($tmps) {
|
||||
->chunk(500, function ($tmps) {
|
||||
/** @var Tmp $tmp */
|
||||
foreach ($tmps as $tmp) {
|
||||
$tmp->delete();
|
||||
}
|
||||
@@ -60,14 +67,35 @@ class DeleteTmpTask extends AbstractTask
|
||||
/**
|
||||
* 表pre_task_worker
|
||||
*/
|
||||
case 'tw':
|
||||
case 'task_worker':
|
||||
{
|
||||
TaskWorker::onlyTrashed()
|
||||
->where('deleted_at', '<', Carbon::now()->subHours($this->hours)->toDateTimeString())
|
||||
->where('deleted_at', '<', Carbon::now()->subHours($this->hours))
|
||||
->orderBy('id')
|
||||
->forceDelete();
|
||||
}
|
||||
break;
|
||||
|
||||
/**
|
||||
* 表pre_file
|
||||
*/
|
||||
case 'file':
|
||||
{
|
||||
$day = intval(env("AUTO_EMPTY_FILE_RECYCLE", 365));
|
||||
if ($day <= 0) {
|
||||
return;
|
||||
}
|
||||
File::onlyTrashed()
|
||||
->where('deleted_at', '<', Carbon::now()->subHours($day))
|
||||
->orderBy('id')
|
||||
->chunk(500, function ($files) {
|
||||
/** @var File $file */
|
||||
foreach ($files as $file) {
|
||||
$file->forceDeleteFile();
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
namespace App\Tasks;
|
||||
|
||||
use App\Models\ProjectTask;
|
||||
use App\Models\ProjectTaskMailLog;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Models\WebSocketDialogMsg;
|
||||
@@ -25,51 +23,6 @@ class EmailNoticeTask extends AbstractTask
|
||||
public function start()
|
||||
{
|
||||
$setting = Base::setting('emailSetting');
|
||||
// 任务通知
|
||||
if ($setting['notice'] === 'open') {
|
||||
$start = intval($setting['task_start_minute']);
|
||||
$hours = floatval($setting['task_remind_hours']);
|
||||
$hours2 = floatval($setting['task_remind_hours2']);
|
||||
if ($start > -1) {
|
||||
ProjectTask::whereNull("complete_at")
|
||||
->whereNull("archived_at")
|
||||
->whereBetween("start_at", [
|
||||
Carbon::now()->subMinutes($start + 10),
|
||||
Carbon::now()->subMinutes($start)
|
||||
])->chunkById(100, function ($tasks) {
|
||||
/** @var ProjectTask $task */
|
||||
foreach ($tasks as $task) {
|
||||
$this->taskEmail($task, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($hours > -1) {
|
||||
ProjectTask::whereNull("complete_at")
|
||||
->whereNull("archived_at")
|
||||
->whereBetween("end_at", [
|
||||
Carbon::now()->addMinutes($hours * 60),
|
||||
Carbon::now()->addMinutes($hours * 60 + 10)
|
||||
])->chunkById(100, function ($tasks) {
|
||||
/** @var ProjectTask $task */
|
||||
foreach ($tasks as $task) {
|
||||
$this->taskEmail($task, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
if ($hours2 > -1) {
|
||||
ProjectTask::whereNull("complete_at")
|
||||
->whereNull("archived_at")
|
||||
->whereBetween("end_at", [
|
||||
Carbon::now()->subMinutes($hours2 * 60 + 10),
|
||||
Carbon::now()->subMinutes($hours2 * 60)
|
||||
])->chunkById(100, function ($tasks) {
|
||||
/** @var ProjectTask $task */
|
||||
foreach ($tasks as $task) {
|
||||
$this->taskEmail($task, 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// 消息通知
|
||||
if ($setting['notice_msg'] === 'open') {
|
||||
$userMinute = intval($setting['msg_unread_user_minute']);
|
||||
@@ -78,6 +31,7 @@ class EmailNoticeTask extends AbstractTask
|
||||
$builder = WebSocketDialogMsg::select(['web_socket_dialog_msgs.*', 'r.id as r_id', 'r.userid as r_userid'])
|
||||
->join('web_socket_dialog_msg_reads as r', 'web_socket_dialog_msgs.id', '=', 'r.msg_id')
|
||||
->whereNull("r.read_at")
|
||||
->where("r.silence", 0)
|
||||
->where("r.email", 0);
|
||||
if ($userMinute > -1) {
|
||||
$builder->clone()
|
||||
@@ -113,70 +67,6 @@ class EmailNoticeTask extends AbstractTask
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务过期前、超期后提醒
|
||||
* @param ProjectTask $task
|
||||
* @param int $type
|
||||
* @return void
|
||||
*/
|
||||
private function taskEmail(ProjectTask $task, int $type)
|
||||
{
|
||||
$userids = $task->taskUser->where('owner', 1)->pluck('userid')->toArray();
|
||||
if (empty($userids)) {
|
||||
return;
|
||||
}
|
||||
$users = User::whereIn('userid', $userids)->whereNull('disable_at')->get();
|
||||
if (empty($users)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$setting = Base::setting('emailSetting');
|
||||
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$data = [
|
||||
'type' => $type,
|
||||
'userid' => $user->userid,
|
||||
'task_id' => $task->id,
|
||||
];
|
||||
$emailLog = ProjectTaskMailLog::where($data)->exists();
|
||||
if ($emailLog) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
if (!Base::isEmail($user->email)) {
|
||||
throw new \Exception("User email '{$user->email}' address error");
|
||||
}
|
||||
$subject = match ($type) {
|
||||
1 => "任务提醒",
|
||||
2 => "任务过期提醒",
|
||||
default => "任务开始提醒",
|
||||
};
|
||||
$content = view('email.task', [
|
||||
'type' => str_replace([0, 1, 2], ['start', 'before', 'after'], $type),
|
||||
'user' => $user,
|
||||
'task' => $task,
|
||||
'setting' => $setting,
|
||||
])->render();
|
||||
Setting::validateAddr($user->email, function($to) use ($content, $subject, $setting) {
|
||||
Factory::mailer()
|
||||
->setDsn("smtp://{$setting['account']}:{$setting['password']}@{$setting['smtp_server']}:{$setting['port']}?verify_peer=0")
|
||||
->setMessage(EmailMessage::create()
|
||||
->from(env('APP_NAME', 'Task') . " <{$setting['account']}>")
|
||||
->to($to)
|
||||
->subject($subject)
|
||||
->html($content))
|
||||
->send();
|
||||
});
|
||||
$data['is_send'] = 1;
|
||||
} catch (\Throwable $e) {
|
||||
$data['send_error'] = $e->getMessage();
|
||||
}
|
||||
$data['email'] = $user->email;
|
||||
ProjectTaskMailLog::createInstance($data)->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 未读消息通知
|
||||
* @param $rows
|
||||
@@ -190,6 +80,7 @@ class EmailNoticeTask extends AbstractTask
|
||||
$data = WebSocketDialogMsg::select(['web_socket_dialog_msgs.*', 'r.id as r_id', 'r.userid as r_userid'])
|
||||
->join('web_socket_dialog_msg_reads as r', 'web_socket_dialog_msgs.id', '=', 'r.msg_id')
|
||||
->whereNull("r.read_at")
|
||||
->where("r.silence", 0)
|
||||
->where("r.email", 0)
|
||||
->where("r.userid", $userid)
|
||||
->where("web_socket_dialog_msgs.dialog_type", $dialogType)
|
||||
@@ -199,7 +90,7 @@ class EmailNoticeTask extends AbstractTask
|
||||
if (empty($data)) {
|
||||
continue;
|
||||
}
|
||||
$user = User::whereNull('disable_at')->find($userid);
|
||||
$user = User::whereBot(0)->whereNull('disable_at')->find($userid);
|
||||
if (empty($user)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
57
app/Tasks/JokeSoupTask.php
Normal file
57
app/Tasks/JokeSoupTask.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tasks;
|
||||
|
||||
use App\Module\Base;
|
||||
use App\Module\Extranet;
|
||||
use Cache;
|
||||
use Carbon\Carbon;
|
||||
|
||||
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
|
||||
|
||||
|
||||
/**
|
||||
* 获取笑话、心灵鸡汤
|
||||
*
|
||||
* 在.env添加笑话 JUKE_KEY_JOKE
|
||||
* 在.env添加鸡汤 JUKE_KEY_SOUP
|
||||
*
|
||||
* 每日小时采集1次
|
||||
*/
|
||||
class JokeSoupTask extends AbstractTask
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
// 判断每小时执行一次
|
||||
if (Cache::get("JokeSoupTask:YmdH") == date("YmdH")) {
|
||||
return;
|
||||
}
|
||||
Cache::put("JokeSoupTask:YmdH", date("YmdH"), Carbon::now()->addDay());
|
||||
//
|
||||
$array = Base::json2array(Cache::get("JokeSoupTask:jokes"));
|
||||
$data = Extranet::randJoke();
|
||||
foreach ($data as $item) {
|
||||
if ($text = trim($item['content'])) {
|
||||
$array[] = $text;
|
||||
}
|
||||
}
|
||||
Cache::forever("JokeSoupTask:jokes", Base::array2json(array_slice($array, -100)));
|
||||
//
|
||||
$array = Base::json2array(Cache::get("JokeSoupTask:soups"));
|
||||
$data = Extranet::soups();
|
||||
if ($data) {
|
||||
$array[] = $data;
|
||||
}
|
||||
Cache::forever("JokeSoupTask:soups", Base::array2json(array_slice($array, -24)));
|
||||
}
|
||||
|
||||
public function end()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ namespace App\Tasks;
|
||||
use App\Models\WebSocket;
|
||||
use App\Models\WebSocketTmpMsg;
|
||||
use App\Module\Base;
|
||||
use App\Module\Doo;
|
||||
use Cache;
|
||||
use Carbon\Carbon;
|
||||
use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||
@@ -111,7 +112,7 @@ class PushTask extends AbstractTask
|
||||
*/
|
||||
public static function push(array $lists, $retryOffline = true, $key = null, $delay = 1)
|
||||
{
|
||||
if (!is_array($lists) || empty($lists)) {
|
||||
if (empty($lists)) {
|
||||
return;
|
||||
}
|
||||
if (!Base::isTwoArray($lists)) {
|
||||
@@ -177,10 +178,12 @@ class PushTask extends AbstractTask
|
||||
Task::deliver($task);
|
||||
} else {
|
||||
try {
|
||||
$swoole->push($fid, Base::array2json($msg));
|
||||
$tmpMsgId > 0 && WebSocketTmpMsg::whereId($tmpMsgId)->update(['send' => 1]);
|
||||
$swoole->push($fid, self::pushMsgFormat($fid, $msg));
|
||||
if ($tmpMsgId > 0) {
|
||||
WebSocketTmpMsg::whereId($tmpMsgId)->update(['send' => 1]);
|
||||
}
|
||||
} catch (\Throwable) {
|
||||
|
||||
// 发送失败
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,4 +194,22 @@ class PushTask extends AbstractTask
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化推送消息
|
||||
* @param $fid
|
||||
* @param $msg
|
||||
* @return string
|
||||
*/
|
||||
private static function pushMsgFormat($fid, $msg)
|
||||
{
|
||||
$encrypt = Base::json2array(Cache::get("User::encrypt:" . $fid));
|
||||
if ($encrypt['type'] == 'pgp') {
|
||||
$msg = [
|
||||
'type' => 'encrypt',
|
||||
'encrypted' => Doo::pgpEncryptApi($msg, $encrypt['key']),
|
||||
];
|
||||
}
|
||||
return Base::array2json($msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use App\Models\WebSocketDialog;
|
||||
use App\Models\WebSocketDialogMsg;
|
||||
use App\Models\WebSocketDialogMsgRead;
|
||||
use App\Module\Base;
|
||||
use Carbon\Carbon;
|
||||
use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||
use Request;
|
||||
|
||||
@@ -23,7 +24,7 @@ class WebSocketDialogMsgTask extends AbstractTask
|
||||
protected $id;
|
||||
protected $ignoreFd;
|
||||
protected $msgNotExistRetry = false; // 推送失败后重试
|
||||
protected $silence = false; // 静默推送(1:前端不通知、2:App不推送)
|
||||
protected $silence = false; // 静默推送(前端不通知、App不推送,如果会话设置了免打扰则强制静默)
|
||||
protected $endPush = [];
|
||||
protected $endArray = [];
|
||||
|
||||
@@ -84,6 +85,13 @@ class WebSocketDialogMsgTask extends AbstractTask
|
||||
if (empty($dialog)) {
|
||||
return;
|
||||
}
|
||||
$updateds = [];
|
||||
$silences = [];
|
||||
foreach ($dialog->dialogUser as $dialogUser) {
|
||||
$updateds[$dialogUser->userid] = $dialogUser->updated_at;
|
||||
$silences[$dialogUser->userid] = $dialogUser->silence;
|
||||
}
|
||||
$userids = array_keys($silences);
|
||||
|
||||
// 提及会员
|
||||
$mentions = [];
|
||||
@@ -95,7 +103,6 @@ class WebSocketDialogMsgTask extends AbstractTask
|
||||
}
|
||||
|
||||
// 将会话以外的成员加入会话内
|
||||
$userids = $dialog->dialogUser->pluck('userid')->toArray();
|
||||
$diffids = array_values(array_diff($mentions, $userids));
|
||||
if ($diffids) {
|
||||
// 仅(群聊)且(是群主或没有群主)才可以@成员以外的人
|
||||
@@ -109,57 +116,78 @@ class WebSocketDialogMsgTask extends AbstractTask
|
||||
// 推送目标①:会话成员/群成员
|
||||
$array = [];
|
||||
foreach ($userids AS $userid) {
|
||||
$silence = $this->silence || $silences[$userid];
|
||||
$updated = $updateds[$userid] ?? $msg->created_at;
|
||||
if ($userid == $msg->userid) {
|
||||
$array[$userid] = false;
|
||||
$array[$userid] = [
|
||||
'userid' => $userid,
|
||||
'mention' => false,
|
||||
'silence' => $silence,
|
||||
'updated' => $updated,
|
||||
];
|
||||
} else {
|
||||
$mention = array_intersect([0, $userid], $mentions) ? 1 : 0;
|
||||
$silence = $mention ? false : $silence;
|
||||
WebSocketDialogMsgRead::createInstance([
|
||||
'dialog_id' => $msg->dialog_id,
|
||||
'msg_id' => $msg->id,
|
||||
'userid' => $userid,
|
||||
'mention' => $mention,
|
||||
'silence' => $silence,
|
||||
])->saveOrIgnore();
|
||||
$array[$userid] = $mention;
|
||||
$array[$userid] = [
|
||||
'userid' => $userid,
|
||||
'mention' => $mention,
|
||||
'silence' => $silence,
|
||||
'updated' => $updated,
|
||||
];
|
||||
// 机器人收到消处理
|
||||
$botUser = User::whereUserid($userid)->whereBot(1)->first();
|
||||
if ($botUser) {
|
||||
$this->endArray[] = new BotReceiveMsgTask($botUser->userid, $msg->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 更新已发送数量
|
||||
$msg->send = WebSocketDialogMsgRead::whereMsgId($msg->id)->count();
|
||||
$msg->save();
|
||||
// 开始推送消息
|
||||
foreach ($array as $userid => $mention) {
|
||||
$umengUserid = [];
|
||||
foreach ($array as $item) {
|
||||
$this->endPush[] = [
|
||||
'userid' => $userid,
|
||||
'userid' => $item['userid'],
|
||||
'ignoreFd' => $this->ignoreFd,
|
||||
'msg' => [
|
||||
'type' => 'dialog',
|
||||
'mode' => 'add',
|
||||
'silence' => $this->silence ? 1 : 0,
|
||||
'silence' => $item['silence'] ? 1 : 0,
|
||||
'data' => array_merge($msg->toArray(), [
|
||||
'mention' => $mention,
|
||||
'mention' => $item['mention'],
|
||||
'user_at' => Carbon::parse($item['updated'])->toDateTimeString('millisecond'),
|
||||
'user_ms' => Carbon::parse($item['updated'])->valueOf(),
|
||||
]),
|
||||
]
|
||||
];
|
||||
if ($item['userid'] != $msg->userid && !$item['silence'] && !$this->silence) {
|
||||
$umengUserid[] = $item['userid'];
|
||||
}
|
||||
}
|
||||
// umeng推送app
|
||||
$setting = Base::setting('appPushSetting');
|
||||
$pushMsg = $setting['push'] === 'open' && $setting['push_msg'] !== 'close';
|
||||
if (!$this->silence && $pushMsg) {
|
||||
$umengUserid = $array;
|
||||
if (isset($umengUserid[$msg->userid])) {
|
||||
unset($umengUserid[$msg->userid]);
|
||||
if ($umengUserid) {
|
||||
$setting = Base::setting('appPushSetting');
|
||||
if ($setting['push'] === 'open') {
|
||||
$umengTitle = User::userid2nickname($msg->userid);
|
||||
if ($dialog->type == 'group') {
|
||||
$umengTitle = "{$dialog->getGroupName()} ($umengTitle)";
|
||||
}
|
||||
$this->endArray[] = new PushUmengMsg($umengUserid, [
|
||||
'title' => $umengTitle,
|
||||
'body' => $msg->previewMsg(),
|
||||
'description' => "MID:{$msg->id}",
|
||||
'seconds' => 3600,
|
||||
'badge' => 1,
|
||||
]);
|
||||
}
|
||||
$umengUserid = array_keys($umengUserid);
|
||||
$umengTitle = User::userid2nickname($msg->userid);
|
||||
if ($dialog->type == 'group') {
|
||||
$umengTitle = "{$dialog->getGroupName()} ($umengTitle)";
|
||||
}
|
||||
$this->endArray[] = new PushUmengMsg($umengUserid, [
|
||||
'title' => $umengTitle,
|
||||
'body' => $msg->previewMsg(),
|
||||
'description' => "MID:{$msg->id}",
|
||||
'seconds' => 3600,
|
||||
'badge' => 1,
|
||||
]);
|
||||
}
|
||||
|
||||
// 推送目标②:正在打开这个任务会话的会员
|
||||
@@ -167,9 +195,9 @@ class WebSocketDialogMsgTask extends AbstractTask
|
||||
$list = User::whereTaskDialogId($dialog->id)->pluck('userid')->toArray();
|
||||
if ($list) {
|
||||
$array = [];
|
||||
foreach ($list as $uid) {
|
||||
if (!in_array($uid, $userids)) {
|
||||
$array[] = $uid;
|
||||
foreach ($list as $item) {
|
||||
if (!in_array($item, $userids)) {
|
||||
$array[] = $item;
|
||||
}
|
||||
}
|
||||
if ($array) {
|
||||
@@ -180,7 +208,10 @@ class WebSocketDialogMsgTask extends AbstractTask
|
||||
'type' => 'dialog',
|
||||
'mode' => 'chat',
|
||||
'silence' => $this->silence ? 1 : 0,
|
||||
'data' => $msg->toArray(),
|
||||
'data' => array_merge($msg->toArray(), [
|
||||
'user_at' => Carbon::parse($msg->created_at)->toDateTimeString('millisecond'),
|
||||
'user_ms' => Carbon::parse($msg->created_at)->valueOf(),
|
||||
]),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
15
bin/auto
Executable file
15
bin/auto
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ ! -f ".env" ]; then
|
||||
echo -e "配置文件不存在!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
debug=`cat .env | grep "^APP_DEBUG=" | awk -F '=' '{print $2}'`
|
||||
if [ "$debug" = "true" ]; then
|
||||
echo "[MODE] development"
|
||||
./bin/inotify ./app
|
||||
else
|
||||
echo "[MODE] production"
|
||||
php bin/laravels start -i
|
||||
fi
|
||||
89
bin/run
89
bin/run
@@ -1,89 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class runLoader
|
||||
*/
|
||||
class runLoader
|
||||
{
|
||||
public function randString($length = 16)
|
||||
{
|
||||
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
$passwordstr = '';
|
||||
$max = strlen($chars) - 1;
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$passwordstr .= $chars[mt_rand(0, $max)];
|
||||
}
|
||||
return $passwordstr;
|
||||
}
|
||||
|
||||
function getEnv(string $key)
|
||||
{
|
||||
if (empty($key) || !is_string($key)) {
|
||||
return '';
|
||||
}
|
||||
$envPath = realpath(__DIR__ . '/../') . DIRECTORY_SEPARATOR . '.env';
|
||||
if (!file_exists($envPath)) {
|
||||
return false;
|
||||
}
|
||||
$envContent = file_get_contents($envPath);
|
||||
preg_match_all("/^" . $key . "\s*=\s*(.*?)$/m", $envContent, $matchs);
|
||||
return $matchs[1] ?: '';
|
||||
}
|
||||
|
||||
function modifyEnv(array $data)
|
||||
{
|
||||
if (empty($data) || !is_array($data)) {
|
||||
return false;
|
||||
}
|
||||
$envPath = realpath(__DIR__ . '/../') . DIRECTORY_SEPARATOR . '.env';
|
||||
if (!file_exists($envPath)) {
|
||||
return false;
|
||||
}
|
||||
$envContent = file_get_contents($envPath);
|
||||
foreach ($data as $key => $val) {
|
||||
$envContent = preg_replace("/^" . $key . "\s*=\s*(.*?)$/m", $key . "=" . $val, $envContent);
|
||||
}
|
||||
file_put_contents($envPath, $envContent);
|
||||
return true;
|
||||
}
|
||||
|
||||
function modifyMode($type)
|
||||
{
|
||||
$filePath = realpath(__DIR__ . '/../') . DIRECTORY_SEPARATOR . '/docker/php/php.conf';
|
||||
if (!file_exists($filePath)) {
|
||||
return false;
|
||||
}
|
||||
$envContent = file_get_contents($filePath);
|
||||
$envContent = str_replace("#command=php bin/laravels start -i", "command=php bin/laravels start -i", $envContent);
|
||||
$envContent = str_replace("#command=./bin/inotify ./app", "command=./bin/inotify ./app", $envContent);
|
||||
if ($type == "dev") {
|
||||
$envContent = str_replace("command=php bin/laravels start -i", "#command=php bin/laravels start -i", $envContent);
|
||||
$this->modifyEnv([
|
||||
'APP_DEBUG' => 'true'
|
||||
]);
|
||||
} else {
|
||||
$envContent = str_replace("command=./bin/inotify ./app", "#command=./bin/inotify ./app", $envContent);
|
||||
$this->modifyEnv([
|
||||
'APP_DEBUG' => 'false'
|
||||
]);
|
||||
}
|
||||
file_put_contents($filePath, $envContent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$array = getopt('', ['port:', 'mode:']);
|
||||
$loader = new runLoader();
|
||||
|
||||
if (isset($array['mode'])) {
|
||||
$loader->modifyMode($array['mode']);
|
||||
}
|
||||
|
||||
$data = [];
|
||||
if (isset($array['port'])) {
|
||||
$data['APP_PORT'] = $array['port'];
|
||||
}
|
||||
if ($data) {
|
||||
$loader->modifyEnv($data);
|
||||
}
|
||||
@@ -57,4 +57,4 @@ ignore_tags = ""
|
||||
# sort the tags chronologically
|
||||
date_order = false
|
||||
# sort the commits inside sections by oldest/newest order
|
||||
sort_commits = "oldest"
|
||||
sort_commits = "newest"
|
||||
|
||||
104
cmd
104
cmd
@@ -41,15 +41,31 @@ rand_string() {
|
||||
fi
|
||||
}
|
||||
|
||||
supervisorctl_restart() {
|
||||
local RES=`run_exec php "supervisorctl update $1"`
|
||||
restart_php() {
|
||||
local RES=`run_exec php "supervisorctl update php"`
|
||||
if [ -z "$RES" ]; then
|
||||
run_exec php "supervisorctl restart $1"
|
||||
RES=`run_exec php "supervisorctl restart php"`
|
||||
fi
|
||||
local IN=`echo $RES | grep "ERROR"`
|
||||
if [[ "$IN" != "" ]]; then
|
||||
$COMPOSE stop php
|
||||
$COMPOSE start php
|
||||
else
|
||||
echo -e "$RES"
|
||||
fi
|
||||
}
|
||||
|
||||
switch_debug() {
|
||||
local debug="false"
|
||||
if [[ "$1" == "true" ]] || [[ "$1" == "dev" ]] || [[ "$1" == "open" ]]; then
|
||||
debug="true"
|
||||
fi
|
||||
if [[ "$(env_get APP_DEBUG)" != "$debug" ]]; then
|
||||
env_set APP_DEBUG "$debug"
|
||||
restart_php
|
||||
fi
|
||||
}
|
||||
|
||||
check_docker() {
|
||||
docker --version &> /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
@@ -90,15 +106,17 @@ run_compile() {
|
||||
if [ ! -d "./node_modules" ]; then
|
||||
npm install
|
||||
fi
|
||||
run_exec php "php bin/run --mode=$type"
|
||||
supervisorctl_restart php
|
||||
if [ "$type" = "dev" ]; then
|
||||
echo "<script>window.location.href=window.location.href.replace(/:\d+/, ':' + $(env_get APP_PORT))</script>" > ./index.html
|
||||
env_set APP_DEV_PORT $(rand 20001 30000)
|
||||
fi
|
||||
switch_debug "$type"
|
||||
#
|
||||
if [ "$type" = "prod" ]; then
|
||||
rm -rf "./public/js/build"
|
||||
npx mix --production
|
||||
echo "$(rand_string 16)" > ./public/js/hash
|
||||
npx vite build -- fromcmd
|
||||
else
|
||||
npx mix watch --hot
|
||||
npx vite -- fromcmd
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -117,18 +135,16 @@ run_electron() {
|
||||
if [ -d "./electron/dist" ]; then
|
||||
rm -rf "./electron/dist"
|
||||
fi
|
||||
if [ -d "./electron/public" ] && [ "$argv" != "--nobuild" ]; then
|
||||
if [ -d "./electron/public" ]; then
|
||||
rm -rf "./electron/public"
|
||||
fi
|
||||
mkdir -p ./electron/public
|
||||
cp ./electron/index.html ./electron/public/index.html
|
||||
#
|
||||
if [ "$argv" != "dev" ] && [ "$argv" != "--nobuild" ]; then
|
||||
npx mix --production -- --env --electron
|
||||
fi
|
||||
if [ "$argv" == "dev" ]; then
|
||||
run_exec php "php bin/run --mode=$argv"
|
||||
supervisorctl_restart php
|
||||
switch_debug "$argv"
|
||||
else
|
||||
mkdir -p ./electron/public
|
||||
cp ./electron/index.html ./electron/public/index.html
|
||||
npx vite build -- fromcmd electronBuild
|
||||
fi
|
||||
node ./electron/build.js $argv
|
||||
}
|
||||
@@ -243,15 +259,6 @@ arg_get() {
|
||||
echo $value
|
||||
}
|
||||
|
||||
is_arm() {
|
||||
local get_arch=`arch`
|
||||
if [[ $get_arch =~ "aarch" ]] || [[ $get_arch =~ "arm" ]]; then
|
||||
echo "yes"
|
||||
else
|
||||
echo "no"
|
||||
fi
|
||||
}
|
||||
|
||||
####################################################################################
|
||||
####################################################################################
|
||||
####################################################################################
|
||||
@@ -264,11 +271,6 @@ fi
|
||||
if [ $# -gt 0 ]; then
|
||||
if [[ "$1" == "init" ]] || [[ "$1" == "install" ]]; then
|
||||
shift 1
|
||||
# 判断架构
|
||||
if [[ "$(is_arm)" == "yes" ]] && [[ -z "$(arg_get force)" ]]; then
|
||||
echo -e "${Error} ${RedBG}暂不支持arm架构,强制安装请使用:./cmd install --force${Font}"
|
||||
exit 1
|
||||
fi
|
||||
# 初始化文件
|
||||
if [[ -n "$(arg_get relock)" ]]; then
|
||||
rm -rf node_modules
|
||||
@@ -295,7 +297,7 @@ if [ $# -gt 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
[[ -z "$(env_get APP_KEY)" ]] && run_exec php "php artisan key:generate"
|
||||
run_exec php "php bin/run --mode=prod"
|
||||
switch_debug "false"
|
||||
# 检查数据库
|
||||
remaining=20
|
||||
while [ ! -f "${cur_path}/docker/mysql/data/$(env_get DB_DATABASE)/db.opt" ]; do
|
||||
@@ -315,24 +317,26 @@ if [ $# -gt 0 ]; then
|
||||
# 设置初始化密码
|
||||
res=`run_exec mariadb "sh /etc/mysql/repassword.sh"`
|
||||
$COMPOSE up -d
|
||||
supervisorctl_restart php
|
||||
restart_php
|
||||
echo -e "${OK} ${GreenBG} 安装完成 ${Font}"
|
||||
echo -e "地址: http://${GreenBG}127.0.0.1:$(env_get APP_PORT)${Font}"
|
||||
echo -e "$res"
|
||||
elif [[ "$1" == "update" ]]; then
|
||||
shift 1
|
||||
run_mysql backup
|
||||
git fetch --all
|
||||
git reset --hard origin/$(git branch | sed -n -e 's/^\* \(.*\)/\1/p')
|
||||
git pull
|
||||
run_exec php "composer update"
|
||||
if [[ -z "$(arg_get local)" ]]; then
|
||||
git fetch --all
|
||||
git reset --hard origin/$(git branch | sed -n -e 's/^\* \(.*\)/\1/p')
|
||||
git pull
|
||||
run_exec php "composer update"
|
||||
fi
|
||||
run_exec php "php artisan migrate"
|
||||
supervisorctl_restart php
|
||||
restart_php
|
||||
$COMPOSE up -d
|
||||
elif [[ "$1" == "uninstall" ]]; then
|
||||
shift 1
|
||||
read -rp "确定要卸载(含:删除容器、数据库、日志)吗?(y/n): " uninstall
|
||||
[[ -z ${uninstall} ]] && uninstall="N"
|
||||
read -rp "确定要卸载(含:删除容器、数据库、日志)吗?(Y/n): " uninstall
|
||||
[[ -z ${uninstall} ]] && uninstall="Y"
|
||||
case $uninstall in
|
||||
[yY][eE][sS] | [yY])
|
||||
echo -e "${RedBG} 开始卸载... ${Font}"
|
||||
@@ -343,6 +347,7 @@ if [ $# -gt 0 ]; then
|
||||
;;
|
||||
esac
|
||||
$COMPOSE down
|
||||
env_set APP_DEBUG "false"
|
||||
rm -rf "./docker/mysql/data"
|
||||
rm -rf "./docker/log/supervisor"
|
||||
find "./storage/logs" -name "*.log" | xargs rm -rf
|
||||
@@ -361,22 +366,22 @@ if [ $# -gt 0 ]; then
|
||||
elif [[ "$1" == "url" ]]; then
|
||||
shift 1
|
||||
env_set APP_URL "$1"
|
||||
supervisorctl_restart php
|
||||
restart_php
|
||||
echo -e "${OK} ${GreenBG} 修改成功 ${Font}"
|
||||
elif [[ "$1" == "env" ]]; then
|
||||
shift 1
|
||||
if [ -n "$1" ]; then
|
||||
env_set $1 "$2"
|
||||
fi
|
||||
supervisorctl_restart php
|
||||
restart_php
|
||||
echo -e "${OK} ${GreenBG} 修改成功 ${Font}"
|
||||
elif [[ "$1" == "repassword" ]]; then
|
||||
shift 1
|
||||
run_exec mariadb "sh /etc/mysql/repassword.sh \"$@\""
|
||||
elif [[ "$1" == "dev" ]] || [[ "$1" == "development" ]]; then
|
||||
elif [[ "$1" == "serve" ]] || [[ "$1" == "dev" ]] || [[ "$1" == "development" ]]; then
|
||||
shift 1
|
||||
run_compile dev
|
||||
elif [[ "$1" == "prod" ]] || [[ "$1" == "production" ]]; then
|
||||
elif [[ "$1" == "build" ]] || [[ "$1" == "prod" ]] || [[ "$1" == "production" ]]; then
|
||||
shift 1
|
||||
run_compile prod
|
||||
elif [[ "$1" == "appbuild" ]] || [[ "$1" == "buildapp" ]]; then
|
||||
@@ -391,12 +396,8 @@ if [ $# -gt 0 ]; then
|
||||
docker run -it --rm -v ${cur_path}:/home/node/apidoc kuaifan/apidoc -i app/Http/Controllers/Api -o public/docs
|
||||
elif [[ "$1" == "debug" ]]; then
|
||||
shift 1
|
||||
if [[ "$@" == "close" ]]; then
|
||||
env_set APP_DEBUG "false"
|
||||
else
|
||||
env_set APP_DEBUG "true"
|
||||
fi
|
||||
supervisorctl_restart php
|
||||
switch_debug "$@"
|
||||
echo "success"
|
||||
elif [[ "$1" == "https" ]]; then
|
||||
shift 1
|
||||
if [[ "$@" == "auto" ]]; then
|
||||
@@ -404,7 +405,7 @@ if [ $# -gt 0 ]; then
|
||||
else
|
||||
env_set APP_SCHEME "true"
|
||||
fi
|
||||
supervisorctl_restart php
|
||||
restart_php
|
||||
elif [[ "$1" == "artisan" ]]; then
|
||||
shift 1
|
||||
e="php artisan $@" && run_exec php "$e"
|
||||
@@ -439,6 +440,9 @@ if [ $# -gt 0 ]; then
|
||||
shift 1
|
||||
run_exec php "php app/Models/clearHelper.php"
|
||||
run_exec php "php artisan ide-helper:models -W"
|
||||
elif [[ "$1" == "translate" ]]; then
|
||||
shift 1
|
||||
run_exec php "cd /var/www/language && php translate.php"
|
||||
elif [[ "$1" == "test" ]]; then
|
||||
shift 1
|
||||
e="./vendor/bin/phpunit $@" && run_exec php "$e"
|
||||
|
||||
@@ -13,13 +13,15 @@
|
||||
"ext-gd": "*",
|
||||
"ext-json": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-simplexml": "*",
|
||||
"directorytree/ldaprecord-laravel": "^2.7",
|
||||
"fideloper/proxy": "^4.4.1",
|
||||
"fruitcake/laravel-cors": "^2.0.4",
|
||||
"guanguans/notify": "^1.21.1",
|
||||
"guzzlehttp/guzzle": "^7.3.0",
|
||||
"hedeqiang/umeng": "^2.1",
|
||||
"laravel/framework": "^v8.48.1",
|
||||
"laravel/framework": "^v8.83.27",
|
||||
"laravel/tinker": "^v2.6.1",
|
||||
"lasserafn/php-initial-avatar-generator": "^4.2",
|
||||
"maatwebsite/excel": "^3.1.31",
|
||||
|
||||
2094
composer.lock
generated
2094
composer.lock
generated
File diff suppressed because it is too large
Load Diff
73
config/ldap.php
Normal file
73
config/ldap.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default LDAP Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which of the LDAP connections below you wish
|
||||
| to use as your default connection for all LDAP operations. Of
|
||||
| course you may add as many connections you'd like below.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('LDAP_CONNECTION', 'default'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| LDAP Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Below you may configure each LDAP connection your application requires
|
||||
| access to. Be sure to include a valid base DN - otherwise you may
|
||||
| not receive any results when performing LDAP search operations.
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'default' => [
|
||||
'hosts' => [env('LDAP_HOST', '127.0.0.1')],
|
||||
'username' => env('LDAP_USERNAME', 'cn=user,dc=local,dc=com'),
|
||||
'password' => env('LDAP_PASSWORD', 'secret'),
|
||||
'port' => env('LDAP_PORT', 389),
|
||||
'base_dn' => env('LDAP_BASE_DN', 'dc=local,dc=com'),
|
||||
'timeout' => env('LDAP_TIMEOUT', 5),
|
||||
'use_ssl' => env('LDAP_SSL', false),
|
||||
'use_tls' => env('LDAP_TLS', false),
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| LDAP Logging
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When LDAP logging is enabled, all LDAP search and authentication
|
||||
| operations are logged using the default application logging
|
||||
| driver. This can assist in debugging issues and more.
|
||||
|
|
||||
*/
|
||||
|
||||
'logging' => (bool) env('APP_DEBUG', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| LDAP Cache
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| LDAP caching enables the ability of caching search results using the
|
||||
| query builder. This is great for running expensive operations that
|
||||
| may take many seconds to complete, such as a pagination request.
|
||||
|
|
||||
*/
|
||||
|
||||
'cache' => [
|
||||
'enabled' => env('LDAP_CACHE', false),
|
||||
'driver' => env('CACHE_DRIVER', 'file'),
|
||||
],
|
||||
|
||||
];
|
||||
@@ -27,7 +27,7 @@ class ProjectTasksAddArchivedFollow extends Migration
|
||||
// 更新数据
|
||||
Project::whereNotNull('archived_at')->chunkById(100, function ($lists) {
|
||||
foreach ($lists as $item) {
|
||||
ProjectTask::whereProjectId($item->id)->whereArchivedAt(null)->update([
|
||||
ProjectTask::whereProjectId($item->id)->whereArchivedAt(null)->change([
|
||||
'archived_at' => $item->archived_at,
|
||||
'archived_follow' => 1
|
||||
]);
|
||||
|
||||
@@ -18,7 +18,7 @@ class ProjectTasksUpdateSubtaskArchivedDelete extends Migration
|
||||
->chunkById(100, function ($lists) {
|
||||
/** @var ProjectTask $task */
|
||||
foreach ($lists as $task) {
|
||||
ProjectTask::whereParentId($task->id)->update([
|
||||
ProjectTask::whereParentId($task->id)->change([
|
||||
'archived_at' => $task->archived_at,
|
||||
'archived_userid' => $task->archived_userid,
|
||||
'archived_follow' => $task->archived_follow,
|
||||
@@ -32,7 +32,7 @@ class ProjectTasksUpdateSubtaskArchivedDelete extends Migration
|
||||
->chunkById(100, function ($lists) {
|
||||
/** @var ProjectTask $task */
|
||||
foreach ($lists as $task) {
|
||||
ProjectTask::whereParentId($task->id)->update([
|
||||
ProjectTask::whereParentId($task->id)->change([
|
||||
'deleted_at' => $task->deleted_at,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ class AddWebSocketDialogUsersInviterImportant extends Migration
|
||||
Schema::table('web_socket_dialog_users', function (Blueprint $table) use (&$isAdd) {
|
||||
if (!Schema::hasColumn('web_socket_dialog_users', 'important')) {
|
||||
$isAdd = true;
|
||||
$table->boolean('important')->default(0)->after('mark_unread')->nullable()->comment('是否不可移出(项目、任务人员)');
|
||||
$table->boolean('important')->default(0)->after('mark_unread')->nullable()->comment('是否不可移出(项目、任务、部门人员)');
|
||||
$table->bigInteger('inviter')->nullable()->default(0)->after('mark_unread')->comment('邀请人');
|
||||
}
|
||||
});
|
||||
@@ -25,7 +25,7 @@ class AddWebSocketDialogUsersInviterImportant extends Migration
|
||||
\App\Models\WebSocketDialog::whereIn('group_type', ['project', 'task'])->chunkById(100, function ($lists) {
|
||||
/** @var \App\Models\WebSocketDialog $item */
|
||||
foreach ($lists as $item) {
|
||||
\App\Models\WebSocketDialogUser::whereDialogId($item->id)->update([
|
||||
\App\Models\WebSocketDialogUser::whereDialogId($item->id)->change([
|
||||
'important' => 1,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateUserCheckinMacsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('user_checkin_macs'))
|
||||
return;
|
||||
|
||||
Schema::create('user_checkin_macs', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('userid')->nullable()->default(0)->comment('会员id');
|
||||
$table->string('mac', 100)->nullable()->default('')->comment('MAC地址');
|
||||
$table->string('remark', 100)->nullable()->default('')->comment('备注');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('user_checkin_macs');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateUserCheckinRecordsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('user_checkin_records'))
|
||||
return;
|
||||
|
||||
Schema::create('user_checkin_records', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('userid')->nullable()->default(0)->comment('会员id');
|
||||
$table->string('mac', 100)->nullable()->default('')->comment('MAC地址');
|
||||
$table->bigInteger('time')->nullable()->default(0)->comment('上报的时间戳');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('user_checkin_records');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddDateTimesUserCheckinRecords extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$isAdd = false;
|
||||
Schema::table('user_checkin_records', function (Blueprint $table) use (&$isAdd) {
|
||||
if (!Schema::hasColumn('user_checkin_records', 'date')) {
|
||||
$isAdd = true;
|
||||
$table->string('date', 20)->nullable()->default('')->after('mac')->comment('签到日期');
|
||||
$table->text('times')->nullable()->after('date')->comment('签到时间');
|
||||
$table->renameColumn('time', 'report_time');
|
||||
}
|
||||
});
|
||||
if ($isAdd) {
|
||||
$userids = \App\Models\UserCheckinRecord::select('userid')->distinct()->get()->pluck('userid');
|
||||
foreach ($userids as $userid) {
|
||||
$list = \App\Models\UserCheckinRecord::whereUserid($userid)->orderBy('created_at')->get();
|
||||
$ids = [];
|
||||
$date = "";
|
||||
$array = [];
|
||||
foreach ($list as $item) {
|
||||
$ids[] = $item->id;
|
||||
$created_at = \Carbon\Carbon::parse($item->created_at);
|
||||
if ($created_at->toDateString() != $date) {
|
||||
$date = $created_at->toDateString();
|
||||
if ($array) {
|
||||
$record = \App\Models\UserCheckinRecord::createInstance($array);
|
||||
$record->save();
|
||||
}
|
||||
$array = [
|
||||
'userid' => $item->userid,
|
||||
'mac' => $item->mac,
|
||||
'date' => $date,
|
||||
'times' => [],
|
||||
'report_time' => $item->report_time,
|
||||
'created_at' => $item->created_at,
|
||||
];
|
||||
}
|
||||
if ($array) {
|
||||
$array['times'][] = $created_at->toTimeString();
|
||||
}
|
||||
}
|
||||
if ($array) {
|
||||
\App\Models\UserCheckinRecord::whereIn('id', $ids)->delete();
|
||||
}
|
||||
if ($array) {
|
||||
$record = \App\Models\UserCheckinRecord::createInstance($array);
|
||||
$record->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
// ... 退回去意义不大
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateUserBotsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (Schema::hasTable('user_bots'))
|
||||
return;
|
||||
|
||||
Schema::create('user_bots', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('userid')->nullable()->default(0)->comment('所属人ID');
|
||||
$table->bigInteger('bot_id')->nullable()->default(0)->comment('机器人ID');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('user_bots');
|
||||
}
|
||||
}
|
||||
35
database/migrations/2022_12_17_123811_add_users_bot.php
Normal file
35
database/migrations/2022_12_17_123811_add_users_bot.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddUsersBot extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('users', 'bot')) {
|
||||
$table->tinyInteger('bot')->nullable()->default(0)->after('email_verity')->comment('是否机器人');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn("bot");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddWebSocketDialogUsersAddSilence extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('web_socket_dialog_users', 'silence')) {
|
||||
$table->boolean('silence')->default(0)->nullable()->after('mark_unread')->comment('是否免打扰:0否,1是');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
|
||||
$table->dropColumn("silence");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddWebSocketDialogMsgReadsAddSilence extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('web_socket_dialog_msg_reads', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('web_socket_dialog_msg_reads', 'silence')) {
|
||||
$table->boolean('silence')->default(0)->nullable()->after('mention')->comment('是否免打扰:0否,1是');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('web_socket_dialog_msg_reads', function (Blueprint $table) {
|
||||
$table->dropColumn("silence");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class DeleteProjectTaskMailLogsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::dropIfExists('project_task_mail_logs');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
// 删除表无法恢复
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddUserBotsClear extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$isAdd = false;
|
||||
Schema::table('user_bots', function (Blueprint $table) use (&$isAdd) {
|
||||
if (!Schema::hasColumn('user_bots', 'clear_day')) {
|
||||
$isAdd = true;
|
||||
$table->smallInteger('clear_day')->nullable()->default(90)->after('bot_id')->comment('消息自动清理天数');
|
||||
$table->timestamp('clear_at')->nullable()->after('clear_day')->comment('下一次清理时间');
|
||||
}
|
||||
});
|
||||
if ($isAdd) {
|
||||
\App\Models\UserBot::whereNull('clear_at')->update(['clear_at' => \Carbon\Carbon::now()->addDays(90)]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('user_bots', function (Blueprint $table) {
|
||||
$table->dropColumn("clear_day");
|
||||
$table->dropColumn("clear_at");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddUserBotsWebhook extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('user_bots', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('user_bots', 'webhook_url')) {
|
||||
$table->string('webhook_url', 255)->nullable()->default('')->after('clear_at')->comment('消息webhook地址');
|
||||
$table->integer('webhook_num')->nullable()->default(0)->after('webhook_url')->comment('消息webhook请求次数');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('user_bots', function (Blueprint $table) {
|
||||
$table->dropColumn("webhook_url");
|
||||
$table->dropColumn("webhook_num");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddWebSocketDialogsAvatar extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('web_socket_dialogs', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('web_socket_dialogs', 'avatar')) {
|
||||
$table->string('avatar', 255)->nullable()->default('')->after('name')->comment('头像(群)');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('web_socket_dialogs', function (Blueprint $table) {
|
||||
$table->dropColumn("avatar");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateWebSocketDialogUsersUpdatedAt extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
|
||||
$pre = DB::connection()->getTablePrefix();
|
||||
DB::statement("ALTER TABLE `{$pre}web_socket_dialog_users` MODIFY COLUMN `created_at` timestamp(3) NULL DEFAULT NULL AFTER `important`");
|
||||
DB::statement("ALTER TABLE `{$pre}web_socket_dialog_users` MODIFY COLUMN `updated_at` timestamp(3) NULL DEFAULT NULL AFTER `created_at`");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
||||
37
database/migrations/2023_03_07_152557_create_deleteds.php
Normal file
37
database/migrations/2023_03_07_152557_create_deleteds.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateDeleteds extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('deleteds', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->string('type', 50)->nullable()->default('')->comment('删除的数据类型(如:project、task、dialog)');
|
||||
$table->bigInteger('did')->nullable()->default(0)->comment('删除的数据ID');
|
||||
$table->bigInteger('userid')->nullable()->default(0)->comment('关系会员ID');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
$table->unique(['type', 'did', 'userid']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('deleted', function (Blueprint $table) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class UpdateTableIndexes extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('web_socket_dialog_msg_reads', function (Blueprint $table) {
|
||||
$table->index(['userid', 'read_at', 'dialog_id']);
|
||||
});
|
||||
Schema::table('web_socket_dialog_msgs', function (Blueprint $table) {
|
||||
$table->index('dialog_id');
|
||||
$table->index('dialog_type');
|
||||
$table->index('userid');
|
||||
});
|
||||
Schema::table('task_workers', function (Blueprint $table) {
|
||||
$table->index('deleted_at');
|
||||
});
|
||||
Schema::table('project_tasks', function (Blueprint $table) {
|
||||
$table->index(['parent_id', 'deleted_at']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user