Compare commits
152 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
905662e963 | ||
|
|
ac2dfcfce3 | ||
|
|
fd6fef4292 | ||
|
|
eee7b8f0f1 | ||
|
|
ef88924f36 | ||
|
|
c9378b0eab | ||
|
|
cde96cb8b4 | ||
|
|
ffbd9b1f26 | ||
|
|
59524c78e6 | ||
|
|
1ad0c3a3ec | ||
|
|
e85d4c24d3 | ||
|
|
18280b4ff9 | ||
|
|
e504fb845f | ||
|
|
003186edd9 | ||
|
|
61e958d757 | ||
|
|
2ee349605e | ||
|
|
7d200d9a73 | ||
|
|
0468178c43 | ||
|
|
6f4a94c8d1 | ||
|
|
e83a65ec40 | ||
|
|
53ea6b00ab | ||
|
|
45865c0c18 | ||
|
|
979d8057a4 | ||
|
|
b81f75b794 | ||
|
|
8f7578ab06 | ||
|
|
dd39a8b63f | ||
|
|
7595309dc8 | ||
|
|
93abe72041 | ||
|
|
60c513ea3b | ||
|
|
385abae741 | ||
|
|
a680538f80 | ||
|
|
11571c2045 | ||
|
|
f1d267e2b2 | ||
|
|
003b52fbc9 | ||
|
|
6e2df6fcfc | ||
|
|
cd151667c1 | ||
|
|
1a3ecbb4e9 | ||
|
|
0b1f433fe4 | ||
|
|
7aa44b8623 | ||
|
|
6e68100f86 | ||
|
|
c869e88c40 | ||
|
|
9771e1e741 | ||
|
|
9034a70b8f | ||
|
|
e58ebebb80 | ||
|
|
edab43504e | ||
|
|
900b11280c | ||
|
|
0f449f151a | ||
|
|
8186649a9f | ||
|
|
364ef34248 | ||
|
|
c66714d1f5 | ||
|
|
d42fcca81f | ||
|
|
16d1591675 | ||
|
|
d7b2f93797 | ||
|
|
9203046d97 | ||
|
|
b860a749f3 | ||
|
|
6c689fe55f | ||
|
|
c18fd1ef20 | ||
|
|
7e56389c34 | ||
|
|
28771c38e7 | ||
|
|
de7e74ff19 | ||
|
|
2cf661b54f | ||
|
|
16ea92c970 | ||
|
|
3ad55cf6d5 | ||
|
|
d0951041eb | ||
|
|
150d9f18b4 | ||
|
|
09f48c32c7 | ||
|
|
596594d8b3 | ||
|
|
59d17aa950 | ||
|
|
836d4f8209 | ||
|
|
c13aa8d7a4 | ||
|
|
5fb07b7aa7 | ||
|
|
ee12cc21c1 | ||
|
|
6abfc7a2e2 | ||
|
|
18758421fd | ||
|
|
644a986747 | ||
|
|
ad164f3539 | ||
|
|
d5a7ae5fa3 | ||
|
|
a845b7b892 | ||
|
|
a29414a477 | ||
|
|
1c3ce47dcc | ||
|
|
8f30693473 | ||
|
|
0f5f7c515a | ||
|
|
8e76493cc6 | ||
|
|
8617c72aab | ||
|
|
7f0a150b74 | ||
|
|
a53aaff620 | ||
|
|
cab81c5c04 | ||
|
|
5923b6169a | ||
|
|
0f5eb829c6 | ||
|
|
d87c06de9a | ||
|
|
76e8870738 | ||
|
|
247b9ee0c1 | ||
|
|
80dd76b078 | ||
|
|
9163cee90e | ||
|
|
fe46d63031 | ||
|
|
d14d5a5942 | ||
|
|
7181652f01 | ||
|
|
4024cfb7c7 | ||
|
|
1cf5a24c26 | ||
|
|
06795ca32c | ||
|
|
c4fd5ee60a | ||
|
|
e30b66e5c1 | ||
|
|
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
|
||||
2083
CHANGELOG.md
Normal file
2083
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);
|
||||
}
|
||||
|
||||
1074
app/Http/Controllers/Api/ApproveController.php
Executable file
1074
app/Http/Controllers/Api/ApproveController.php
Executable file
File diff suppressed because it is too large
Load Diff
@@ -3,9 +3,9 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\AbstractModel;
|
||||
use App\Models\Deleted;
|
||||
use App\Models\File;
|
||||
use App\Models\FileContent;
|
||||
use App\Models\FileLink;
|
||||
use App\Models\ProjectTask;
|
||||
use App\Models\ProjectTaskFile;
|
||||
use App\Models\User;
|
||||
@@ -15,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
|
||||
@@ -36,7 +36,10 @@ 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] 每页显示数量,默认:50,最大:100
|
||||
*
|
||||
@@ -48,11 +51,13 @@ class DialogController extends AbstractController
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$builder = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence'])
|
||||
$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')
|
||||
@@ -62,7 +67,12 @@ class DialogController extends AbstractController
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,7 +98,7 @@ class DialogController extends AbstractController
|
||||
return Base::retError('请输入搜索关键词');
|
||||
}
|
||||
// 搜索会话
|
||||
$dialogs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence'])
|
||||
$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)
|
||||
@@ -104,7 +114,11 @@ class DialogController extends AbstractController
|
||||
if (count($list) < 20 && Base::judgeClientVersion("0.21.60")) {
|
||||
$users = User::select(User::$basicField)
|
||||
->where(function ($query) use ($key) {
|
||||
$query->where("email", "like", "%{$key}%")->orWhere("nickname", "like", "%{$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();
|
||||
@@ -121,7 +135,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
// 搜索消息会话
|
||||
if (count($list) < 20) {
|
||||
$msgs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', '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)
|
||||
@@ -158,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', 'u.silence'])
|
||||
$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)
|
||||
@@ -302,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', [
|
||||
@@ -340,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('打开会话失败');
|
||||
}
|
||||
@@ -357,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之后的数据
|
||||
@@ -459,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;
|
||||
@@ -479,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
|
||||
@@ -508,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
|
||||
@@ -528,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
|
||||
@@ -580,7 +664,10 @@ class DialogController extends AbstractController
|
||||
*
|
||||
* @apiParam {Number} dialog_id 对话ID
|
||||
* @apiParam {String} text 消息内容
|
||||
* @apiParam {Number} [update_id] 更新消息ID(优先大于reply_id)
|
||||
* @apiParam {String} [text_type] 消息类型
|
||||
* - html: HTML(默认)
|
||||
* - md: MARKDOWN
|
||||
* @apiParam {Number} [update_id] 更新消息ID(优先大于 reply_id)
|
||||
* @apiParam {Number} [reply_id] 回复ID
|
||||
* @apiParam {String} [silence] 是否静默发送
|
||||
* - no: 正常发送(默认)
|
||||
@@ -592,7 +679,6 @@ class DialogController extends AbstractController
|
||||
*/
|
||||
public function msg__sendtext()
|
||||
{
|
||||
Base::checkClientVersion('0.19.0');
|
||||
$user = User::auth();
|
||||
//
|
||||
if (!$user->bot) {
|
||||
@@ -607,11 +693,13 @@ class DialogController extends AbstractController
|
||||
}
|
||||
}
|
||||
//
|
||||
$dialog_id = Base::getPostInt('dialog_id');
|
||||
$update_id = Base::getPostInt('update_id');
|
||||
$reply_id = Base::getPostInt('reply_id');
|
||||
$text = trim(Base::getPostValue('text'));
|
||||
$silence = trim(Base::getPostValue('silence')) === 'yes';
|
||||
$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);
|
||||
//
|
||||
@@ -623,7 +711,9 @@ 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) {
|
||||
@@ -643,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,
|
||||
@@ -652,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, false, false, $silence);
|
||||
}
|
||||
//
|
||||
return WebSocketDialogMsg::sendMsg($action, $dialog_id, 'text', ['text' => $text], $user->userid, false, false, $silence);
|
||||
$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
|
||||
@@ -681,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('说话时间太短');
|
||||
}
|
||||
@@ -708,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
|
||||
@@ -730,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,
|
||||
@@ -792,7 +887,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/sendfileid 15. 通过文件ID发送文件
|
||||
* @api {get} api/dialog/msg/sendfileid 17. 通过文件ID发送文件
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -819,12 +914,11 @@ class DialogController extends AbstractController
|
||||
return Base::retError("请选择转发对话或成员");
|
||||
}
|
||||
//
|
||||
$file = File::permissionFind($file_id);
|
||||
$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>";
|
||||
//
|
||||
$sender = $user->userid;
|
||||
return AbstractModel::transaction(function() use ($sender, $fileMsg, $userids, $dialogids) {
|
||||
return AbstractModel::transaction(function() use ($user, $fileMsg, $userids, $dialogids) {
|
||||
$msgs = [];
|
||||
$already = [];
|
||||
if ($dialogids) {
|
||||
@@ -832,7 +926,7 @@ class DialogController extends AbstractController
|
||||
$dialogids = [$dialogids];
|
||||
}
|
||||
foreach ($dialogids as $dialogid) {
|
||||
$res = WebSocketDialogMsg::sendMsg(null, $dialogid, 'text', ['text' => $fileMsg], $sender);
|
||||
$res = WebSocketDialogMsg::sendMsg(null, $dialogid, 'text', ['text' => $fileMsg], $user->userid);
|
||||
if (Base::isSuccess($res)) {
|
||||
$msgs[] = $res['data'];
|
||||
$already[] = $dialogid;
|
||||
@@ -847,9 +941,9 @@ class DialogController extends AbstractController
|
||||
if (!User::whereUserid($userid)->exists()) {
|
||||
continue;
|
||||
}
|
||||
$dialog = WebSocketDialog::checkUserDialog($sender, $userid);
|
||||
$dialog = WebSocketDialog::checkUserDialog($user, $userid);
|
||||
if ($dialog && !in_array($dialog->id, $already)) {
|
||||
$res = WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $fileMsg], $sender);
|
||||
$res = WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $fileMsg], $user->userid);
|
||||
if (Base::isSuccess($res)) {
|
||||
$msgs[] = $res['data'];
|
||||
}
|
||||
@@ -863,7 +957,60 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/readlist 16. 获取消息阅读情况
|
||||
* @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
|
||||
@@ -892,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
|
||||
@@ -940,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
|
||||
@@ -977,13 +1124,13 @@ class DialogController extends AbstractController
|
||||
}
|
||||
//
|
||||
$filePath = public_path($array['path']);
|
||||
return Response::streamDownload(function() use ($filePath) {
|
||||
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
|
||||
@@ -1004,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
|
||||
@@ -1018,8 +1165,9 @@ 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 返回信息(错误描述)
|
||||
@@ -1028,42 +1176,54 @@ class DialogController extends AbstractController
|
||||
public function msg__mark()
|
||||
{
|
||||
$user = User::auth();
|
||||
$dialogId = intval(Request::input('dialog_id'));
|
||||
//
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
$type = Request::input('type');
|
||||
$dialogUser = WebSocketDialogUser::whereUserid($user->userid)->whereDialogId($dialogId)->first();
|
||||
if (!$dialogUser) {
|
||||
return Base::retError("会话不存在");
|
||||
$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('会话不存在');
|
||||
}
|
||||
$data = [
|
||||
'id' => $dialogId,
|
||||
];
|
||||
switch ($type) {
|
||||
case 'read':
|
||||
$data['unread'] = 0;
|
||||
WebSocketDialogMsgRead::whereUserid($user->userid)
|
||||
->whereReadAt(null)
|
||||
->whereDialogId($dialogId)
|
||||
->chunkById(100, function ($list) {
|
||||
WebSocketDialogMsgRead::onlyMarkRead($list);
|
||||
});
|
||||
$dialogUser->mark_unread = 0;
|
||||
$dialogUser->save();
|
||||
$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':
|
||||
$dialogUser->mark_unread = 1;
|
||||
$dialogUser->save();
|
||||
$data = [
|
||||
'id' => $dialogUser->webSocketDialog->id,
|
||||
'mark_unread' => 1,
|
||||
];
|
||||
break;
|
||||
|
||||
default:
|
||||
return Base::retError("参数错误");
|
||||
}
|
||||
$data['mark_unread'] = $dialogUser->mark_unread;
|
||||
return Base::retSuccess("success", $data);
|
||||
$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 20. 消息免打扰
|
||||
* @api {get} api/dialog/msg/silence 24. 消息免打扰
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1126,7 +1286,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/msg/forward 21. 转发消息给
|
||||
* @api {get} api/dialog/msg/forward 25. 转发消息给
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1159,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
|
||||
@@ -1198,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
|
||||
@@ -1227,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
|
||||
@@ -1270,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
|
||||
@@ -1300,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
|
||||
@@ -1347,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 返回信息(错误描述)
|
||||
@@ -1365,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('请选择群成员');
|
||||
@@ -1388,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);
|
||||
@@ -1399,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
|
||||
@@ -1407,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 返回信息(错误描述)
|
||||
@@ -1418,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身份
|
||||
* - 有群主时:只有群主可以邀请
|
||||
@@ -1474,7 +1666,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/group/deluser 30. 移出(退出)群成员
|
||||
* @api {get} api/dialog/group/deluser 34. 移出(退出)群成员
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* - 只有群主、邀请人可以踢人
|
||||
@@ -1518,7 +1710,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/group/transfer 31. 转让群组
|
||||
* @api {get} api/dialog/group/transfer 35. 转让群组
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* - 只有群主且是个人类型群可以解散
|
||||
@@ -1562,7 +1754,7 @@ class DialogController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/dialog/group/disband 32. 解散群组
|
||||
* @api {get} api/dialog/group/disband 36. 解散群组
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* - 只有群主且是个人类型群可以解散
|
||||
@@ -1586,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,16 +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, [
|
||||
@@ -282,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) {
|
||||
@@ -327,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) {
|
||||
@@ -397,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) {
|
||||
@@ -458,7 +488,7 @@ class FileController extends AbstractController
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
User::auth();
|
||||
$user = User::auth();
|
||||
//
|
||||
$ids = Request::input('ids');
|
||||
//
|
||||
@@ -470,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;
|
||||
}
|
||||
@@ -513,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('参数错误');
|
||||
@@ -563,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;
|
||||
@@ -659,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);
|
||||
@@ -717,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) {
|
||||
@@ -776,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",
|
||||
@@ -863,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)
|
||||
@@ -896,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)) {
|
||||
@@ -1060,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('不能退出自己共享的文件');
|
||||
@@ -1098,7 +1135,7 @@ class FileController extends AbstractController
|
||||
$id = intval(Request::input('id'));
|
||||
$refresh = Request::input('refresh', 'no');
|
||||
//
|
||||
$file = File::permissionFind($id);
|
||||
$file = File::permissionFind($id, $user);
|
||||
$fileLink = $file->getShareLink($user->userid, $refresh == 'yes');
|
||||
//
|
||||
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;
|
||||
@@ -21,6 +22,8 @@ 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;
|
||||
@@ -58,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
|
||||
@@ -104,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');
|
||||
@@ -128,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);
|
||||
@@ -503,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,
|
||||
@@ -564,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'],
|
||||
]);
|
||||
}
|
||||
@@ -834,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
|
||||
@@ -865,7 +879,7 @@ class ProjectController extends AbstractController
|
||||
*/
|
||||
public function task__lists()
|
||||
{
|
||||
User::auth();
|
||||
$user = User::auth();
|
||||
//
|
||||
$builder = ProjectTask::with(['taskUser', 'taskTag']);
|
||||
//
|
||||
@@ -873,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');
|
||||
@@ -883,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;
|
||||
@@ -892,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);
|
||||
@@ -913,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') {
|
||||
@@ -947,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1113,7 +1131,8 @@ class ProjectController extends AbstractController
|
||||
'styles' => ["A1:P1" => ["font" => ["bold" => true]]],
|
||||
'data' => [],
|
||||
];
|
||||
$sheets[] = BillExport::create()->setTitle($data['nickname'] ?: $ownerid)->setHeadings($headings)->setData($data['data'])->setStyles($data['styles']);
|
||||
$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];
|
||||
@@ -1153,7 +1172,116 @@ 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
|
||||
@@ -1179,7 +1307,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/one 21. 获取单个任务信息
|
||||
* @api {get} api/project/task/one 22. 获取单个任务信息
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1213,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
|
||||
@@ -1241,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
|
||||
@@ -1266,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
|
||||
@@ -1299,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
|
||||
@@ -1343,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
|
||||
@@ -1386,13 +1514,13 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
//
|
||||
$filePath = public_path($file->getRawOriginal('path'));
|
||||
return Response::streamDownload(function() use ($filePath) {
|
||||
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
|
||||
@@ -1415,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'];
|
||||
// 项目
|
||||
@@ -1464,7 +1593,7 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/task/addsub 28. 添加子任务
|
||||
* @api {get} api/project/task/addsub 29. 添加子任务
|
||||
*
|
||||
* @apiDescription 需要token身份(限:项目、任务负责人)
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1504,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
|
||||
@@ -1535,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);
|
||||
@@ -1551,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
|
||||
@@ -1600,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
|
||||
@@ -1642,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
|
||||
@@ -1667,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();
|
||||
@@ -1676,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
|
||||
@@ -1735,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
|
||||
@@ -1817,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
|
||||
@@ -1843,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
|
||||
@@ -1861,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('参数错误');
|
||||
@@ -1877,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
|
||||
@@ -1909,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
|
||||
@@ -1948,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;
|
||||
});
|
||||
@@ -1961,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
|
||||
|
||||
@@ -92,7 +92,7 @@ class PublicController extends AbstractController
|
||||
if ($key != $setting['key']) {
|
||||
return 'key error';
|
||||
}
|
||||
$times = $setting['time'] ? Base::json2array($setting['time']) : ['00:00', '23:59'];
|
||||
$times = $setting['time'] ? Base::json2array($setting['time']) : ['09:00', '18:00'];
|
||||
$advance = (intval($setting['advance']) ?: 120) * 60;
|
||||
$delay = (intval($setting['delay']) ?: 120) * 60;
|
||||
//
|
||||
@@ -129,20 +129,36 @@ class PublicController extends AbstractController
|
||||
}
|
||||
//
|
||||
if ($checkins && $botUser = User::botGetOrCreate('check-in')) {
|
||||
$sendMsg = function($type, UserCheckinMac $checkin) use ($botUser, $nowDate) {
|
||||
$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->userid, $checkin->userid);
|
||||
$dialog = WebSocketDialog::checkUserDialog($botUser, $checkin->userid);
|
||||
if ($dialog) {
|
||||
$hi = date("H:i");
|
||||
$pre = $type == "up" ? "上班" : "下班";
|
||||
$remark = $checkin->remark ? " ({$checkin->remark})": "";
|
||||
$text = "{$pre}打卡成功,打卡时间: {$hi} {$remark}";
|
||||
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid);
|
||||
$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, false, false, $type != "up");
|
||||
}
|
||||
};
|
||||
if ($timeAdvance <= Base::time() && Base::time() < $timeEnd) {
|
||||
|
||||
@@ -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;
|
||||
@@ -136,14 +137,14 @@ class ReportController extends AbstractController
|
||||
$user = User::auth();
|
||||
//
|
||||
$input = [
|
||||
"id" => Base::getPostValue("id", 0),
|
||||
"sign" => Base::getPostValue("sign"),
|
||||
"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',
|
||||
@@ -306,7 +307,7 @@ 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> ') : '';
|
||||
$pre = $type == Report::WEEKLY ? ('<span>[' . Doo::translate('周' . ['日', '一', '二', '三', '四', '五', '六'][$complete_at->dayOfWeek]) . ']</span> ') : '';
|
||||
$completeContent .= "<li>{$pre}[{$task->project->name}] {$task->name}</li>";
|
||||
}
|
||||
} else {
|
||||
@@ -327,7 +328,7 @@ 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> ' : '';
|
||||
$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 {
|
||||
@@ -340,14 +341,19 @@ 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" => '<h2>' . Base::Lang('已完成工作') . '</h2><ol>' .
|
||||
$completeContent . '</ol><h2>' .
|
||||
Base::Lang('未完成的工作') . '</h2><ol>' .
|
||||
$unfinishedContent . '</ol>',
|
||||
"content" => $content,
|
||||
"complete_task" => $complete_task,
|
||||
"unfinished_task" => $unfinished_task,
|
||||
];
|
||||
@@ -460,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));
|
||||
|
||||
@@ -8,10 +8,13 @@ use App\Models\UserCheckinRecord;
|
||||
use App\Module\Base;
|
||||
use App\Module\BillExport;
|
||||
use App\Module\BillMultipleExport;
|
||||
use Arr;
|
||||
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;
|
||||
@@ -35,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 返回信息(错误描述)
|
||||
@@ -53,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'
|
||||
])) {
|
||||
@@ -89,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('{}'));
|
||||
@@ -197,18 +208,25 @@ 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/checkin 03. 获取签到设置、保存签到设置(限管理员)
|
||||
* @api {get} api/system/setting/checkin 04. 获取签到设置、保存签到设置(限管理员)
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@@ -216,7 +234,7 @@ class SystemController extends AbstractController
|
||||
*
|
||||
* @apiParam {String} type
|
||||
* - get: 获取(默认)
|
||||
* - save: 保存设置(参数:['open', 'time', 'advance', 'delay', 'edit', 'key'])
|
||||
* - save: 保存设置(参数:['open', 'time', 'advance', 'delay', 'remindin', 'remindexceed', 'edit', 'key'])
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
@@ -237,6 +255,8 @@ class SystemController extends AbstractController
|
||||
'time',
|
||||
'advance',
|
||||
'delay',
|
||||
'remindin',
|
||||
'remindexceed',
|
||||
'edit',
|
||||
'key',
|
||||
])) {
|
||||
@@ -257,9 +277,11 @@ class SystemController extends AbstractController
|
||||
}
|
||||
//
|
||||
$setting['open'] = $setting['open'] ?: 'close';
|
||||
$setting['time'] = $setting['time'] ? Base::json2array($setting['time']) : ['00:00', '23:59'];
|
||||
$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";
|
||||
//
|
||||
@@ -267,7 +289,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/setting/apppush 04. 获取APP推送设置、保存APP推送设置(限管理员)
|
||||
* @api {get} api/system/setting/apppush 05. 获取APP推送设置、保存APP推送设置(限管理员)
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@@ -312,7 +334,77 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
@@ -336,7 +428,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/system/priority 06. 任务优先级
|
||||
* @api {post} api/system/priority 08. 任务优先级
|
||||
*
|
||||
* @apiDescription 获取任务优先级、保存任务优先级
|
||||
* @apiVersion 1.0.0
|
||||
@@ -357,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('参数错误');
|
||||
@@ -385,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
|
||||
@@ -406,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('参数错误');
|
||||
@@ -432,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['info']['people'] > 0 && $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
|
||||
@@ -451,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
|
||||
@@ -476,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
|
||||
@@ -493,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
|
||||
@@ -506,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
|
||||
@@ -523,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
|
||||
@@ -561,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,
|
||||
@@ -587,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
|
||||
@@ -642,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 = [
|
||||
@@ -651,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';
|
||||
}
|
||||
@@ -683,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
|
||||
@@ -704,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,
|
||||
@@ -725,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
|
||||
@@ -745,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
|
||||
@@ -791,7 +978,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/checkin/export 17. 导出签到数据(限管理员)
|
||||
* @api {get} api/system/checkin/export 22. 导出签到数据(限管理员)
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@@ -850,7 +1037,7 @@ class SystemController extends AbstractController
|
||||
$sheets = [];
|
||||
$startD = Carbon::parse($date[0])->startOfDay();
|
||||
$endD = Carbon::parse($date[1])->endOfDay();
|
||||
$users = User::whereIn('userid', $userid)->take(20)->get();
|
||||
$users = User::whereIn('userid', $userid)->take(100)->get();
|
||||
/** @var User $user */
|
||||
foreach ($users as $user) {
|
||||
$recordTimes = UserCheckinRecord::getTimes($user->userid, [$startD, $endD]);
|
||||
@@ -914,7 +1101,8 @@ class SystemController extends AbstractController
|
||||
];
|
||||
$startT += 86400;
|
||||
}
|
||||
$sheets[] = BillExport::create()->setTitle($nickname ?: $user->userid)->setHeadings($headings)->setData($datas)->setStyles($styles);
|
||||
$title = (count($sheets) + 1) . "." . ($nickname ?: $user->userid);
|
||||
$sheets[] = BillExport::create()->setTitle($title)->setHeadings($headings)->setData($datas)->setStyles($styles);
|
||||
}
|
||||
if (empty($sheets)) {
|
||||
return Base::retError('没有任何数据');
|
||||
@@ -957,7 +1145,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/checkin/down 17. 下载导出的签到数据
|
||||
* @api {get} api/system/checkin/down 23. 下载导出的签到数据
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@@ -983,7 +1171,7 @@ class SystemController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/system/version 18. 获取版本号
|
||||
* @api {get} api/system/version 24. 获取版本号
|
||||
*
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup system
|
||||
@@ -998,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;
|
||||
|
||||
@@ -2,21 +2,25 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Ldap\LdapUser;
|
||||
use App\Models\AbstractModel;
|
||||
use App\Models\Meeting;
|
||||
use App\Models\Project;
|
||||
use App\Models\UmengAlias;
|
||||
use App\Models\User;
|
||||
use App\Models\UserBot;
|
||||
use App\Models\UserCheckinMac;
|
||||
use App\Models\UserCheckinRecord;
|
||||
use App\Models\UserDelete;
|
||||
use App\Models\UserDepartment;
|
||||
use App\Models\UserEmailVerification;
|
||||
use App\Models\UserTransfer;
|
||||
use App\Models\WebSocket;
|
||||
use App\Models\WebSocketDialog;
|
||||
use App\Models\WebSocketDialogMsg;
|
||||
use App\Module\AgoraIO\AgoraTokenGenerator;
|
||||
use App\Module\Base;
|
||||
use App\Module\Doo;
|
||||
use Arr;
|
||||
use Cache;
|
||||
use Captcha;
|
||||
@@ -44,6 +48,7 @@ class UsersController extends AbstractController
|
||||
* @apiParam {String} email 邮箱
|
||||
* @apiParam {String} password 密码
|
||||
* @apiParam {String} [code] 登录验证码
|
||||
* @apiParam {String} [code_key] 验证码通过key验证
|
||||
* @apiParam {String} [invite] 注册邀请码
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
@@ -75,10 +80,16 @@ class UsersController extends AbstractController
|
||||
$needCode = !Base::isError(User::needCode($email));
|
||||
if ($needCode) {
|
||||
$code = trim(Request::input('code'));
|
||||
$codeKey = trim(Request::input('code_key'));
|
||||
if (empty($code)) {
|
||||
return Base::retError('请输入验证码', ['code' => 'need']);
|
||||
}
|
||||
if (!Captcha::check($code)) {
|
||||
if ($codeKey) {
|
||||
$check = Captcha::check_api($code, $codeKey);
|
||||
} else {
|
||||
$check = Captcha::check($code);
|
||||
}
|
||||
if (!$check) {
|
||||
return Base::retError('请输入正确的验证码', ['code' => 'need']);
|
||||
}
|
||||
}
|
||||
@@ -89,11 +100,23 @@ class UsersController extends AbstractController
|
||||
$needData = ['code' => $needCode ? 'need' : 'no'];
|
||||
return Base::retError($msg, $needData);
|
||||
};
|
||||
//
|
||||
$user = User::whereEmail($email)->first();
|
||||
$usePassword = true;
|
||||
if (LdapUser::isOpen()) {
|
||||
if (empty($user) || $user->isLdap()) {
|
||||
$user = LdapUser::userLogin($email, $password, $user);
|
||||
if ($user) {
|
||||
$user->identity = Base::arrayImplode(array_merge(array_diff($user->identity, ['ldap']), ['ldap']));
|
||||
$user->save();
|
||||
}
|
||||
$usePassword = false;
|
||||
}
|
||||
}
|
||||
if (empty($user)) {
|
||||
return $retError('帐号或密码错误');
|
||||
}
|
||||
if ($user->password != Base::md52($password, $user->encrypt)) {
|
||||
if ($usePassword && $user->password != Doo::md5s($password, $user->encrypt)) {
|
||||
return $retError('帐号或密码错误');
|
||||
}
|
||||
//
|
||||
@@ -116,12 +139,13 @@ class UsersController extends AbstractController
|
||||
];
|
||||
$user->updateInstance($array);
|
||||
$user->save();
|
||||
User::token($user);
|
||||
User::generateToken($user);
|
||||
LdapUser::userSync($user, $password);
|
||||
//
|
||||
if (!Project::withTrashed()->whereUserid($user->userid)->wherePersonal(1)->exists()) {
|
||||
Project::createProject([
|
||||
'name' => Base::Lang('个人项目'),
|
||||
'desc' => Base::Lang('注册时系统自动创建项目,你可以自由删除。'),
|
||||
'name' => Doo::translate('个人项目'),
|
||||
'desc' => Doo::translate('注册时系统自动创建项目,你可以自由删除。'),
|
||||
'personal' => 1,
|
||||
], $user->userid);
|
||||
}
|
||||
@@ -130,7 +154,58 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/login/needcode 02. 是否需要验证码
|
||||
* @api {get} api/users/login/qrcode 02. 二维码登录
|
||||
*
|
||||
* @apiDescription 通过二维码code登录 (或:是否登录成功)
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup users
|
||||
* @apiName login__qrcode
|
||||
*
|
||||
* @apiParam {String} type 类型
|
||||
* - login: 登录(用于:app登录)
|
||||
* - status: 状态 (默认,用于:网页、客户端获取)
|
||||
* @apiParam {String} code 二维码 code
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1需要、0不需要)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function login__qrcode()
|
||||
{
|
||||
$type = trim(Request::input('type'));
|
||||
$code = trim(Request::input('code'));
|
||||
$key = "User::qrcode:" . $code;
|
||||
//
|
||||
if (strlen($code) < 32) {
|
||||
return Base::retError("参数错误");
|
||||
}
|
||||
//
|
||||
if ($type === 'login') {
|
||||
$user = User::auth();
|
||||
Cache::put($key, $user->userid, Carbon::now()->addSeconds(30));
|
||||
return Base::retSuccess("扫码成功");
|
||||
}
|
||||
//
|
||||
$userid = intval(Cache::get($key));
|
||||
if ($userid > 0 && $user = User::whereUserid($userid)->first()) {
|
||||
$array = [
|
||||
'login_num' => $user->login_num + 1,
|
||||
'last_ip' => Base::getIp(),
|
||||
'last_at' => Carbon::now(),
|
||||
'line_ip' => Base::getIp(),
|
||||
'line_at' => Carbon::now(),
|
||||
];
|
||||
$user->updateInstance($array);
|
||||
$user->save();
|
||||
User::generateToken($user);
|
||||
return Base::retSuccess("success", $user);
|
||||
}
|
||||
//
|
||||
return Base::retError("No identity");
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/login/needcode 03. 是否需要验证码
|
||||
*
|
||||
* @apiDescription 用于判断是否需要登录验证码
|
||||
* @apiVersion 1.0.0
|
||||
@@ -149,7 +224,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/login/codeimg 03. 验证码图片
|
||||
* @api {get} api/users/login/codeimg 04. 验证码图片
|
||||
*
|
||||
* @apiDescription 用于判断是否需要登录验证码
|
||||
* @apiVersion 1.0.0
|
||||
@@ -164,7 +239,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/login/codejson 04. 验证码json
|
||||
* @api {get} api/users/login/codejson 05. 验证码json
|
||||
*
|
||||
* @apiDescription 用于判断是否需要登录验证码
|
||||
* @apiVersion 1.0.0
|
||||
@@ -182,7 +257,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/reg/needinvite 05. 是否需要邀请码
|
||||
* @api {get} api/users/reg/needinvite 06. 是否需要邀请码
|
||||
*
|
||||
* @apiDescription 用于判断注册是否需要邀请码
|
||||
* @apiVersion 1.0.0
|
||||
@@ -201,7 +276,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/info 06. 获取我的信息
|
||||
* @api {get} api/users/info 07. 获取我的信息
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -232,7 +307,16 @@ class UsersController extends AbstractController
|
||||
public function info()
|
||||
{
|
||||
$user = User::auth();
|
||||
User::token($user);
|
||||
//
|
||||
$refreshToken = false;
|
||||
if (in_array(Base::platform(), ['ios', 'android'])) {
|
||||
// 移动端token还剩7天到期时获取新的token
|
||||
$expiredAt = Doo::userExpiredAt();
|
||||
if ($expiredAt && Carbon::parse($expiredAt)->isBefore(Carbon::now()->addDays(7))) {
|
||||
$refreshToken = true;
|
||||
}
|
||||
}
|
||||
User::generateToken($user, $refreshToken);
|
||||
//
|
||||
$data = $user->toArray();
|
||||
$data['nickname_original'] = $user->getRawOriginal('nickname');
|
||||
@@ -241,7 +325,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/editdata 07. 修改自己的资料
|
||||
* @api {get} api/users/editdata 08. 修改自己的资料
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -260,14 +344,20 @@ class UsersController extends AbstractController
|
||||
public function editdata()
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$data = Request::all();
|
||||
$user->checkSystem(1);
|
||||
$upLdap = [];
|
||||
// 头像
|
||||
if (Arr::exists($data, 'userimg')) {
|
||||
$userimg = Request::input('userimg');
|
||||
$user->userimg = $userimg ? Base::unFillUrl(is_array($userimg) ? $userimg[0]['path'] : $userimg) : '';
|
||||
if (str_contains($user->userimg, 'avatar/')) {
|
||||
$user->userimg = '';
|
||||
$userimg = $userimg ? Base::unFillUrl(is_array($userimg) ? $userimg[0]['path'] : $userimg) : '';
|
||||
if (str_contains($userimg, 'avatar/')) {
|
||||
$userimg = '';
|
||||
}
|
||||
$user->userimg = $userimg;
|
||||
if (file_exists(public_path($userimg))) {
|
||||
$upLdap['jpegPhoto'] = file_get_contents(public_path($userimg));
|
||||
}
|
||||
}
|
||||
// 电话
|
||||
@@ -280,6 +370,7 @@ class UsersController extends AbstractController
|
||||
return Base::retError('联系电话已存在');
|
||||
}
|
||||
$user->tel = $tel;
|
||||
$upLdap['mobile'] = $tel;
|
||||
}
|
||||
// 昵称
|
||||
if (Arr::exists($data, 'nickname')) {
|
||||
@@ -292,6 +383,7 @@ class UsersController extends AbstractController
|
||||
$user->nickname = $nickname;
|
||||
$user->az = Base::getFirstCharter($nickname);
|
||||
$user->pinyin = Base::cn2pinyin($nickname);
|
||||
$upLdap['displayName'] = $nickname;
|
||||
}
|
||||
}
|
||||
// 职位/职称
|
||||
@@ -303,20 +395,19 @@ class UsersController extends AbstractController
|
||||
return Base::retError('职位/职称最多只能设置20个字');
|
||||
} else {
|
||||
$user->profession = $profession;
|
||||
$upLdap['employeeType'] = $profession;
|
||||
}
|
||||
}
|
||||
//
|
||||
$user->save();
|
||||
User::token($user);
|
||||
User::generateToken($user);
|
||||
LdapUser::userUpdate($user->email, $upLdap);
|
||||
//
|
||||
if (empty($user->userimg)) {
|
||||
$user->userimg = $user->getUserimgAttribute(null);
|
||||
}
|
||||
return Base::retSuccess('修改成功', $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/editpass 08. 修改自己的密码
|
||||
* @api {get} api/users/editpass 09. 修改自己的密码
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -342,21 +433,22 @@ class UsersController extends AbstractController
|
||||
}
|
||||
User::passwordPolicy($newpass);
|
||||
//
|
||||
$verify = User::whereUserid($user->userid)->wherePassword(Base::md52($oldpass, User::token2encrypt()))->count();
|
||||
$verify = User::whereUserid($user->userid)->wherePassword(Doo::md5s($oldpass, Doo::userEncrypt()))->count();
|
||||
if (empty($verify)) {
|
||||
return Base::retError('请填写正确的旧密码');
|
||||
}
|
||||
//
|
||||
$user->encrypt = Base::generatePassword(6);
|
||||
$user->password = Base::md52($newpass, $user->encrypt);
|
||||
$user->password = Doo::md5s($newpass, $user->encrypt);
|
||||
$user->changepass = 0;
|
||||
$user->save();
|
||||
User::token($user);
|
||||
User::generateToken($user);
|
||||
LdapUser::userUpdate($user->email, ['userPassword' => $newpass]);
|
||||
return Base::retSuccess('修改成功', $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/search 09. 搜索会员列表
|
||||
* @api {get} api/users/search 10. 搜索会员列表
|
||||
*
|
||||
* @apiDescription 搜索会员列表
|
||||
* @apiVersion 1.0.0
|
||||
@@ -364,8 +456,8 @@ class UsersController extends AbstractController
|
||||
* @apiName searchinfo
|
||||
*
|
||||
* @apiParam {Object} keys 搜索条件
|
||||
* - keys.key 昵称、邮箱关键字
|
||||
* - keys.disable 0-排除禁止(默认),1-仅禁止,2-含禁止
|
||||
* - keys.key 昵称、拼音、邮箱关键字
|
||||
* - keys.disable 0-排除离职(默认),1-仅离职,2-含离职
|
||||
* - keys.bot 0-排除机器人(默认),1-仅机器人,2-含机器人
|
||||
* - keys.project_id 在指定项目ID
|
||||
* - keys.no_project_id 不在指定项目ID
|
||||
@@ -387,7 +479,12 @@ class UsersController extends AbstractController
|
||||
*/
|
||||
public function search()
|
||||
{
|
||||
$builder = User::select(User::$basicField);
|
||||
$user = User::auth();
|
||||
//
|
||||
$columns = User::$basicField;
|
||||
$columns[] = 'created_at';
|
||||
$columns[] = 'identity';
|
||||
$builder = User::select($columns);
|
||||
//
|
||||
$keys = Request::input('keys');
|
||||
$sorts = Request::input('sorts');
|
||||
@@ -397,10 +494,14 @@ class UsersController extends AbstractController
|
||||
$sorts = is_array($sorts) ? $sorts : [];
|
||||
//
|
||||
if ($keys['key']) {
|
||||
$builder->where(function($query) use ($keys) {
|
||||
$query->where("email", "like", "%{$keys['key']}%")
|
||||
->orWhere("nickname", "like", "%{$keys['key']}%");
|
||||
});
|
||||
if (str_contains($keys['key'], "@")) {
|
||||
$builder->where("email", "like", "%{$keys['key']}%");
|
||||
} else {
|
||||
$builder->where(function($query) use ($keys) {
|
||||
$query->where("nickname", "like", "%{$keys['key']}%")
|
||||
->orWhere("pinyin", "like", "%{$keys['key']}%");
|
||||
});
|
||||
}
|
||||
}
|
||||
if (intval($keys['disable']) == 0) {
|
||||
$builder->whereNull("disable_at");
|
||||
@@ -432,6 +533,13 @@ class UsersController extends AbstractController
|
||||
}
|
||||
if (in_array($sorts['az'], ['asc', 'desc'])) {
|
||||
$builder->orderBy('az', $sorts['az']);
|
||||
} else {
|
||||
if (intval($keys['disable']) == 2) {
|
||||
$builder->orderBy('disable_at');
|
||||
}
|
||||
if (intval($keys['bot']) == 2) {
|
||||
$builder->orderBy('bot');
|
||||
}
|
||||
}
|
||||
//
|
||||
if (Request::exists('page')) {
|
||||
@@ -440,17 +548,38 @@ class UsersController extends AbstractController
|
||||
$list = $builder->orderBy('userid')->take(Base::getPaginate(100, 10, 'take'))->get();
|
||||
}
|
||||
//
|
||||
if ($state === 1) {
|
||||
$list->transform(function (User $userInfo) {
|
||||
$userInfo->online = $userInfo->getOnlineStatus();
|
||||
return $userInfo;
|
||||
$list->transform(function (User $userInfo) use ($user, $state) {
|
||||
$tags = [];
|
||||
$dep = $userInfo->getDepartmentName();
|
||||
$dep = array_filter(explode(",", $dep), function($item) {
|
||||
return preg_match("/\(M\)$/", $item);
|
||||
});
|
||||
}
|
||||
if ($dep) {
|
||||
$tags[] = preg_replace("/\(M\)$/", "", trim($dep[0])) . Doo::translate("负责人");
|
||||
}
|
||||
if ($user->isAdmin()) {
|
||||
if ($userInfo->isAdmin()) {
|
||||
$tags[] = Doo::translate("系统管理员");
|
||||
}
|
||||
if ($userInfo->isTemp()) {
|
||||
$tags[] = Doo::translate("临时帐号");
|
||||
}
|
||||
if ($userInfo->userid > 3 && Carbon::parse($userInfo->created_at)->isAfter(Carbon::now()->subDays(30))) {
|
||||
$tags[] = Doo::translate("新帐号");
|
||||
}
|
||||
}
|
||||
$userInfo->tags = $tags;
|
||||
//
|
||||
if ($state === 1) {
|
||||
$userInfo->online = $userInfo->getOnlineStatus();
|
||||
}
|
||||
return $userInfo;
|
||||
});
|
||||
return Base::retSuccess('success', $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/basic 10. 获取指定会员基础信息
|
||||
* @api {get} api/users/basic 11. 获取指定会员基础信息
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -489,7 +618,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/lists 11. 会员列表(限管理员)
|
||||
* @api {get} api/users/lists 12. 会员列表(限管理员)
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -590,7 +719,16 @@ class UsersController extends AbstractController
|
||||
$query->where("department", "")->orWhere("department", ",,");
|
||||
});
|
||||
} else {
|
||||
$builder->where("department", "like", "%,{$keys['department']},%");
|
||||
// 关联user_departments表中owner_userid查询出负责人,重新排序,部门负责人始终在前面
|
||||
$builder->where(function($query) use ($keys) {
|
||||
$query->where("department", "like", "%,{$keys['department']},%");
|
||||
$query->orWhereIn('userid', function ($query) use ($keys) {
|
||||
$query->select('owner_userid')->from('user_departments')->where("id", "=", trim($keys['department'], ','));
|
||||
});
|
||||
});
|
||||
$prefix = \DB::getTablePrefix();
|
||||
$builder->selectRaw("if(EXISTS(select id from {$prefix}user_departments where owner_userid = userid and id={$keys['department']}),1,0) as is_principal");
|
||||
$builder->orderBy("is_principal","desc");
|
||||
}
|
||||
}
|
||||
if ($getCheckinMac && isset($keys['checkin_mac'])) {
|
||||
@@ -602,11 +740,14 @@ class UsersController extends AbstractController
|
||||
$builder->whereNull('disable_at');
|
||||
$builder->where('bot', 0);
|
||||
}
|
||||
$list = $builder->orderByDesc('userid')->paginate(Base::getPaginate(50, 20));
|
||||
$builder = $keys['department'] == '0' ? $builder->orderByDesc('userid') : $builder;
|
||||
$list = $builder->paginate(Base::getPaginate(50, 20));
|
||||
//
|
||||
if ($getCheckinMac) {
|
||||
$list->transform(function (User $user) {
|
||||
$user->checkin_macs = UserCheckinMac::select(['id', 'mac', 'remark'])->whereUserid($user->userid)->orderBy('id')->get();
|
||||
$list->transform(function (User $user) use ($getCheckinMac) {
|
||||
if($getCheckinMac){
|
||||
$user->checkin_macs = UserCheckinMac::select(['id', 'mac', 'remark'])->whereUserid($user->userid)->orderBy('id')->get();
|
||||
}
|
||||
return $user;
|
||||
});
|
||||
}
|
||||
@@ -615,7 +756,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/operation 12. 操作会员(限管理员)
|
||||
* @api {get} api/users/operation 13. 操作会员(限管理员)
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -626,6 +767,8 @@ class UsersController extends AbstractController
|
||||
* @apiParam {String} [type] 操作
|
||||
* - setadmin 设为管理员
|
||||
* - clearadmin 取消管理员
|
||||
* - settemp 设为临时帐号
|
||||
* - cleartemp 取消临时身份(取消临时帐号)
|
||||
* - checkin_macs 修改自动签到mac地址(需要参数 checkin_macs)
|
||||
* - department 修改部门(需要参数 department)
|
||||
* - setdisable 设为离职(需要参数 disable_time、transfer_userid)
|
||||
@@ -661,6 +804,7 @@ class UsersController extends AbstractController
|
||||
$userInfo->checkSystem(1);
|
||||
//
|
||||
$upArray = [];
|
||||
$upLdap = [];
|
||||
$transferUser = null;
|
||||
switch ($type) {
|
||||
case 'setadmin':
|
||||
@@ -672,6 +816,15 @@ class UsersController extends AbstractController
|
||||
$upArray['identity'] = array_diff($userInfo->identity, ['admin']);
|
||||
break;
|
||||
|
||||
case 'settemp':
|
||||
$upArray['identity'] = array_diff($userInfo->identity, ['temp']);
|
||||
$upArray['identity'][] = 'temp';
|
||||
break;
|
||||
|
||||
case 'cleartemp':
|
||||
$upArray['identity'] = array_diff($userInfo->identity, ['temp']);
|
||||
break;
|
||||
|
||||
case 'checkin_macs':
|
||||
$list = is_array($data['checkin_macs']) ? $data['checkin_macs'] : [];
|
||||
$array = [];
|
||||
@@ -737,10 +890,10 @@ class UsersController extends AbstractController
|
||||
break;
|
||||
}
|
||||
if (isset($upArray['identity'])) {
|
||||
$upArray['identity'] = "," . implode(",", $upArray['identity']) . ",";
|
||||
$upArray['identity'] = Base::arrayImplode($upArray['identity']);
|
||||
}
|
||||
if (isset($upArray['department'])) {
|
||||
$upArray['department'] = "," . implode(",", $upArray['department']) . ",";
|
||||
$upArray['department'] = Base::arrayImplode($upArray['department']);
|
||||
}
|
||||
// 邮箱
|
||||
if (Arr::exists($data, 'email')) {
|
||||
@@ -748,6 +901,9 @@ class UsersController extends AbstractController
|
||||
if (User::whereEmail($email)->where('userid', '!=', $userInfo->userid)->exists()) {
|
||||
return Base::retError('邮箱地址已存在');
|
||||
}
|
||||
if ($userInfo->isLdap()) {
|
||||
return Base::retError('LDAP 用户禁止修改邮箱');
|
||||
}
|
||||
$upArray['email'] = $email;
|
||||
}
|
||||
// 电话
|
||||
@@ -757,14 +913,16 @@ class UsersController extends AbstractController
|
||||
return Base::retError('联系电话已存在');
|
||||
}
|
||||
$upArray['tel'] = $tel;
|
||||
$upLdap['mobile'] = $tel;
|
||||
}
|
||||
// 密码
|
||||
if (Arr::exists($data, 'password')) {
|
||||
$password = trim($data['password']);
|
||||
User::passwordPolicy($password);
|
||||
$upArray['encrypt'] = Base::generatePassword(6);
|
||||
$upArray['password'] = Base::md52($password, $upArray['encrypt']);
|
||||
$upArray['password'] = Doo::md5s($password, $upArray['encrypt']);
|
||||
$upArray['changepass'] = 1;
|
||||
$upLdap['userPassword'] = $password;
|
||||
}
|
||||
// 昵称
|
||||
if (Arr::exists($data, 'nickname')) {
|
||||
@@ -777,6 +935,7 @@ class UsersController extends AbstractController
|
||||
$upArray['nickname'] = $nickname;
|
||||
$upArray['az'] = Base::getFirstCharter($nickname);
|
||||
$upArray['pinyin'] = Base::cn2pinyin($nickname);
|
||||
$upLdap['displayName'] = $nickname;
|
||||
}
|
||||
}
|
||||
// 职位/职称
|
||||
@@ -788,14 +947,16 @@ class UsersController extends AbstractController
|
||||
return Base::retError('职位/职称最多只能设置20个字');
|
||||
} else {
|
||||
$upArray['profession'] = $profession;
|
||||
$upLdap['employeeType'] = $profession;
|
||||
}
|
||||
}
|
||||
if ($upArray) {
|
||||
AbstractModel::transaction(function() use ($user, $type, $upArray, $userInfo, $transferUser) {
|
||||
AbstractModel::transaction(function() use ($upLdap, $user, $type, $upArray, $userInfo, $transferUser) {
|
||||
$exitIds = array_diff($userInfo->department, Base::explodeInt($upArray['department']));
|
||||
$joinIds = array_diff(Base::explodeInt($upArray['department']), $userInfo->department);
|
||||
$userInfo->updateInstance($upArray);
|
||||
$userInfo->save();
|
||||
LdapUser::userUpdate($userInfo->email, $upLdap);
|
||||
if ($type === 'department') {
|
||||
$userids = [$userInfo->userid];
|
||||
// 退出群组
|
||||
@@ -835,7 +996,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/email/verification 13. 邮箱验证
|
||||
* @api {get} api/users/email/verification 14. 邮箱验证
|
||||
*
|
||||
* @apiDescription 不需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -883,7 +1044,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/umeng/alias 14. 设置友盟别名
|
||||
* @api {get} api/users/umeng/alias 15. 设置友盟别名
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -905,12 +1066,7 @@ class UsersController extends AbstractController
|
||||
'alias.between:2,20' => '别名的长度在2-20个字符',
|
||||
]);
|
||||
//
|
||||
$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 {
|
||||
if (!in_array(Base::platform(), ['ios', 'android'])) {
|
||||
return Base::retError('设备类型错误');
|
||||
}
|
||||
//
|
||||
@@ -918,7 +1074,7 @@ class UsersController extends AbstractController
|
||||
$inArray = [
|
||||
'userid' => $user->userid,
|
||||
'alias' => $data['alias'],
|
||||
'platform' => $platform,
|
||||
'platform' => Base::platform(),
|
||||
];
|
||||
$row = UmengAlias::where($inArray);
|
||||
if ($row->exists()) {
|
||||
@@ -934,7 +1090,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/meeting/open 15. 【会议】创建会议、加入会议
|
||||
* @api {get} api/users/meeting/open 16. 【会议】创建会议、加入会议
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1008,7 +1164,7 @@ class UsersController extends AbstractController
|
||||
if (!User::whereUserid($userid)->exists()) {
|
||||
continue;
|
||||
}
|
||||
$dialog = WebSocketDialog::checkUserDialog($user->userid, $userid);
|
||||
$dialog = WebSocketDialog::checkUserDialog($user, $userid);
|
||||
if ($dialog) {
|
||||
$res = WebSocketDialogMsg::sendMsg(null, $dialog->id, 'meeting', $data, $user->userid);
|
||||
if (Base::isSuccess($res)) {
|
||||
@@ -1026,7 +1182,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/meeting/invitation 16. 【会议】发送邀请
|
||||
* @api {get} api/users/meeting/invitation 17. 【会议】发送邀请
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1058,7 +1214,7 @@ class UsersController extends AbstractController
|
||||
if (!User::whereUserid($userid)->exists()) {
|
||||
continue;
|
||||
}
|
||||
$dialog = WebSocketDialog::checkUserDialog($user->userid, $userid);
|
||||
$dialog = WebSocketDialog::checkUserDialog($user, $userid);
|
||||
if ($dialog) {
|
||||
$res = WebSocketDialogMsg::sendMsg(null, $dialog->id, 'meeting', $data, $user->userid);
|
||||
if (Base::isSuccess($res)) {
|
||||
@@ -1072,7 +1228,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/email/send 17. 发送邮箱验证码
|
||||
* @api {get} api/users/email/send 18. 发送邮箱验证码
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1112,7 +1268,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/email/edit 18. 修改邮箱
|
||||
* @api {get} api/users/email/edit 19. 修改邮箱
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1132,6 +1288,10 @@ class UsersController extends AbstractController
|
||||
//
|
||||
$user->checkSystem();
|
||||
//
|
||||
if ($user->isLdap()) {
|
||||
return Base::retError('LDAP 用户禁止修改邮箱');
|
||||
}
|
||||
//
|
||||
$newEmail = trim(Request::input('newEmail'));
|
||||
$code = trim(Request::input('code'));
|
||||
if (!$newEmail) {
|
||||
@@ -1148,12 +1308,12 @@ class UsersController extends AbstractController
|
||||
|
||||
$user->email = $newEmail;
|
||||
$user->save();
|
||||
User::token($user);
|
||||
User::generateToken($user);
|
||||
return Base::retSuccess('修改成功', $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/delete/account 19. 删除帐号
|
||||
* @api {get} api/users/delete/account 20. 删除帐号
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1200,7 +1360,7 @@ class UsersController extends AbstractController
|
||||
if (!$password) {
|
||||
return Base::retError('请输入登录密码');
|
||||
}
|
||||
if ($user->password != Base::md52($password, $user->encrypt)) {
|
||||
if ($user->password != Doo::md5s($password, $user->encrypt)) {
|
||||
return Base::retError('密码错误');
|
||||
}
|
||||
}
|
||||
@@ -1215,7 +1375,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/department/list 20. 部门列表(限管理员)
|
||||
* @api {get} api/users/department/list 21. 部门列表(限管理员)
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1234,7 +1394,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/department/add 21. 新建、修改部门(限管理员)
|
||||
* @api {get} api/users/department/add 22. 新建、修改部门(限管理员)
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1268,6 +1428,12 @@ class UsersController extends AbstractController
|
||||
if (mb_strlen($name) < 2 || mb_strlen($name) > 20) {
|
||||
return Base::retError('部门名称长度限制2-20个字');
|
||||
}
|
||||
if (preg_match('/[\Q~!@#$%^&*()+-_=.:?<>,\E]/', $name)) {
|
||||
return Base::retError('部门名称不能包含特殊符号');
|
||||
}
|
||||
if (str_contains($name, '(M)')) {
|
||||
return Base::retError('部门名称不能包含:(M)');
|
||||
}
|
||||
//
|
||||
if ($id > 0) {
|
||||
$userDepartment = UserDepartment::find($id);
|
||||
@@ -1305,12 +1471,13 @@ class UsersController extends AbstractController
|
||||
'parent_id' => $parent_id,
|
||||
'owner_userid' => $owner_userid,
|
||||
], $dialog_useid);
|
||||
Cache::forever("UserDepartment::rand", Base::generatePassword());
|
||||
//
|
||||
return Base::retSuccess($parent_id > 0 ? '保存成功' : '新建成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/department/del 22. 删除部门(限管理员)
|
||||
* @api {get} api/users/department/del 23. 删除部门(限管理员)
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1334,12 +1501,13 @@ class UsersController extends AbstractController
|
||||
return Base::retError('部门不存在或已被删除');
|
||||
}
|
||||
$userDepartment->deleteDepartment();
|
||||
Cache::forever("UserDepartment::rand", Base::generatePassword());
|
||||
//
|
||||
return Base::retSuccess('删除成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/checkin/get 22. 获取签到设置
|
||||
* @api {get} api/users/checkin/get 24. 获取签到设置
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1360,7 +1528,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/users/checkin/save 22. 保存签到设置
|
||||
* @api {post} api/users/checkin/save 25. 保存签到设置
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1385,7 +1553,7 @@ class UsersController extends AbstractController
|
||||
return Base::retError('未开放修改权限,请联系管理员');
|
||||
}
|
||||
//
|
||||
$list = Base::getPostValue('list');
|
||||
$list = Request::input('list');
|
||||
$array = [];
|
||||
if (empty($list) || !is_array($list)) {
|
||||
return Base::retError('参数错误');
|
||||
@@ -1408,7 +1576,7 @@ class UsersController extends AbstractController
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/checkin/list 22. 获取签到数据
|
||||
* @api {get} api/users/checkin/list 26. 获取签到数据
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
@@ -1416,6 +1584,7 @@ class UsersController extends AbstractController
|
||||
* @apiName checkin__list
|
||||
*
|
||||
* @apiParam {String} ym 年-月(如:2020-01)
|
||||
* @apiParam {Number} [before] 取月份之前的数据(单位:月数,最大3)
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
@@ -1429,6 +1598,11 @@ class UsersController extends AbstractController
|
||||
$start = Carbon::parse(date("Y-m-01 00:00:00", strtotime($ym)));
|
||||
$end = (clone $start)->addMonth()->subSecond();
|
||||
//
|
||||
$before = min(3, intval(Request::input('before')));
|
||||
if ($before > 0) {
|
||||
$start = $start->subMonths($before);
|
||||
}
|
||||
//
|
||||
$recordTimes = UserCheckinRecord::getTimes($user->userid, [$start, $end]);
|
||||
$array = [];
|
||||
$startT = $start->timestamp;
|
||||
@@ -1447,4 +1621,177 @@ class UsersController extends AbstractController
|
||||
//
|
||||
return Base::retSuccess('success', $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/socket/status 27. 获取socket状态
|
||||
*
|
||||
* @apiDescription 需要token身份
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup users
|
||||
* @apiName socket__status
|
||||
*
|
||||
* @apiParam {String} [fd]
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function socket__status()
|
||||
{
|
||||
$row = WebSocket::select(['id', 'fd', 'userid', 'updated_at'])->whereFd(Base::headerOrInput('fd'))->first();
|
||||
if (empty($row)) {
|
||||
return Base::retError('error');
|
||||
}
|
||||
return Base::retSuccess('success', $row);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/key/client 28. 客户端KEY
|
||||
*
|
||||
* @apiDescription 获取客户端KEY,用于加密数据发送给服务端
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup users
|
||||
* @apiName key__client
|
||||
*
|
||||
* @apiParam {String} [client_id] 客户端ID(希望不变的,除非清除浏览器缓存或者卸载应用)
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function key__client()
|
||||
{
|
||||
$clientId = (trim(Request::input('client_id')) ?: Base::generatePassword(6)) . Doo::userId();
|
||||
//
|
||||
$cacheKey = "KeyPair::" . $clientId;
|
||||
if (Cache::has($cacheKey)) {
|
||||
$cacheData = Base::json2array(Cache::get($cacheKey));
|
||||
if ($cacheData['private_key']) {
|
||||
return Base::retSuccess('success', [
|
||||
'type' => 'pgp',
|
||||
'id' => $clientId,
|
||||
'key' => $cacheData['public_key'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
//
|
||||
$name = Doo::userEmail() ?: Base::generatePassword(6);
|
||||
$email = Doo::userEmail() ?: 'aa@bb.cc';
|
||||
$data = Doo::pgpGenerateKeyPair($name, $email, Base::generatePassword());
|
||||
Cache::put("KeyPair::" . $clientId, Base::array2json($data), Carbon::now()->addQuarter());
|
||||
//
|
||||
return Base::retSuccess('success', [
|
||||
'type' => 'pgp',
|
||||
'id' => $clientId,
|
||||
'key' => $data['public_key'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/users/bot/info 29. 机器人信息
|
||||
*
|
||||
* @apiDescription 需要token身份,获取我的机器人信息
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup users
|
||||
* @apiName bot__info
|
||||
*
|
||||
* @apiParam {Number} id 机器人ID
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function bot__info()
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$botId = intval(Request::input('id'));
|
||||
$botUser = User::whereUserid($botId)->whereBot(1)->first();
|
||||
if (empty($botUser)) {
|
||||
return Base::retError('机器人不存在');
|
||||
}
|
||||
$userBot = UserBot::whereBotId($botUser->userid)->whereUserid($user->userid)->first();
|
||||
if (empty($userBot)) {
|
||||
return Base::retError('不是你的机器人');
|
||||
}
|
||||
return Base::retSuccess('success', [
|
||||
'id' => $botUser->userid,
|
||||
'name' => $botUser->nickname,
|
||||
'avatar' => $botUser->userimg,
|
||||
'clear_day' => $userBot->clear_day,
|
||||
'webhook_url' => $userBot->webhook_url,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/users/bot/edit 30. 编辑机器人
|
||||
*
|
||||
* @apiDescription 需要token身份,编辑我的机器人信息
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup users
|
||||
* @apiName bot__edit
|
||||
*
|
||||
* @apiParam {Number} id 机器人ID
|
||||
* @apiParam {String} [name] 机器人名称
|
||||
* @apiParam {String} [avatar] 机器人头像
|
||||
* @apiParam {Number} [clear_day] 清理天数
|
||||
* @apiParam {String} [webhook_url] Webhook地址
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function bot__edit()
|
||||
{
|
||||
$user = User::auth();
|
||||
//
|
||||
$botId = intval(Request::input('id'));
|
||||
$botUser = User::whereUserid($botId)->whereBot(1)->first();
|
||||
if (empty($botUser)) {
|
||||
return Base::retError('机器人不存在');
|
||||
}
|
||||
$userBot = UserBot::whereBotId($botUser->userid)->whereUserid($user->userid)->first();
|
||||
if (empty($userBot)) {
|
||||
return Base::retError('不是你的机器人');
|
||||
}
|
||||
//
|
||||
$data = Request::input();
|
||||
$upUser = [];
|
||||
$upBot = [];
|
||||
//
|
||||
if (isset($data['name'])) {
|
||||
$upUser['nickname'] = trim($data['name']);
|
||||
}
|
||||
if (isset($data['avatar'])) {
|
||||
$avatar = $data['avatar'];
|
||||
$avatar = $avatar ? Base::unFillUrl(is_array($avatar) ? $avatar[0]['path'] : $avatar) : '';
|
||||
if (str_contains($avatar, 'avatar/')) {
|
||||
$avatar = '';
|
||||
}
|
||||
$upUser['userimg'] = $avatar;
|
||||
}
|
||||
if (isset($data['clear_day'])) {
|
||||
$upBot['clear_day'] = min(max(intval($data['clear_day']), 1), 999);
|
||||
}
|
||||
if (isset($data['webhook_url'])) {
|
||||
$upBot['webhook_url'] = trim($data['webhook_url']);
|
||||
}
|
||||
//
|
||||
if ($upUser) {
|
||||
$botUser->updateInstance($upUser);
|
||||
$botUser->save();
|
||||
}
|
||||
if ($upBot) {
|
||||
$userBot->updateInstance($upBot);
|
||||
$userBot->save();
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('修改成功', [
|
||||
'id' => $botUser->userid,
|
||||
'name' => $botUser->nickname,
|
||||
'avatar' => $botUser->userimg,
|
||||
'clear_day' => $userBot->clear_day,
|
||||
'webhook_url' => $userBot->webhook_url,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -49,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\"");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,28 +83,28 @@ class IndexController extends InvokeController
|
||||
}
|
||||
$array = [
|
||||
"office/web-apps/apps/api/documents/api.js?hash=" . Base::getVersion(),
|
||||
"office/7.2.2-56/web-apps/vendor/requirejs/require.js",
|
||||
"office/7.2.2-56/web-apps/apps/api/documents/api.js",
|
||||
"office/7.2.2-56/sdkjs/common/AllFonts.js",
|
||||
"office/7.2.2-56/web-apps/vendor/xregexp/xregexp-all-min.js",
|
||||
"office/7.2.2-56/web-apps/vendor/sockjs/sockjs.min.js",
|
||||
"office/7.2.2-56/web-apps/vendor/jszip/jszip.min.js",
|
||||
"office/7.2.2-56/web-apps/vendor/jszip-utils/jszip-utils.min.js",
|
||||
"office/7.2.2-56/sdkjs/common/libfont/wasm/fonts.js",
|
||||
"office/7.2.2-56/sdkjs/common/Charts/ChartStyles.js",
|
||||
"office/7.2.2-56/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.2.2-56/web-apps/apps/presentationeditor/main/app.js",
|
||||
"office/7.2.2-56/sdkjs/slide/sdk-all-min.js",
|
||||
"office/7.2.2-56/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.2.2-56/web-apps/apps/documenteditor/main/app.js",
|
||||
"office/7.2.2-56/sdkjs/word/sdk-all-min.js",
|
||||
"office/7.2.2-56/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.2.2-56/web-apps/apps/spreadsheeteditor/main/app.js",
|
||||
"office/7.2.2-56/sdkjs/cell/sdk-all-min.js",
|
||||
"office/7.2.2-56/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);
|
||||
@@ -187,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";
|
||||
}
|
||||
@@ -199,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;
|
||||
}
|
||||
@@ -234,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),
|
||||
];
|
||||
@@ -248,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),
|
||||
];
|
||||
@@ -266,9 +283,9 @@ 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);
|
||||
}
|
||||
@@ -283,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,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,53 +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/users/checkin/save/',
|
||||
|
||||
// 签到上报
|
||||
'api/public/checkin/report/',
|
||||
// 接口部分
|
||||
'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
|
||||
|
||||
30
app/Models/ApproveProcMsg.php
Normal file
30
app/Models/ApproveProcMsg.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* App\Models\ApproveProcMsg
|
||||
*
|
||||
* @property int $id
|
||||
* @property string|null $type 信息类型(如:candidate-候选人、participant-参与人、notifier-抄送人)
|
||||
* @property int|null $proc_inst_id 流程实例ID
|
||||
* @property int|null $userid 会员ID
|
||||
* @property int|null $msg_id 消息ID
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ApproveProcMsg newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ApproveProcMsg newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ApproveProcMsg query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ApproveProcMsg whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ApproveProcMsg whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ApproveProcMsg whereMsgId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ApproveProcMsg whereProcInstId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ApproveProcMsg whereType($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ApproveProcMsg whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ApproveProcMsg whereUserid($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ApproveProcMsg extends AbstractModel
|
||||
{
|
||||
|
||||
}
|
||||
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,27 @@ 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
|
||||
@@ -390,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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理返回图片地址
|
||||
@@ -411,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
|
||||
@@ -97,7 +112,7 @@ class FileContent extends AbstractModel
|
||||
} else {
|
||||
$filePath = public_path($content['url']);
|
||||
}
|
||||
return Response::streamDownload(function() use ($filePath) {
|
||||
return Base::streamDownload(function() use ($filePath) {
|
||||
echo file_get_contents($filePath);
|
||||
}, $name);
|
||||
}
|
||||
@@ -128,7 +143,7 @@ class FileContent extends AbstractModel
|
||||
if ($download) {
|
||||
$filePath = public_path($path);
|
||||
if (isset($filePath)) {
|
||||
return Response::streamDownload(function() use ($filePath) {
|
||||
return Base::streamDownload(function() use ($filePath) {
|
||||
echo file_get_contents($filePath);
|
||||
}, $name);
|
||||
} else {
|
||||
|
||||
@@ -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) {
|
||||
@@ -674,9 +672,6 @@ class ProjectTask extends AbstractModel
|
||||
'owner' => 1,
|
||||
]);
|
||||
$array[] = $uid;
|
||||
if ($this->parent_id) {
|
||||
break; // 子任务只能是一个负责人
|
||||
}
|
||||
}
|
||||
if ($array) {
|
||||
if (count($older) == 0 && count($array) == 1 && $array[0] == User::userid()) {
|
||||
@@ -764,12 +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]
|
||||
]);
|
||||
$this->taskPush(null, 3);
|
||||
$this->taskPush(null, 3, $newDesc);
|
||||
}
|
||||
// 以下紧顶级任务可修改
|
||||
// 以下仅顶级任务可修改
|
||||
if ($this->parent_id === 0) {
|
||||
// 重复周期
|
||||
$loopAt = $this->loop_at;
|
||||
@@ -1046,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();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1179,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) {
|
||||
// 标记未完成
|
||||
@@ -1191,7 +1188,7 @@ class ProjectTask extends AbstractModel
|
||||
$this->addLog("标记{任务}未完成");
|
||||
if ($addMsg) {
|
||||
WebSocketDialogMsg::sendMsg(null, $this->dialog_id, 'notice', [
|
||||
'notice' => '标记任务未完成'
|
||||
'notice' => "标记任务未完成"
|
||||
], 0, true, true);
|
||||
}
|
||||
} else {
|
||||
@@ -1204,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);
|
||||
}
|
||||
}
|
||||
@@ -1255,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,
|
||||
@@ -1282,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("删除{任务}");
|
||||
@@ -1299,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("还原{任务}");
|
||||
@@ -1434,18 +1434,20 @@ class ProjectTask extends AbstractModel
|
||||
* 任务提醒
|
||||
* @param $userids
|
||||
* @param int $type 0-新任务、1-即将超时、2-已超时、3-修改时间
|
||||
* @param string $suffix 描述后缀
|
||||
* @return void
|
||||
*/
|
||||
public function taskPush($userids, int $type)
|
||||
public function taskPush($userids, int $type, string $suffix = "")
|
||||
{
|
||||
if ($userids === null) {
|
||||
$userids = $this->taskUser->where('owner', 1)->pluck('userid')->toArray();
|
||||
$userids = $this->taskUser->pluck('userid')->toArray();
|
||||
}
|
||||
if (empty($userids)) {
|
||||
return;
|
||||
}
|
||||
$users = User::whereIn('userid', $userids)->whereNull('disable_at')->get();
|
||||
if (empty($users)) {
|
||||
$owners = $this->taskUser->pluck('owner', 'userid')->toArray();
|
||||
$receivers = User::whereIn('userid', $userids)->whereNull('disable_at')->get();
|
||||
if (empty($receivers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1454,27 +1456,32 @@ class ProjectTask extends AbstractModel
|
||||
return;
|
||||
}
|
||||
|
||||
$text = view('push.task', [
|
||||
'type' => str_replace([0, 1, 2, 3], ['start', 'before', 'after', 'times'], $type),
|
||||
'task' => $this,
|
||||
])->render();
|
||||
$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 ($users as $user) {
|
||||
foreach ($receivers as $receiver) {
|
||||
$data = [
|
||||
'type' => $type,
|
||||
'userid' => $user->userid,
|
||||
'userid' => $receiver->userid,
|
||||
'task_id' => $this->id,
|
||||
];
|
||||
$pushLog = ProjectTaskPushLog::where($data)->exists();
|
||||
if ($pushLog) {
|
||||
if (in_array($type, [1, 2]) && ProjectTaskPushLog::where($data)->exists()) {
|
||||
continue;
|
||||
}
|
||||
//
|
||||
$dialog = WebSocketDialog::checkUserDialog($botUser->userid, $data['userid']);
|
||||
$replace = $owners[$receiver->userid] ? "您负责的任务" : "您协助的任务";
|
||||
$dialog = WebSocketDialog::checkUserDialog($botUser, $receiver->userid);
|
||||
if ($dialog) {
|
||||
ProjectTaskPushLog::createInstance($data)->save();
|
||||
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid);
|
||||
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', [
|
||||
'text' => str_replace("您的任务", $replace, $text) . $suffix
|
||||
], $botUser->userid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -19,7 +19,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @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)
|
||||
@@ -28,8 +28,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @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)->whereSilence(0)->whereReadAt(null)->count();
|
||||
$lists = $rows->groupBy('platform');
|
||||
$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;
|
||||
|
||||
@@ -81,19 +82,6 @@ class User extends AbstractModel
|
||||
// 基本信息的字段
|
||||
public static $basicField = ['userid', 'email', 'nickname', 'profession', 'department', 'userimg', 'bot', '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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 昵称
|
||||
* @param $value
|
||||
@@ -149,21 +137,49 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为部门负责人
|
||||
*/
|
||||
public function isDepartmentOwner()
|
||||
{
|
||||
return UserDepartment::where('owner_userid', $this->userid)->exists();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取机器人所有者
|
||||
* @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 = $this->bot || intval(Cache::get("User::online:" . $this->userid, 0)) > 0;
|
||||
$online = $this->bot || Cache::get("User::online:" . $this->userid) === "on";
|
||||
if ($online) {
|
||||
return true;
|
||||
}
|
||||
@@ -171,9 +187,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');
|
||||
}
|
||||
@@ -251,38 +303,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);
|
||||
}
|
||||
@@ -290,73 +344,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
|
||||
@@ -392,8 +379,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);
|
||||
@@ -418,61 +404,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() || $authInfo['timestamp'] === -1) {
|
||||
$row = self::whereUserid($authInfo['userid'])->whereEmail($authInfo['email'])->whereEncrypt($authInfo['encrypt'])->first();
|
||||
if ($row) {
|
||||
if (!$row->bot && $authInfo['timestamp'] === -1) {
|
||||
return $_A["__static_auth"] = false; // 非机器人token时间不允许-1
|
||||
}
|
||||
$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)
|
||||
{
|
||||
$time = $userinfo->bot ? -1 : time();
|
||||
$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', 30)));
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -506,8 +480,7 @@ class User extends AbstractModel
|
||||
*/
|
||||
public static function userid2nickname($userid)
|
||||
{
|
||||
$basic = self::userid2basic($userid);
|
||||
return $basic ? $basic->nickname : '';
|
||||
return self::userid2basic($userid)?->nickname ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -549,14 +522,19 @@ class User extends AbstractModel
|
||||
return Base::fillUrl($userimg);
|
||||
}
|
||||
// 机器人头像
|
||||
if ($email == 'system-msg@bot.system') {
|
||||
return url("images/avatar/default_system.png");
|
||||
} elseif ($email == 'task-alert@bot.system') {
|
||||
return url("images/avatar/default_task.png");
|
||||
} elseif ($email == 'check-in@bot.system') {
|
||||
return url("images/avatar/default_checkin.png");
|
||||
} elseif ($email == 'bot-manager@bot.system') {
|
||||
return url("images/avatar/default_bot.png");
|
||||
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 'approval-alert@bot.system':
|
||||
return url("images/avatar/default_approval.png");
|
||||
case 'bot-manager@bot.system':
|
||||
return url("images/avatar/default_bot.png");
|
||||
}
|
||||
// 生成文字头像
|
||||
if (self::$defaultAvatarMode === 'auto') {
|
||||
@@ -603,19 +581,18 @@ class User extends AbstractModel
|
||||
* @param $key
|
||||
* @param $update
|
||||
* @param $userid
|
||||
* @return self
|
||||
* @return self|null
|
||||
*/
|
||||
public static function botGetOrCreate($key, $update = [], $userid = 0)
|
||||
{
|
||||
$email = "{$key}@bot.system";
|
||||
$botUser = self::whereEmail($email)->first();
|
||||
if (empty($botUser)) {
|
||||
$encrypt = Base::generatePassword(6);
|
||||
$botUser = self::createInstance([
|
||||
'bot' => 1,
|
||||
'encrypt' => $encrypt,
|
||||
'email' => $email,
|
||||
'password' => Base::md52(Base::generatePassword(32), $encrypt),
|
||||
$botUser = Doo::userCreate($email, Base::generatePassword(32));
|
||||
if (empty($botUser)) {
|
||||
return null;
|
||||
}
|
||||
$botUser->updateInstance([
|
||||
'created_ip' => Base::getIp(),
|
||||
]);
|
||||
$botUser->save();
|
||||
@@ -626,14 +603,25 @@ class User extends AbstractModel
|
||||
])->save();
|
||||
}
|
||||
//
|
||||
if ($key === 'system-msg') {
|
||||
$update['nickname'] = '系统消息';
|
||||
} elseif ($key === 'task-alert') {
|
||||
$update['nickname'] = '任务提醒';
|
||||
} elseif ($key === 'check-in') {
|
||||
$update['nickname'] = '签到打卡';
|
||||
} elseif ($key === 'bot-manager') {
|
||||
$update['nickname'] = '机器人管理';
|
||||
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 'approval-alert':
|
||||
$update['nickname'] = '审批';
|
||||
break;
|
||||
case 'bot-manager':
|
||||
$update['nickname'] = '机器人管理';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($update) {
|
||||
|
||||
@@ -2,25 +2,126 @@
|
||||
|
||||
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 => '',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,6 @@ class UserDepartment extends AbstractModel
|
||||
$dialog = WebSocketDialog::find($this->dialog_id);
|
||||
if ($dialog) {
|
||||
$dialog->deleteDialog();
|
||||
$dialog->pushMsg("groupDelete");
|
||||
}
|
||||
//
|
||||
$this->delete();
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Models;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Module\Base;
|
||||
use App\Module\Doo;
|
||||
use App\Tasks\PushTask;
|
||||
use Cache;
|
||||
use Carbon\Carbon;
|
||||
@@ -17,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)
|
||||
@@ -37,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
|
||||
*/
|
||||
@@ -68,35 +81,34 @@ class WebSocketDialog extends AbstractModel
|
||||
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->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 ?? $dialogUserFun('top_at');
|
||||
$this->bot = 0;
|
||||
switch ($this->type) {
|
||||
case "user":
|
||||
@@ -107,7 +119,8 @@ class WebSocketDialog extends AbstractModel
|
||||
$basic = User::userid2basic($dialog_user->userid);
|
||||
if ($basic) {
|
||||
$this->name = $basic->nickname;
|
||||
$this->bot = $basic->bot;
|
||||
$this->bot = $basic->getBotOwner();
|
||||
$this->quick_msgs = UserBot::quickMsgs($basic->email);
|
||||
} else {
|
||||
$this->name = 'non-existent';
|
||||
$this->dialog_delete = 1;
|
||||
@@ -135,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;
|
||||
}
|
||||
@@ -148,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;
|
||||
}
|
||||
|
||||
@@ -211,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);
|
||||
@@ -258,6 +310,7 @@ class WebSocketDialog extends AbstractModel
|
||||
});
|
||||
$this->delete();
|
||||
});
|
||||
$this->pushMsg("groupDelete");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -265,9 +318,10 @@ class WebSocketDialog extends AbstractModel
|
||||
* 还原会话
|
||||
* @return bool
|
||||
*/
|
||||
public function recoveryDialog()
|
||||
public function restoreDialog()
|
||||
{
|
||||
$this->restore();
|
||||
$this->pushMsg("groupRestore");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -302,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('当前会话禁言');
|
||||
}
|
||||
}
|
||||
@@ -326,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;
|
||||
}
|
||||
}
|
||||
@@ -364,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
|
||||
@@ -434,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
|
||||
],
|
||||
]
|
||||
@@ -558,12 +580,12 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
// 基础处理
|
||||
$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";
|
||||
@@ -594,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))) {
|
||||
@@ -617,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");
|
||||
@@ -637,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";
|
||||
@@ -655,7 +687,7 @@ 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('文件夹不支持分享');
|
||||
}
|
||||
@@ -663,7 +695,7 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
$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('文件分享错误');
|
||||
@@ -672,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);
|
||||
foreach ($matchs[0] as $key => $str) {
|
||||
$herf = $matchs[2][$key];
|
||||
$title = $matchs[3][$key] ?: $herf;
|
||||
preg_match("/\/single\/file\/(.*?)$/i", strip_tags($title), $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;
|
||||
@@ -691,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;
|
||||
@@ -707,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);
|
||||
}
|
||||
|
||||
@@ -743,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';
|
||||
}
|
||||
}
|
||||
@@ -813,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);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* App\Models\WebSocketDialogUser
|
||||
*
|
||||
@@ -15,6 +17,7 @@ namespace App\Models;
|
||||
* @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()
|
||||
@@ -32,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,6 +6,12 @@
|
||||
|
||||
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
|
||||
|
||||
// 判断是否通过cmd命令执行的
|
||||
if (function_exists('info')){
|
||||
echo "Success \n";
|
||||
return;
|
||||
}
|
||||
|
||||
$path = dirname(__FILE__). '/';
|
||||
$lists = scandir($path);
|
||||
//
|
||||
|
||||
@@ -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"];
|
||||
}
|
||||
@@ -917,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
|
||||
@@ -1280,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
|
||||
@@ -1407,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
|
||||
@@ -1462,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 {
|
||||
@@ -1482,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
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1498,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
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1843,111 +1844,6 @@ class Base
|
||||
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
|
||||
@@ -2037,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
|
||||
@@ -2123,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
|
||||
@@ -2171,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
|
||||
@@ -2237,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)
|
||||
@@ -2245,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 = "";
|
||||
@@ -2316,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'])) {
|
||||
@@ -2335,7 +2180,7 @@ class Base
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param array $param [ type=[文件类型], file=>Request::file, path=>文件路径, fileName=>文件名称, scale=>[压缩原图宽,高, 压缩方式], size=>限制大小KB, autoThumb=>false不要自动生成缩略图, chmod=>权限(默认0644) ]
|
||||
* @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)
|
||||
@@ -2352,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'];
|
||||
@@ -2374,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'];
|
||||
@@ -2461,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];
|
||||
@@ -2510,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 {
|
||||
@@ -2567,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);
|
||||
@@ -2661,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);
|
||||
@@ -3108,4 +2955,73 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 多维数组字母转下划线格式
|
||||
* @param $array
|
||||
* @return array
|
||||
*/
|
||||
public static function arrayKeyToUnderline($array)
|
||||
{
|
||||
$newArray = [];
|
||||
foreach ($array as $key => $value) {
|
||||
//如果是数组,递归调用
|
||||
if (is_array($value)) {
|
||||
$value = self::arrayKeyToUnderline($value);
|
||||
}
|
||||
$newKey = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $key));
|
||||
$newArray[$newKey] = $value;
|
||||
}
|
||||
return $newArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* 多维数组字母转驼峰格式
|
||||
*
|
||||
* @param [type] $array
|
||||
* @return array
|
||||
*/
|
||||
public static function arrayKeyToCamel($array)
|
||||
{
|
||||
$newArray = [];
|
||||
foreach ($array as $key => $value) {
|
||||
//如果是数组,递归调用
|
||||
if (is_array($value)) {
|
||||
$value = self::arrayKeyToCamel($value);
|
||||
}
|
||||
$newKey = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $key))));
|
||||
$newArray[$newKey] = $value;
|
||||
}
|
||||
return $newArray;
|
||||
}
|
||||
}
|
||||
|
||||
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,9 +3,13 @@
|
||||
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);
|
||||
|
||||
@@ -46,9 +50,7 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
if ($dialog->type !== 'user') {
|
||||
return;
|
||||
}
|
||||
if ($botUser->email === 'bot-manager@bot.system') {
|
||||
$this->botManagerReceive($msg);
|
||||
}
|
||||
$this->botManagerReceive($msg, $botUser);
|
||||
}
|
||||
|
||||
public function end()
|
||||
@@ -59,25 +61,65 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
/**
|
||||
* 机器人管理处理消息
|
||||
* @param WebSocketDialogMsg $msg
|
||||
* @param User $botUser
|
||||
* @return void
|
||||
*/
|
||||
private function botManagerReceive(WebSocketDialogMsg $msg)
|
||||
private function botManagerReceive(WebSocketDialogMsg $msg, User $botUser)
|
||||
{
|
||||
if ($msg->type === 'text') {
|
||||
$text = trim(strip_tags($msg->msg['text']));
|
||||
if (empty($text)) {
|
||||
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(" ", "{$text} "));
|
||||
//
|
||||
$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.*'])
|
||||
$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)
|
||||
@@ -90,6 +132,18 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
}
|
||||
break;
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
case '/info':
|
||||
$botId = $isManager ? $array[1] : $botUser->userid;
|
||||
$data = $this->botManagerOne($botId, $msg->userid);
|
||||
if (!$data) {
|
||||
$type = "notice";
|
||||
$notice = "机器人不存在。";
|
||||
}
|
||||
break;
|
||||
|
||||
/**
|
||||
* 创建
|
||||
*/
|
||||
@@ -116,9 +170,9 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
$notice = "创建失败。";
|
||||
break;
|
||||
}
|
||||
$dialog = WebSocketDialog::checkUserDialog($data->userid, $msg->userid);
|
||||
$dialog = WebSocketDialog::checkUserDialog($data, $msg->userid);
|
||||
if ($dialog) {
|
||||
$text = "你好,我是你的机器人:{$data->nickname}, 我的机器人ID是:{$data->userid}";
|
||||
$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;
|
||||
@@ -127,16 +181,18 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
* 修改名字
|
||||
*/
|
||||
case '/setname':
|
||||
if (strlen($array[2]) < 2 || strlen($array[2]) > 20) {
|
||||
$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($array[1], $msg->userid);
|
||||
$data = $this->botManagerOne($botId, $msg->userid);
|
||||
if ($data) {
|
||||
$data->nickname = $array[2];
|
||||
$data->az = Base::getFirstCharter($array[2]);
|
||||
$data->pinyin = Base::cn2pinyin($array[2]);
|
||||
$data->nickname = $nameString;
|
||||
$data->az = Base::getFirstCharter($nameString);
|
||||
$data->pinyin = Base::cn2pinyin($nameString);
|
||||
$data->save();
|
||||
} else {
|
||||
$type = "notice";
|
||||
@@ -149,7 +205,8 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
* 删除
|
||||
*/
|
||||
case '/deletebot':
|
||||
$data = $this->botManagerOne($array[1], $msg->userid);
|
||||
$botId = $isManager ? $array[1] : $botUser->userid;
|
||||
$data = $this->botManagerOne($botId, $msg->userid);
|
||||
if ($data) {
|
||||
$data->deleteUser('delete bot');
|
||||
} else {
|
||||
@@ -162,9 +219,10 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
* 获取Token
|
||||
*/
|
||||
case '/token':
|
||||
$data = $this->botManagerOne($array[1], $msg->userid);
|
||||
$botId = $isManager ? $array[1] : $botUser->userid;
|
||||
$data = $this->botManagerOne($botId, $msg->userid);
|
||||
if ($data) {
|
||||
User::token($data);
|
||||
User::generateToken($data);
|
||||
} else {
|
||||
$type = "notice";
|
||||
$notice = "机器人不存在。";
|
||||
@@ -175,10 +233,11 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
* 更新Token
|
||||
*/
|
||||
case '/revoke':
|
||||
$data = $this->botManagerOne($array[1], $msg->userid);
|
||||
$botId = $isManager ? $array[1] : $botUser->userid;
|
||||
$data = $this->botManagerOne($botId, $msg->userid);
|
||||
if ($data) {
|
||||
$data->encrypt = Base::generatePassword(6);
|
||||
$data->password = Base::md52(Base::generatePassword(32), $data->encrypt);
|
||||
$data->password = Doo::md5s(Base::generatePassword(32), $data->encrypt);
|
||||
$data->save();
|
||||
} else {
|
||||
$type = "notice";
|
||||
@@ -186,15 +245,64 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
}
|
||||
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':
|
||||
$data = $this->botManagerOne($array[1], $msg->userid);
|
||||
$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'])
|
||||
$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', "%{$array[2]}%")
|
||||
->where('web_socket_dialogs.name', 'LIKE', "%{$nameKey}%")
|
||||
->where('u.userid', $data->userid)
|
||||
->orderByDesc('u.top_at')
|
||||
->orderByDesc('web_socket_dialogs.last_at')
|
||||
@@ -207,7 +315,7 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
$list->transform(function (WebSocketDialog $item) use ($data) {
|
||||
return $item->formatData($data->userid);
|
||||
});
|
||||
$data->list = $list;
|
||||
$data->list = $list; // 这个参数只是作为输出,所以不保存
|
||||
}
|
||||
} else {
|
||||
$type = "notice";
|
||||
@@ -220,11 +328,33 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
'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], $this->userid, false, false, true); // todo 未能在任务end事件来发送任务
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +368,13 @@ class BotReceiveMsgTask extends AbstractTask
|
||||
$botId = intval($botId);
|
||||
$userid = intval($userid);
|
||||
if ($botId > 0) {
|
||||
return User::select(['users.*'])
|
||||
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)
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -63,11 +70,32 @@ class DeleteTmpTask extends AbstractTask
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,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,25 @@ 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') {
|
||||
if (is_array($msg) && $msg['type'] == 'dialog') {
|
||||
// 仅加密对话消息
|
||||
$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;
|
||||
|
||||
@@ -84,7 +85,12 @@ class WebSocketDialogMsgTask extends AbstractTask
|
||||
if (empty($dialog)) {
|
||||
return;
|
||||
}
|
||||
$silences = $dialog->dialogUser->pluck('silence', 'userid')->toArray();
|
||||
$updateds = [];
|
||||
$silences = [];
|
||||
foreach ($dialog->dialogUser as $dialogUser) {
|
||||
$updateds[$dialogUser->userid] = $dialogUser->updated_at;
|
||||
$silences[$dialogUser->userid] = $dialogUser->silence;
|
||||
}
|
||||
$userids = array_keys($silences);
|
||||
|
||||
// 提及会员
|
||||
@@ -111,14 +117,17 @@ 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] = [
|
||||
'userid' => $userid,
|
||||
'silence' => $silence,
|
||||
'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,
|
||||
@@ -128,8 +137,9 @@ class WebSocketDialogMsgTask extends AbstractTask
|
||||
])->saveOrIgnore();
|
||||
$array[$userid] = [
|
||||
'userid' => $userid,
|
||||
'silence' => $silence,
|
||||
'mention' => $mention,
|
||||
'silence' => $silence,
|
||||
'updated' => $updated,
|
||||
];
|
||||
// 机器人收到消处理
|
||||
$botUser = User::whereUserid($userid)->whereBot(1)->first();
|
||||
@@ -153,6 +163,8 @@ class WebSocketDialogMsgTask extends AbstractTask
|
||||
'silence' => $item['silence'] ? 1 : 0,
|
||||
'data' => array_merge($msg->toArray(), [
|
||||
'mention' => $item['mention'],
|
||||
'user_at' => Carbon::parse($item['updated'])->toDateTimeString('millisecond'),
|
||||
'user_ms' => Carbon::parse($item['updated'])->valueOf(),
|
||||
]),
|
||||
]
|
||||
];
|
||||
@@ -196,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"
|
||||
|
||||
100
cmd
100
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,19 +317,21 @@ 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
|
||||
@@ -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,7 +13,9 @@
|
||||
"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",
|
||||
@@ -79,5 +81,7 @@
|
||||
"sort-packages": true
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
"prefer-stable": true,
|
||||
"repositories": {
|
||||
}
|
||||
}
|
||||
|
||||
1250
composer.lock
generated
1250
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,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -14,16 +14,11 @@ class AddUsersBot extends Migration
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$isAdd = false;
|
||||
Schema::table('users', function (Blueprint $table) use (&$isAdd) {
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('users', 'bot')) {
|
||||
$isAdd = true;
|
||||
$table->tinyInteger('bot')->nullable()->default(0)->after('email_verity')->comment('是否机器人');
|
||||
}
|
||||
});
|
||||
if ($isAdd && User::count() > 0) {
|
||||
User::botGetOrCreate('bot-manager');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\WebSocketDialog;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class GenerateWebSocketDialogsAllGroup extends Migration
|
||||
class GenerateWebSocketDialogsDefaultGroup extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
@@ -14,7 +15,16 @@ class GenerateWebSocketDialogsAllGroup extends Migration
|
||||
public function up()
|
||||
{
|
||||
if (WebSocketDialog::count() > 0 && !WebSocketDialog::whereGroupType('all')->exists()) {
|
||||
$userids = User::whereNull('disable_at')->pluck('userid')->toArray();
|
||||
$botUser = User::botGetOrCreate('bot-manager');
|
||||
if ($botUser) {
|
||||
$dialog = WebSocketDialog::checkUserDialog($botUser, 1);
|
||||
if ($dialog) {
|
||||
$dialog->last_at = Carbon::now();
|
||||
$dialog->save();
|
||||
}
|
||||
}
|
||||
|
||||
$userids = User::whereBot(0)->whereNull('disable_at')->pluck('userid')->toArray();
|
||||
WebSocketDialog::createGroup("全体成员 All members", $userids, 'all');
|
||||
}
|
||||
}
|
||||
26
database/migrations/2023_04_01_164418_update_file_pshare.php
Normal file
26
database/migrations/2023_04_01_164418_update_file_pshare.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class UpdateFilePshare extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
\App\Models\File::whereShare(1)->wherePshare(0)->update(['pshare' => DB::raw('id')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateApproveProcMsgs extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (!Schema::hasTable('approve_proc_msgs')) {
|
||||
Schema::create('approve_proc_msgs', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('proc_inst_id')->nullable()->default(0)->comment('流程实例ID');
|
||||
$table->bigInteger('userid')->nullable()->default(0)->comment('会员ID');
|
||||
$table->bigInteger('msg_id')->nullable()->default(0)->comment('消息ID');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('approve_proc_msgs', function (Blueprint $table) {
|
||||
Schema::dropIfExists('approve_proc_msgs');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class UsersTableSeeder extends Seeder
|
||||
@@ -71,8 +70,5 @@ class UsersTableSeeder extends Seeder
|
||||
'updated_at' => seeders_at('2021-07-01 16:58:00'),
|
||||
),
|
||||
));
|
||||
|
||||
User::botGetOrCreate('bot-manager');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\WebSocketDialog;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class WebSocketDialogsTableSeeder extends Seeder
|
||||
@@ -223,7 +224,16 @@ class WebSocketDialogsTableSeeder extends Seeder
|
||||
),
|
||||
));
|
||||
|
||||
$userids = User::whereNull('disable_at')->pluck('userid')->toArray();
|
||||
$botUser = User::botGetOrCreate('bot-manager');
|
||||
if ($botUser) {
|
||||
$dialog = WebSocketDialog::checkUserDialog($botUser, 1);
|
||||
if ($dialog) {
|
||||
$dialog->last_at = Carbon::now();
|
||||
$dialog->save();
|
||||
}
|
||||
}
|
||||
|
||||
$userids = User::whereBot(0)->whereNull('disable_at')->pluck('userid')->toArray();
|
||||
WebSocketDialog::createGroup("全体成员 All members", $userids, 'all');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,12 @@ version: '3'
|
||||
services:
|
||||
php:
|
||||
container_name: "dootask-php-${APP_ID}"
|
||||
image: "kuaifan/php:swoole-8.0"
|
||||
shm_size: "1024m"
|
||||
image: "kuaifan/php:swoole-8.0.rc9"
|
||||
shm_size: "2gb"
|
||||
ulimits:
|
||||
core:
|
||||
soft: 0
|
||||
hard: 0
|
||||
volumes:
|
||||
- ./docker/crontab/crontab.conf:/etc/supervisor/conf.d/crontab.conf
|
||||
- ./docker/php/php.conf:/etc/supervisor/conf.d/php.conf
|
||||
@@ -12,7 +16,6 @@ services:
|
||||
- ./docker/log/supervisor:/var/log/supervisor
|
||||
- ./:/var/www
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
LANG: "C.UTF-8"
|
||||
MODE: "production"
|
||||
MYSQL_HOST: "${DB_HOST}"
|
||||
@@ -36,8 +39,6 @@ services:
|
||||
volumes:
|
||||
- ./docker/nginx:/etc/nginx/conf.d
|
||||
- ./public:/var/www/public
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: "${APP_IPPR}.3"
|
||||
@@ -47,13 +48,12 @@ services:
|
||||
- fileview
|
||||
- drawio-webapp
|
||||
- drawio-export
|
||||
- minder
|
||||
restart: unless-stopped
|
||||
|
||||
redis:
|
||||
container_name: "dootask-redis-${APP_ID}"
|
||||
image: "redis:alpine"
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: "${APP_IPPR}.4"
|
||||
@@ -62,14 +62,13 @@ services:
|
||||
mariadb:
|
||||
container_name: "dootask-mariadb-${APP_ID}"
|
||||
image: "mariadb:10.7.3"
|
||||
ports: # mysql ports item
|
||||
- "33062:3306" # mysql ports value
|
||||
ports:
|
||||
- "${DB_OPEN_PORT:-}:3306"
|
||||
volumes:
|
||||
- ./docker/mysql/repassword.sh:/etc/mysql/repassword.sh
|
||||
- ./docker/mysql/conf.d:/etc/mysql/conf.d
|
||||
- ./docker/mysql/data:/var/lib/mysql
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
MYSQL_PREFIX: "${DB_PREFIX}"
|
||||
MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASSWORD}"
|
||||
MYSQL_DATABASE: "${DB_DATABASE}"
|
||||
@@ -82,11 +81,11 @@ services:
|
||||
|
||||
office:
|
||||
container_name: "dootask-office-${APP_ID}"
|
||||
image: "onlyoffice/documentserver:7.2.2.56"
|
||||
image: "onlyoffice/documentserver:7.3.2.8"
|
||||
volumes:
|
||||
- ./docker/office/logs:/var/log/onlyoffice
|
||||
- ./docker/office/data:/var/www/onlyoffice/Data
|
||||
- ./docker/office/resources/api/documents/api.js:/var/www/onlyoffice/documentserver/web-apps/apps/api/documents/api.js
|
||||
- ./docker/office/resources/require.js:/var/www/onlyoffice/documentserver/web-apps/vendor/requirejs/require.js
|
||||
- ./docker/office/resources/documenteditor/main/resources/css/app.css:/var/www/onlyoffice/documentserver/web-apps/apps/documenteditor/main/resources/css/app.css
|
||||
- ./docker/office/resources/documenteditor/mobile/css/964.css:/var/www/onlyoffice/documentserver/web-apps/apps/documenteditor/mobile/css/964.css
|
||||
- ./docker/office/resources/presentationeditor/main/resources/css/app.css:/var/www/onlyoffice/documentserver/web-apps/apps/presentationeditor/main/resources/css/app.css
|
||||
@@ -94,7 +93,6 @@ services:
|
||||
- ./docker/office/resources/spreadsheeteditor/main/resources/css/app.css:/var/www/onlyoffice/documentserver/web-apps/apps/spreadsheeteditor/main/resources/css/app.css
|
||||
- ./docker/office/resources/spreadsheeteditor/mobile/css/887.css:/var/www/onlyoffice/documentserver/web-apps/apps/spreadsheeteditor/mobile/css/887.css
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
JWT_ENABLED: false
|
||||
networks:
|
||||
extnetwork:
|
||||
@@ -103,9 +101,9 @@ services:
|
||||
|
||||
fileview:
|
||||
container_name: "dootask-fileview-${APP_ID}"
|
||||
image: "kuaifan/fileview:4.1.0-SNAPSHOT-RC18"
|
||||
image: "kuaifan/fileview:4.1.0-SNAPSHOT-RC20"
|
||||
platform: linux/amd64
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
KK_CONTEXT_PATH: "/fileview"
|
||||
KK_OFFICE_PREVIEW_SWITCH_DISABLED: true
|
||||
KK_FILE_UPLOAD_ENABLED: true
|
||||
@@ -117,7 +115,7 @@ services:
|
||||
|
||||
drawio-webapp:
|
||||
container_name: "dootask-drawio-webapp-${APP_ID}"
|
||||
image: "jgraph/drawio:16.6.1"
|
||||
image: "jgraph/drawio:20.8.20"
|
||||
volumes:
|
||||
- ./docker/drawio/webapp/index.html:/usr/local/tomcat/webapps/draw/index.html
|
||||
- ./docker/drawio/webapp/stencils:/usr/local/tomcat/webapps/draw/stencils
|
||||
@@ -127,24 +125,47 @@ services:
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: "${APP_IPPR}.8"
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
depends_on:
|
||||
- drawio-export
|
||||
restart: unless-stopped
|
||||
|
||||
drawio-export:
|
||||
container_name: "dootask-drawio-export-${APP_ID}"
|
||||
image: "jgraph/export-server"
|
||||
image: "kuaifan/export-server:latest"
|
||||
platform: linux/amd64
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: "${APP_IPPR}.9"
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
volumes:
|
||||
- ./docker/drawio/export/fonts:/usr/share/fonts/drawio
|
||||
restart: unless-stopped
|
||||
|
||||
minder:
|
||||
container_name: "dootask-minder-${APP_ID}"
|
||||
image: "kuaifan/minder:0.1.1"
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: "${APP_IPPR}.10"
|
||||
restart: unless-stopped
|
||||
|
||||
approve:
|
||||
container_name: "dootask-approve-${APP_ID}"
|
||||
image: "hitosea2020/go-approve:latest"
|
||||
environment:
|
||||
TZ: "Asia/Shanghai"
|
||||
MYSQL_HOST: "${DB_HOST}"
|
||||
MYSQL_PORT: "${DB_PORT}"
|
||||
MYSQL_DBNAME: "${DB_DATABASE}"
|
||||
MYSQL_USERNAME: "${DB_USERNAME}"
|
||||
MYSQL_PASSWORD: "${DB_PASSWORD}"
|
||||
MYSQL_Prefix: "${DB_PREFIX}approve_"
|
||||
networks:
|
||||
extnetwork:
|
||||
ipv4_address: "${APP_IPPR}.11"
|
||||
depends_on:
|
||||
- mariadb
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
extnetwork:
|
||||
name: "dootask-networks-${APP_ID}"
|
||||
|
||||
@@ -1,24 +1,3 @@
|
||||
# Change
|
||||
|
||||
## js/diagramly/ElectronApp.js
|
||||
|
||||
- 隐藏文件中的无用菜单
|
||||
|
||||
## js/app.min.js
|
||||
|
||||
- 隐藏帮助菜单
|
||||
- 取消未保存关闭窗口提示
|
||||
- `EmbedFile.prototype.getTitle=...` 改 `EmbedFile.prototype.getTitle=function(){return this.desc.title||(urlParams.title?decodeURIComponent(urlParams.title):"")}`
|
||||
- `c.insertTemplateEnabled&&!c.isOffline()&&this.addMenuItems(b,["insertTemplate"],d)` 改 `c.insertTemplateEnabled&&this.addMenuItems(b,["insertTemplate"],d)`
|
||||
- `390:270` 改 `390:285`
|
||||
- `!this.editorUi.isOffline()&&d.length<=c/4?(f=b-Math.ceil((e-c/4)/c),mxUtils.get(ICONSEARCH_PATH` 改 `d.length<=c/4?(f=b-Math.ceil((e-c/4)/c),mxUtils.get(ICONSEARCH_PATH`
|
||||
|
||||
## index.html
|
||||
|
||||
- 隐藏加载中的提示
|
||||
- 加入
|
||||
```js
|
||||
window.EXPORT_URL = window.location.origin + "/drawio/export/";
|
||||
window.DRAWIO_LIGHTBOX_URL = window.location.origin + "/drawio/webapp";
|
||||
setInterval(function() {window.ICONSEARCH_PATH = window.location.origin + "/drawio/iconsearch";}, 1000)
|
||||
```
|
||||
diff https://github.com/jgraph/drawio/tree/1a858166fb4f1330cf23e58941e3fbec1dcd16f8
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<title>Flowchart Maker & Online Diagram Software</title>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="Description" content="diagrams.net is free online diagram software for making flowcharts, process diagrams, org charts, UML, ER and network diagrams">
|
||||
<meta name="Keywords" content="diagram, online, flow chart, flowchart maker, uml, erd">
|
||||
<meta itemprop="name" content="diagrams.net - free flowchart maker and diagrams online">
|
||||
<meta itemprop="description" content="diagrams.net is a free online diagramming application and flowchart maker . You can use it to create UML, entity relationship,
|
||||
<meta name="Description" content="draw.io is free online diagram software for making flowcharts, process diagrams, org charts, UML, ER and network diagrams">
|
||||
<meta name="Keywords" content="drawio, diagram, online, flow chart, flowchart maker, uml, erd">
|
||||
<meta itemprop="name" content="draw.io - free flowchart maker and diagrams online">
|
||||
<meta itemprop="description" content="draw.io is a free online diagramming application and flowchart maker . You can use it to create UML, entity relationship,
|
||||
org charts, BPMN and BPM, database schema and networks. Also possible are telecommunication network, workflow, flowcharts, maps overlays and GIS, electronic
|
||||
circuit and social network diagrams.">
|
||||
<meta itemprop="image" content="https://lh4.googleusercontent.com/-cLKEldMbT_E/Tx8qXDuw6eI/AAAAAAAAAAs/Ke0pnlk8Gpg/w500-h344-k/BPMN%2Bdiagram%2Brc2f.png">
|
||||
@@ -17,12 +17,11 @@
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="theme-color" content="#d89000">
|
||||
<script type="text/javascript">
|
||||
window.SERVER_HOME_URL = window.location.origin + "/"
|
||||
window.EXPORT_URL = window.SERVER_HOME_URL + "drawio/export/";
|
||||
window.DRAWIO_LIGHTBOX_URL = window.SERVER_HOME_URL + "drawio/webapp";
|
||||
setInterval(function() {window.ICONSEARCH_PATH = window.SERVER_HOME_URL + "drawio/iconsearch";}, 1000)
|
||||
window.EXPORT_URL = window.location.origin + "/drawio/export/";
|
||||
window.DRAWIO_LIGHTBOX_URL = window.location.origin + "/drawio/webapp";
|
||||
setInterval(function() {window.ICONSEARCH_PATH = window.location.origin + "/drawio/iconsearch";}, 1000)
|
||||
|
||||
/**
|
||||
/**
|
||||
* URL Parameters and protocol description are here:
|
||||
*
|
||||
* https://desk.draw.io/support/solutions/articles/16000042546-what-url-parameters-are-supported
|
||||
@@ -48,7 +47,7 @@
|
||||
|
||||
for (var i = 0; i < params.length; i++)
|
||||
{
|
||||
idx = params[i].indexOf('=');
|
||||
var idx = params[i].indexOf('=');
|
||||
|
||||
if (idx > 0)
|
||||
{
|
||||
@@ -143,7 +142,7 @@
|
||||
/**
|
||||
* Synchronously adds scripts to the page.
|
||||
*/
|
||||
function mxscript(src, onLoad, id, dataAppKey, noWrite)
|
||||
function mxscript(src, onLoad, id, dataAppKey, noWrite, onError)
|
||||
{
|
||||
var defer = onLoad == null && !noWrite;
|
||||
|
||||
@@ -179,6 +178,14 @@
|
||||
};
|
||||
}
|
||||
|
||||
if (onError != null)
|
||||
{
|
||||
s.onerror = function(e)
|
||||
{
|
||||
onError('Failed to load ' + src, e);
|
||||
};
|
||||
}
|
||||
|
||||
var t = document.getElementsByTagName('script')[0];
|
||||
|
||||
if (t != null)
|
||||
@@ -267,6 +274,8 @@
|
||||
mxForceIncludes = true;
|
||||
}
|
||||
|
||||
mxForceIncludes = false;
|
||||
|
||||
mxscript(drawDevUrl + 'js/PreConfig.js');
|
||||
mxscript(drawDevUrl + 'js/diagramly/Init.js');
|
||||
mxscript(geBasePath + '/Init.js');
|
||||
@@ -353,11 +362,14 @@
|
||||
};
|
||||
</script>
|
||||
<link rel="chrome-webstore-item" href="https://chrome.google.com/webstore/detail/plgmlhohecdddhbmmkncjdmlhcmaachm">
|
||||
<link rel="stylesheet" type="text/css" href="js/croppie/croppie.min.css">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="images/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="images/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="images/favicon-16x16.png">
|
||||
<link rel="mask-icon" href="images/safari-pinned-tab.svg" color="#d89000">
|
||||
<link rel="stylesheet" type="text/css" href="styles/grapheditor.css">
|
||||
<link rel="preconnect" href="https://storage.googleapis.com">
|
||||
<link rel="canonical" href="https://app.diagrams.net">
|
||||
<link rel="manifest" href="images/manifest.json">
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
<style type="text/css">
|
||||
body { overflow:hidden; }
|
||||
div.picker { z-index: 10007; }
|
||||
@@ -380,8 +392,8 @@
|
||||
padding-top:0px;
|
||||
}
|
||||
.geEditor *:not(.geScrollable)::-webkit-scrollbar {
|
||||
width:14px;
|
||||
height:14px;
|
||||
width:10px;
|
||||
height:10px;
|
||||
}
|
||||
.geEditor ::-webkit-scrollbar-track {
|
||||
background-clip:padding-box;
|
||||
@@ -411,9 +423,6 @@
|
||||
cursor:pointer;
|
||||
margin:5px;
|
||||
}
|
||||
.geDialog h2 {
|
||||
line-height: 1.2;
|
||||
}
|
||||
</style>
|
||||
<!-- Workaround for binary XHR in IE 9/10, see App.loadUrl -->
|
||||
<!--[if (IE 9)|(IE 10)]><!-->
|
||||
@@ -434,7 +443,7 @@
|
||||
<div class="geBlock">
|
||||
<h1>Flowchart Maker and Online Diagram Software</h1>
|
||||
<p>
|
||||
diagrams.net (formerly draw.io) is free online diagram software. You can use it as a flowchart maker, network diagram software, to create UML online, as an ER diagram tool,
|
||||
draw.io is free online diagram software. You can use it as a flowchart maker, network diagram software, to create UML online, as an ER diagram tool,
|
||||
to design database schema, to build BPMN online, as a circuit diagram maker, and more. draw.io can import .vsdx, Gliffy™ and Lucidchart™ files .
|
||||
</p>
|
||||
<h2 id="geStatus">Loading...</h2>
|
||||
@@ -447,32 +456,17 @@
|
||||
/**
|
||||
* Main
|
||||
*/
|
||||
if (navigator.userAgent != null && navigator.userAgent.toLowerCase().
|
||||
indexOf(' electron/') >= 0 && typeof process !== 'undefined' && process.versions.electron < 5)
|
||||
if (urlParams['dev'] != '1' && typeof document.createElement('canvas').getContext === "function")
|
||||
{
|
||||
// Redirects old Electron app to latest version
|
||||
var div = document.getElementById('geInfo');
|
||||
|
||||
if (div != null)
|
||||
window.addEventListener('load', function()
|
||||
{
|
||||
div.innerHTML = '<center><h2>You are using an out of date version of this app.<br>Please download the latest version ' +
|
||||
'<a href="https://github.com/jgraph/drawio-desktop/releases/latest" target="_blank">here</a>.</h2></center>';
|
||||
}
|
||||
mxWinLoaded = true;
|
||||
checkAllLoaded();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if (urlParams['dev'] != '1' && typeof document.createElement('canvas').getContext === "function")
|
||||
{
|
||||
window.addEventListener('load', function()
|
||||
{
|
||||
mxWinLoaded = true;
|
||||
checkAllLoaded();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
App.main();
|
||||
}
|
||||
App.main();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
16669
docker/drawio/webapp/js/app.min.js
vendored
16669
docker/drawio/webapp/js/app.min.js
vendored
File diff suppressed because one or more lines are too long
753
docker/drawio/webapp/js/diagramly/ElectronApp.js
vendored
753
docker/drawio/webapp/js/diagramly/ElectronApp.js
vendored
@@ -1,9 +1,9 @@
|
||||
window.PLUGINS_BASE_PATH = '.';
|
||||
window.TEMPLATE_PATH = 'templates';
|
||||
window.DRAW_MATH_URL = 'math';
|
||||
window.DRAW_MATH_URL = 'math/es5';
|
||||
window.DRAWIO_BASE_URL = '.'; //Prevent access to online website since it is not allowed
|
||||
FeedbackDialog.feedbackUrl = 'https://log.draw.io/email';
|
||||
|
||||
EditorUi.draftSaveDelay = 5000;
|
||||
//Disables eval for JS (uses shapes-14-6-5.min.js)
|
||||
mxStencilRegistry.allowEval = false;
|
||||
|
||||
@@ -60,10 +60,18 @@ mxStencilRegistry.allowEval = false;
|
||||
if (file)
|
||||
{
|
||||
file.updateFileData();
|
||||
xml = file.getData();
|
||||
xml = editorUi.getFileData(true, null, null, null, null, false,
|
||||
null, null, null, false, true);
|
||||
title = file.title;
|
||||
}
|
||||
|
||||
var extras = {globalVars: editorUi.editor.graph.getExportVariables()};
|
||||
|
||||
if (Graph.translateDiagram)
|
||||
{
|
||||
extras.diagramLanguage = Graph.diagramLanguage;
|
||||
}
|
||||
|
||||
new mxElectronRequest('export', {
|
||||
print: true,
|
||||
format: 'pdf',
|
||||
@@ -78,6 +86,7 @@ mxStencilRegistry.allowEval = false;
|
||||
sheetsAcross: sheetsAcross,
|
||||
sheetsDown: sheetsDown,
|
||||
scale: zoom,
|
||||
extras: JSON.stringify(extras),
|
||||
fileTitle: title
|
||||
}).send(function(){}, function(){});
|
||||
};
|
||||
@@ -85,13 +94,15 @@ mxStencilRegistry.allowEval = false;
|
||||
var oldWindowOpen = window.open;
|
||||
window.open = async function(url)
|
||||
{
|
||||
if (url != null && url.startsWith('http'))
|
||||
// Only open a native electron window when url is empty. We use this in our code in several places.
|
||||
if (url == null)
|
||||
{
|
||||
await requestSync({action: 'openExternal', url: url});
|
||||
return oldWindowOpen(url);
|
||||
}
|
||||
else
|
||||
{
|
||||
return oldWindowOpen(url);
|
||||
// Open external will filter urls based on their protocol
|
||||
await requestSync({action: 'openExternal', url: url});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,53 +110,93 @@ mxStencilRegistry.allowEval = false;
|
||||
|
||||
App.main = async function()
|
||||
{
|
||||
// Set AutoSave delay
|
||||
var draftSaveDelay = mxSettings.getDraftSaveDelay();
|
||||
|
||||
if (draftSaveDelay != null)
|
||||
{
|
||||
EditorUi.draftSaveDelay = draftSaveDelay * 1000;
|
||||
EditorUi.enableDrafts = draftSaveDelay > 0;
|
||||
}
|
||||
|
||||
//Load desktop plugins
|
||||
var plugins = (mxSettings.settings != null) ? mxSettings.getPlugins() : null;
|
||||
App.initPluginCallback();
|
||||
|
||||
if (plugins != null && plugins.length > 0)
|
||||
{
|
||||
for (var i = 0; i < plugins.length; i++)
|
||||
// Workaround for body not defined if plugins are used in dev mode
|
||||
if (urlParams['dev'] == '1')
|
||||
{
|
||||
try
|
||||
EditorUi.debug('App.main', 'Skipped plugins', plugins);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < plugins.length; i++)
|
||||
{
|
||||
if (plugins[i].startsWith('/plugins/'))
|
||||
try
|
||||
{
|
||||
plugins[i] = '.' + plugins[i];
|
||||
if (plugins[i].indexOf('..') >= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (plugins[i].startsWith('/plugins/'))
|
||||
{
|
||||
plugins[i] = '.' + plugins[i];
|
||||
}
|
||||
else if (plugins[i].startsWith('plugins/'))
|
||||
{
|
||||
plugins[i] = './' + plugins[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
let pluginFile = await requestSync({
|
||||
action: 'getPluginFile',
|
||||
plugin: plugins[i]
|
||||
});
|
||||
|
||||
if (pluginFile != null)
|
||||
{
|
||||
plugins[i] = 'file://' + pluginFile;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue; //skip not found files
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
mxscript(plugins[i]);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
else if (plugins[i].startsWith('plugins/'))
|
||||
catch (e)
|
||||
{
|
||||
plugins[i] = './' + plugins[i];
|
||||
// ignore
|
||||
}
|
||||
//Support old plugins added using file:// workaround
|
||||
else if (!plugins[i].startsWith('file://'))
|
||||
{
|
||||
let appFolder = await requestSync('getAppDataFolder');
|
||||
|
||||
let pluginsFile = await requestSync({
|
||||
action: 'checkFileExists',
|
||||
pathParts: [appFolder, '/plugins', plugins[i]]
|
||||
});
|
||||
|
||||
if (pluginsFile.exists)
|
||||
{
|
||||
plugins[i] = 'file://' + pluginsFile.path;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue; //skip not found files
|
||||
}
|
||||
}
|
||||
|
||||
mxscript(plugins[i]);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Remove old relaxed CSP and add strict one
|
||||
// var allMeta = document.getElementsByTagName('meta');
|
||||
//
|
||||
// for (var i = 0; i < allMeta.length; i++)
|
||||
// {
|
||||
// if (allMeta[i].getAttribute('http-equiv') == 'Content-Security-Policy')
|
||||
// {
|
||||
// allMeta[i].parentNode.removeChild(allMeta[i]);
|
||||
// }
|
||||
//
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// mxmeta(null, 'default-src \'self\'; connect-src \'self\' https://fonts.googleapis.com https://fonts.gstatic.com; img-src * data:; media-src *; font-src *; style-src \'self\' \'unsafe-inline\' https://fonts.googleapis.com', 'Content-Security-Policy');
|
||||
|
||||
//Disable web plugins loading
|
||||
urlParams['plugins'] = '0';
|
||||
origAppMain.apply(this, arguments);
|
||||
@@ -208,8 +259,8 @@ mxStencilRegistry.allowEval = false;
|
||||
// Replaces file menu to replace openFrom menu with open and rename downloadAs to export
|
||||
this.put('file', new Menu(mxUtils.bind(this, function(menu, parent)
|
||||
{
|
||||
this.addMenuItems(menu, ['import'], parent);
|
||||
this.addSubmenu('exportAs', menu, parent);
|
||||
this.addMenuItems(menu, ['import'], parent);
|
||||
this.addSubmenu('exportAs', menu, parent);
|
||||
menu.addSeparator(parent);
|
||||
this.addSubmenu('embed', menu, parent);
|
||||
menu.addSeparator(parent);
|
||||
@@ -229,7 +280,7 @@ mxStencilRegistry.allowEval = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.addMenuItems(menu, ['-', 'pageSetup', 'print'], parent);
|
||||
this.addMenuItems(menu, ['-', 'pageSetup', 'print'], parent);
|
||||
// LATER: Find API for application.quit
|
||||
})));
|
||||
};
|
||||
@@ -267,25 +318,25 @@ mxStencilRegistry.allowEval = false;
|
||||
var editorUi = this;
|
||||
var graph = this.editor.graph;
|
||||
|
||||
window.__emt_isModified = function()
|
||||
electron.registerMsgListener('isModified', () =>
|
||||
{
|
||||
if (editorUi.getCurrentFile())
|
||||
const currentFile = editorUi.getCurrentFile();
|
||||
let reply = {isModified: false, draftPath: null};
|
||||
|
||||
if (currentFile != null)
|
||||
{
|
||||
return editorUi.getCurrentFile().isModified()
|
||||
reply.isModified = currentFile.isModified();
|
||||
reply.draftPath = EditorUi.enableDrafts && currentFile.fileObject? currentFile.fileObject.draftFileName : null;
|
||||
}
|
||||
|
||||
return false
|
||||
};
|
||||
electron.sendMessage('isModified-result', reply);
|
||||
});
|
||||
|
||||
window.__emt_removeDraft = function()
|
||||
electron.registerMsgListener('removeDraft', () =>
|
||||
{
|
||||
var currentFile = editorUi.getCurrentFile();
|
||||
|
||||
if (currentFile != null && EditorUi.enableDrafts)
|
||||
{
|
||||
currentFile.removeDraft();
|
||||
}
|
||||
};
|
||||
editorUi.getCurrentFile().removeDraft();
|
||||
electron.sendMessage('draftRemoved', {});
|
||||
});
|
||||
|
||||
// Adds support for libraries
|
||||
this.actions.addAction('newLibrary...', mxUtils.bind(this, function()
|
||||
@@ -588,7 +639,19 @@ mxStencilRegistry.allowEval = false;
|
||||
|
||||
editorUi.actions.addAction('plugins...', function()
|
||||
{
|
||||
editorUi.showDialog(new PluginsDialog(editorUi, function(callback)
|
||||
var pluginsMap = {};
|
||||
//Initialize it with plugins in settings
|
||||
var plugins = (mxSettings.settings != null) ? mxSettings.getPlugins() : null;
|
||||
|
||||
if (plugins != null)
|
||||
{
|
||||
for (var i = 0; i < plugins.length; i++)
|
||||
{
|
||||
pluginsMap[plugins[i]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
editorUi.showDialog(new PluginsDialog(editorUi, async function(callback)
|
||||
{
|
||||
var div = document.createElement('div');
|
||||
|
||||
@@ -602,9 +665,13 @@ mxStencilRegistry.allowEval = false;
|
||||
|
||||
for (var i = 0; i < App.publicPlugin.length; i++)
|
||||
{
|
||||
var p = App.publicPlugin[i];
|
||||
|
||||
if (pluginsMap[App.pluginRegistry[p]]) continue;
|
||||
|
||||
var option = document.createElement('option');
|
||||
mxUtils.write(option, App.publicPlugin[i]);
|
||||
option.value = App.publicPlugin[i];
|
||||
mxUtils.write(option, p);
|
||||
option.value = p;
|
||||
pluginsSelect.appendChild(option);
|
||||
}
|
||||
|
||||
@@ -616,63 +683,89 @@ mxStencilRegistry.allowEval = false;
|
||||
mxUtils.write(title, mxResources.get('extPlugins') + ': ');
|
||||
div.appendChild(title);
|
||||
|
||||
var extPluginsBtn = mxUtils.button(mxResources.get('selectFile') + '...', async function()
|
||||
if (await requestSync('isPluginsEnabled'))
|
||||
{
|
||||
var lastDir = localStorage.getItem('.lastPluginDir');
|
||||
var extPluginsBtn = mxUtils.button(mxResources.get('selectFile') + '...', async function()
|
||||
{
|
||||
var warningMsgs = mxResources.get('pluginWarning').split('\\n');
|
||||
var warningMsg = warningMsgs.pop(); //Last line in the message
|
||||
|
||||
var paths = await requestSync({
|
||||
action: 'showOpenDialog',
|
||||
defaultPath: lastDir || (await requestSync('getDocumentsFolder')),
|
||||
filters: [
|
||||
{ name: 'draw.io Plugins', extensions: ['js'] },
|
||||
{ name: 'All Files', extensions: ['*'] }
|
||||
],
|
||||
properties: ['openFile']
|
||||
if (!warningMsg)
|
||||
{
|
||||
warningMsg = warningMsgs.pop();
|
||||
}
|
||||
|
||||
if (!confirm(warningMsg))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var lastDir = localStorage.getItem('.lastPluginDir');
|
||||
|
||||
var paths = await requestSync({
|
||||
action: 'showOpenDialog',
|
||||
defaultPath: lastDir || (await requestSync('getDocumentsFolder')),
|
||||
filters: [
|
||||
{ name: 'draw.io Plugins', extensions: ['js'] },
|
||||
{ name: 'All Files', extensions: ['*'] }
|
||||
],
|
||||
properties: ['openFile']
|
||||
});
|
||||
|
||||
if (paths !== undefined && paths[0] != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
let ret = await requestSync({
|
||||
action: 'installPlugin',
|
||||
filePath: paths[0]
|
||||
});
|
||||
|
||||
localStorage.setItem('.lastPluginDir', ret.selDir);
|
||||
callback(ret.pluginName);
|
||||
editorUi.hideDialog();
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
if (e.message == 'fileExists')
|
||||
{
|
||||
alert(mxResources.get('fileExists'));
|
||||
}
|
||||
else
|
||||
{
|
||||
alert('Adding plugin failed.');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (paths !== undefined && paths[0] != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
let ret = await requestSync({
|
||||
action: 'installPlugin',
|
||||
filePath: paths[0]
|
||||
});
|
||||
|
||||
localStorage.setItem('.lastPluginDir', ret.selDir);
|
||||
callback(ret.pluginName);
|
||||
editorUi.hideDialog();
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
if (e.message == 'fileExists')
|
||||
{
|
||||
alert(mxResources.get('fileExists'));
|
||||
}
|
||||
else
|
||||
{
|
||||
alert('Adding plugin failed.');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
extPluginsBtn.className = 'geBtn';
|
||||
div.appendChild(extPluginsBtn);
|
||||
extPluginsBtn.className = 'geBtn';
|
||||
div.appendChild(extPluginsBtn);
|
||||
}
|
||||
else
|
||||
{
|
||||
title = document.createElement('span');
|
||||
mxUtils.write(title, mxResources.get('pluginsDisabled'));
|
||||
div.appendChild(title);
|
||||
}
|
||||
|
||||
var dlg = new CustomDialog(editorUi, div, mxUtils.bind(this, function()
|
||||
{
|
||||
callback(App.pluginRegistry[pluginsSelect.value]);
|
||||
var newP = App.pluginRegistry[pluginsSelect.value];
|
||||
pluginsMap[newP] = true;
|
||||
callback(newP);
|
||||
}));
|
||||
editorUi.showDialog(dlg.container, 300, 125, true, true);
|
||||
},
|
||||
async function(plugin)
|
||||
{
|
||||
delete pluginsMap[plugin];
|
||||
|
||||
await requestSync({
|
||||
action: 'uninstallPlugin',
|
||||
plugin: plugin
|
||||
});
|
||||
}).container, 360, 225, true, false);
|
||||
}, true).container, 360, 225, true, false);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -829,25 +922,24 @@ mxStencilRegistry.allowEval = false;
|
||||
|
||||
if (file.fileObject != null)
|
||||
{
|
||||
var title = file.fileObject.path;
|
||||
|
||||
if (title.length > 100)
|
||||
{
|
||||
title = '...' + title.substr(title.length - 97);
|
||||
}
|
||||
|
||||
this.addRecent({id: file.fileObject.path, title: title});
|
||||
file.addToRecent();
|
||||
|
||||
await requestSync({
|
||||
action: 'watchFile',
|
||||
path: file.fileObject.path,
|
||||
listener: mxUtils.bind(this, function(curr, prev)
|
||||
{
|
||||
EditorUi.debug('EditorUi.watchFile', [this],
|
||||
'file', [file], 'stat', [file.stat],
|
||||
'curr', [curr], 'prev', [prev],
|
||||
'inConflictState', file.inConflictState,
|
||||
'unwatchedSaves', file.unwatchedSaves);
|
||||
|
||||
//File is changed (not just accessed) && File is not already in a conflict state
|
||||
if (curr.mtimeMs != prev.mtimeMs && !file.inConflictState)
|
||||
{
|
||||
//Ignore our own changes
|
||||
if (file.unwatchedSaves || (file.state != null && file.stat.mtimeMs == curr.mtimeMs))
|
||||
if (file.unwatchedSaves || (file.stat != null && file.stat.mtimeMs == curr.mtimeMs))
|
||||
{
|
||||
file.unwatchedSaves = false;
|
||||
return;
|
||||
@@ -855,26 +947,18 @@ mxStencilRegistry.allowEval = false;
|
||||
|
||||
file.inConflictState = true;
|
||||
|
||||
this.showError(mxResources.get('externalChanges'),
|
||||
mxResources.get('fileChangedSyncDialog'),
|
||||
mxResources.get('synchronize'), mxUtils.bind(this, function()
|
||||
file.addConflictStatus(null, mxUtils.bind(this, function()
|
||||
{
|
||||
file.ui.editor.setStatus(mxUtils.htmlEntities(
|
||||
mxResources.get('updatingDocument')));
|
||||
file.synchronizeFile(mxUtils.bind(this, function()
|
||||
{
|
||||
if (this.spinner.spin(document.body, mxResources.get('updatingDocument')))
|
||||
{
|
||||
file.synchronizeFile(mxUtils.bind(this, function()
|
||||
{
|
||||
this.spinner.stop();
|
||||
}), mxUtils.bind(this, function(err)
|
||||
{
|
||||
file.handleFileError(err, true);
|
||||
}));
|
||||
}
|
||||
}), null, null, null,
|
||||
mxResources.get('cancel'), mxUtils.bind(this, function()
|
||||
file.handleFileSuccess(false);
|
||||
}), mxUtils.bind(this, function(err)
|
||||
{
|
||||
this.hideDialog();
|
||||
file.handleFileError(null, false);
|
||||
}), 340, 150);
|
||||
file.handleFileError(err, true);
|
||||
}));
|
||||
}));
|
||||
}
|
||||
})
|
||||
});
|
||||
@@ -964,6 +1048,21 @@ mxStencilRegistry.allowEval = false;
|
||||
}
|
||||
};
|
||||
|
||||
EditorUi.prototype.normalizeFilename = function(title, defaultExtension)
|
||||
{
|
||||
var tokens = title.split('.');
|
||||
var ext = (tokens.length > 1) ? tokens[tokens.length - 1] : '';
|
||||
defaultExtension = (defaultExtension != null) ? defaultExtension : 'drawio';
|
||||
|
||||
if (tokens.length == 1 || mxUtils.indexOf(['xml',
|
||||
'html', 'drawio', 'png', 'svg'], ext) < 0)
|
||||
{
|
||||
tokens.push(defaultExtension);
|
||||
}
|
||||
|
||||
return tokens.join('.');
|
||||
};
|
||||
|
||||
//In order not to repeat the logic for opening a file, we collect files information here and use them in openLocalFile
|
||||
var origOpenFiles = EditorUi.prototype.openFiles;
|
||||
var openFilesMap = {};
|
||||
@@ -1160,7 +1259,7 @@ mxStencilRegistry.allowEval = false;
|
||||
{
|
||||
this.ui.readGraphFile(mxUtils.bind(this, function(fileEntry, data, stat, name, isModified)
|
||||
{
|
||||
var file = new LocalFile(this, data, '');
|
||||
var file = new LocalFile(this.ui, data, '');
|
||||
file.stat = stat;
|
||||
file.setModified(isModified? true : false);
|
||||
success(file);
|
||||
@@ -1276,6 +1375,12 @@ mxStencilRegistry.allowEval = false;
|
||||
|
||||
LocalFile.prototype.save = function(revision, success, error, unloading, overwrite)
|
||||
{
|
||||
if (!this.isEditable())
|
||||
{
|
||||
this.saveAs(this.title, success, error);
|
||||
return;
|
||||
}
|
||||
|
||||
DrawioFile.prototype.save.apply(this, [revision, mxUtils.bind(this, function()
|
||||
{
|
||||
this.saveFile(revision, mxUtils.bind(this, function()
|
||||
@@ -1307,23 +1412,6 @@ mxStencilRegistry.allowEval = false;
|
||||
this.editable = editable;
|
||||
};
|
||||
|
||||
LocalFile.prototype.getFilename = function()
|
||||
{
|
||||
var filename = this.title;
|
||||
|
||||
// Adds default extension
|
||||
if (filename.length > 0 && (!/(\.xml)$/i.test(filename) && !/(\.html)$/i.test(filename) &&
|
||||
!/(\.svg)$/i.test(filename) && !/(\.png)$/i.test(filename) && !/(\.drawio)$/i.test(filename)))
|
||||
{
|
||||
filename += '.drawio';
|
||||
}
|
||||
|
||||
return filename;
|
||||
};
|
||||
|
||||
// Prototype inheritance needs new functions to be added to subclasses
|
||||
LocalLibrary.prototype.getFilename = LocalFile.prototype.getFilename;
|
||||
|
||||
LocalFile.prototype.saveFile = async function(revision, success, error, unloading, overwrite)
|
||||
{
|
||||
//Safeguard in case saveFile is called from online code in the future
|
||||
@@ -1420,83 +1508,114 @@ mxStencilRegistry.allowEval = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (this.fileObject == null)
|
||||
try
|
||||
{
|
||||
var lastDir = localStorage.getItem('.lastSaveDir');
|
||||
var name = this.getFilename();
|
||||
var ext = null;
|
||||
|
||||
if (name != null)
|
||||
if (this.fileObject == null)
|
||||
{
|
||||
var idx = name.lastIndexOf('.');
|
||||
var lastDir = localStorage.getItem('.lastSaveDir');
|
||||
var name = this.ui.normalizeFilename(this.getTitle(),
|
||||
this.constructor == LocalLibrary ? 'xml' : null);
|
||||
var ext = null;
|
||||
|
||||
if (idx > 0)
|
||||
if (name != null)
|
||||
{
|
||||
ext = name.substring(idx + 1);
|
||||
name = name.substring(0, idx);
|
||||
var idx = name.lastIndexOf('.');
|
||||
|
||||
if (idx > 0)
|
||||
{
|
||||
ext = name.substring(idx + 1);
|
||||
}
|
||||
}
|
||||
|
||||
var path = await requestSync({
|
||||
action: 'showSaveDialog',
|
||||
defaultPath: (lastDir || (await requestSync('getDocumentsFolder'))) + '/' + name,
|
||||
filters: this.ui.createFileSystemFilters(ext)
|
||||
});
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
localStorage.setItem('.lastSaveDir', await requestSync({action: 'dirname', path: path}));
|
||||
this.fileObject = new Object();
|
||||
this.fileObject.path = path;
|
||||
this.fileObject.name = path.replace(/^.*[\\\/]/, '');
|
||||
this.fileObject.type = 'utf-8';
|
||||
this.title = this.fileObject.name;
|
||||
this.addToRecent();
|
||||
fn();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ui.spinner.stop();
|
||||
}
|
||||
}
|
||||
|
||||
var path = await requestSync({
|
||||
action: 'showSaveDialog',
|
||||
defaultPath: (lastDir || (await requestSync('getDocumentsFolder'))) + '/' + name,
|
||||
filters: this.ui.createFileSystemFilters(ext)
|
||||
});
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
localStorage.setItem('.lastSaveDir', await requestSync({action: 'dirname', path: path}));
|
||||
this.fileObject = new Object();
|
||||
this.fileObject.path = path;
|
||||
this.fileObject.name = path.replace(/^.*[\\\/]/, '');
|
||||
this.fileObject.type = 'utf-8';
|
||||
else
|
||||
{
|
||||
fn();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ui.spinner.stop();
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (e)
|
||||
{
|
||||
fn();
|
||||
error(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
LocalFile.prototype.saveAs = async function(title, success, error)
|
||||
LocalFile.prototype.addToRecent = function()
|
||||
{
|
||||
var lastDir = localStorage.getItem('.lastSaveDir');
|
||||
var name = this.getFilename();
|
||||
var ext = null;
|
||||
if (this.fileObject == null) return;
|
||||
|
||||
if (name == '' && this.fileObject != null && this.fileObject.name != null)
|
||||
var title = this.fileObject.path;
|
||||
|
||||
if (title.length > 100)
|
||||
{
|
||||
name = this.fileObject.name;
|
||||
var idx = name.lastIndexOf('.');
|
||||
|
||||
if (idx > 0)
|
||||
{
|
||||
ext = name.substring(idx + 1);
|
||||
name = name.substring(0, idx);
|
||||
}
|
||||
title = '...' + title.substr(title.length - 97);
|
||||
}
|
||||
|
||||
var path = await requestSync({
|
||||
action: 'showSaveDialog',
|
||||
defaultPath: (lastDir || (await requestSync('getDocumentsFolder'))) + '/' + name,
|
||||
filters: this.ui.createFileSystemFilters(ext)
|
||||
});
|
||||
this.ui.addRecent({id: this.fileObject.path, title: title});
|
||||
};
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
localStorage.setItem('.lastSaveDir', await requestSync({action: 'dirname', path: path}));
|
||||
this.fileObject = new Object();
|
||||
this.fileObject.path = path;
|
||||
this.fileObject.name = path.replace(/^.*[\\\/]/, '');
|
||||
this.fileObject.type = 'utf-8';
|
||||
this.setEditable(true); //In case original file is read only
|
||||
this.save(false, success, error, null, true);
|
||||
LocalFile.prototype.saveAs = async function(title, success, error)
|
||||
{
|
||||
try
|
||||
{
|
||||
var lastDir = localStorage.getItem('.lastSaveDir');
|
||||
var name = this.ui.normalizeFilename(this.getTitle(),
|
||||
this.constructor == LocalLibrary ? 'xml' : null);
|
||||
var ext = null;
|
||||
|
||||
if (name != null)
|
||||
{
|
||||
var idx = name.lastIndexOf('.');
|
||||
|
||||
if (idx > 0)
|
||||
{
|
||||
ext = name.substring(idx + 1);
|
||||
}
|
||||
}
|
||||
|
||||
var path = await requestSync({
|
||||
action: 'showSaveDialog',
|
||||
defaultPath: (lastDir || (await requestSync('getDocumentsFolder'))) + '/' + name,
|
||||
filters: this.ui.createFileSystemFilters(ext)
|
||||
});
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
localStorage.setItem('.lastSaveDir', await requestSync({action: 'dirname', path: path}));
|
||||
this.fileObject = new Object();
|
||||
this.fileObject.path = path;
|
||||
this.fileObject.name = path.replace(/^.*[\\\/]/, '');
|
||||
this.fileObject.type = 'utf-8';
|
||||
this.title = this.fileObject.name;
|
||||
this.addToRecent();
|
||||
this.setEditable(true); //In case original file is read only
|
||||
this.save(false, success, error, null, true);
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
error(e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1543,6 +1662,11 @@ mxStencilRegistry.allowEval = false;
|
||||
}
|
||||
};
|
||||
|
||||
LocalLibrary.prototype.addToRecent = function()
|
||||
{
|
||||
// do nothing
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads the given file handle as a local file.
|
||||
*/
|
||||
@@ -1710,155 +1834,16 @@ mxStencilRegistry.allowEval = false;
|
||||
electron.sendMessage('toggleSpellCheck');
|
||||
}
|
||||
|
||||
var origUpdateHeader = App.prototype.updateHeader;
|
||||
|
||||
App.prototype.updateHeader = function()
|
||||
App.prototype.toggleStoreBkp = function()
|
||||
{
|
||||
origUpdateHeader.apply(this, arguments);
|
||||
|
||||
if (urlParams['winCtrls'] != '1')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
document.querySelectorAll('.geStatus').forEach(i => i.style.webkitAppRegion = 'no-drag');
|
||||
var menubarContainer = document.querySelector('.geMenubarContainer');
|
||||
|
||||
if (urlParams['sketch'] == '1')
|
||||
{
|
||||
menubarContainer = this.titlebar;
|
||||
}
|
||||
|
||||
menubarContainer.style.webkitAppRegion = 'drag';
|
||||
|
||||
//Add window control buttons
|
||||
this.windowControls = document.createElement('div');
|
||||
this.windowControls.id = 'geWindow-controls';
|
||||
this.windowControls.innerHTML =
|
||||
'<div class="button" id="min-button">' +
|
||||
' <svg width="10" height="1" viewBox="0 0 11 1">' +
|
||||
' <path d="m11 0v1h-11v-1z" stroke-width=".26208"/>' +
|
||||
' </svg>' +
|
||||
'</div>' +
|
||||
'<div class="button" id="max-button">' +
|
||||
' <svg width="10" height="10" viewBox="0 0 10 10">' +
|
||||
' <path d="m10-1.6667e-6v10h-10v-10zm-1.001 1.001h-7.998v7.998h7.998z" stroke-width=".25" />' +
|
||||
' </svg>' +
|
||||
'</div>' +
|
||||
'<div class="button" id="restore-button">' +
|
||||
' <svg width="10" height="10" viewBox="0 0 11 11">' +
|
||||
' <path' +
|
||||
' d="m11 8.7978h-2.2021v2.2022h-8.7979v-8.7978h2.2021v-2.2022h8.7979zm-3.2979-5.5h-6.6012v6.6011h6.6012zm2.1968-2.1968h-6.6012v1.1011h5.5v5.5h1.1011z"' +
|
||||
' stroke-width=".275" />' +
|
||||
' </svg>' +
|
||||
'</div>' +
|
||||
'<div class="button" id="close-button">' +
|
||||
' <svg width="10" height="10" viewBox="0 0 12 12">' +
|
||||
' <path' +
|
||||
' d="m6.8496 6 5.1504 5.1504-0.84961 0.84961-5.1504-5.1504-5.1504 5.1504-0.84961-0.84961 5.1504-5.1504-5.1504-5.1504 0.84961-0.84961 5.1504 5.1504 5.1504-5.1504 0.84961 0.84961z"' +
|
||||
' stroke-width=".3" />' +
|
||||
' </svg>' +
|
||||
'</div>';
|
||||
|
||||
if (uiTheme == 'atlas')
|
||||
{
|
||||
this.windowControls.style.top = '9px';
|
||||
}
|
||||
else if (urlParams['sketch'] == '1')
|
||||
{
|
||||
this.windowControls.style.top = '-1px';
|
||||
}
|
||||
|
||||
menubarContainer.appendChild(this.windowControls);
|
||||
|
||||
var handleDarkModeChange = mxUtils.bind(this, function ()
|
||||
{
|
||||
if (uiTheme == 'atlas' || Editor.isDarkMode())
|
||||
{
|
||||
this.windowControls.style.fill = 'white';
|
||||
document.querySelectorAll('#geWindow-controls .button').forEach(b => b.className = 'button dark');
|
||||
}
|
||||
else
|
||||
{
|
||||
this.windowControls.style.fill = '#999';
|
||||
document.querySelectorAll('#geWindow-controls .button').forEach(b => b.className = 'button white');
|
||||
}
|
||||
});
|
||||
|
||||
handleDarkModeChange();
|
||||
this.addListener('darkModeChanged', handleDarkModeChange);
|
||||
|
||||
if (this.appIcon != null)
|
||||
{
|
||||
this.appIcon.style.webkitAppRegion = 'no-drag';
|
||||
}
|
||||
|
||||
if (this.menubar != null)
|
||||
{
|
||||
this.menubar.container.style.webkitAppRegion = 'no-drag';
|
||||
|
||||
if (uiTheme == 'atlas')
|
||||
{
|
||||
this.menubar.container.style.width = 'fit-content';
|
||||
}
|
||||
|
||||
if (this.menubar.langIcon != null)
|
||||
{
|
||||
this.menubar.langIcon.parentNode.removeChild(this.menubar.langIcon);
|
||||
menubarContainer.appendChild(this.menubar.langIcon);
|
||||
}
|
||||
}
|
||||
|
||||
window.onbeforeunload = async (event) => {
|
||||
/* If window is reloaded, remove win event listeners
|
||||
(DOM element listeners get auto garbage collected but not
|
||||
Electron win listeners as the win is not dereferenced unless closed) */
|
||||
await requestSync({action: 'windowAction', method: 'removeAllListeners'});
|
||||
}
|
||||
|
||||
// Make minimise/maximise/restore/close buttons work when they are clicked
|
||||
document.getElementById('min-button').addEventListener("click", async event => {
|
||||
await requestSync({action: 'windowAction', method: 'minimize'});
|
||||
});
|
||||
|
||||
document.getElementById('max-button').addEventListener("click", async event => {
|
||||
await requestSync({action: 'windowAction', method: 'maximize'});
|
||||
});
|
||||
|
||||
document.getElementById('restore-button').addEventListener("click", async event => {
|
||||
await requestSync({action: 'windowAction', method: 'unmaximize'});
|
||||
});
|
||||
|
||||
document.getElementById('close-button').addEventListener("click", async event => {
|
||||
await requestSync({action: 'windowAction', method: 'close'});
|
||||
});
|
||||
|
||||
// Toggle maximise/restore buttons when maximisation/unmaximisation occurs
|
||||
toggleMaxRestoreButtons();
|
||||
electron.registerMsgListener('maximize', toggleMaxRestoreButtons)
|
||||
electron.registerMsgListener('unmaximize', toggleMaxRestoreButtons)
|
||||
electron.registerMsgListener('resize', toggleMaxRestoreButtons)
|
||||
|
||||
async function toggleMaxRestoreButtons() {
|
||||
if (await requestSync({action: 'windowAction', method: 'isMaximized'})) {
|
||||
document.body.classList.add('geMaximized');
|
||||
} else {
|
||||
document.body.classList.remove('geMaximized');
|
||||
}
|
||||
}
|
||||
electron.sendMessage('toggleStoreBkp');
|
||||
}
|
||||
|
||||
var origUpdateDocumentTitle = App.prototype.updateDocumentTitle;
|
||||
|
||||
App.prototype.updateDocumentTitle = function()
|
||||
App.prototype.openDevTools = function()
|
||||
{
|
||||
origUpdateDocumentTitle.apply(this, arguments);
|
||||
electron.sendMessage('openDevTools');
|
||||
}
|
||||
|
||||
if (this.titlebar != null && this.titlebar.firstChild != null)
|
||||
{
|
||||
this.titlebar.firstChild.innerHTML = mxUtils.htmlEntities(document.title);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Copies the given cells and XML to the clipboard as an embedded image.
|
||||
*/
|
||||
@@ -1961,80 +1946,13 @@ mxStencilRegistry.allowEval = false;
|
||||
}
|
||||
|
||||
//Direct export to pdf
|
||||
EditorUi.prototype.createDownloadRequest = function(filename, format, ignoreSelection, base64, transparent,
|
||||
currentPage, scale, border, grid, includeXml)
|
||||
EditorUi.prototype.createDownloadRequest = function(filename, format, ignoreSelection,
|
||||
base64, transparent, currentPage, scale, border, grid, includeXml, pageRange, w, h)
|
||||
{
|
||||
var graph = this.editor.graph;
|
||||
var bounds = graph.getGraphBounds();
|
||||
var params = this.downloadRequestBuilder(filename, format, ignoreSelection,
|
||||
base64, transparent, currentPage, scale, border, grid, includeXml, pageRange, w, h);
|
||||
|
||||
// Exports only current page for images that does not contain file data, but for
|
||||
// the other formats with XML included or pdf with all pages, we need to send the complete data and use
|
||||
// the from/to URL parameters to specify the page to be exported.
|
||||
var data = this.getFileData(true, null, null, null, ignoreSelection, currentPage == false? false : format != 'xmlpng');
|
||||
var range = null;
|
||||
var allPages = null;
|
||||
|
||||
var embed = (includeXml) ? '1' : '0';
|
||||
|
||||
if (format == 'pdf' && currentPage == false)
|
||||
{
|
||||
allPages = '1';
|
||||
}
|
||||
|
||||
if (format == 'xmlpng')
|
||||
{
|
||||
embed = '1';
|
||||
format = 'png';
|
||||
|
||||
// Finds the current page number
|
||||
if (this.pages != null && this.currentPage != null)
|
||||
{
|
||||
for (var i = 0; i < this.pages.length; i++)
|
||||
{
|
||||
if (this.pages[i] == this.currentPage)
|
||||
{
|
||||
range = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var bg = graph.background;
|
||||
|
||||
if (format == 'png' && transparent)
|
||||
{
|
||||
bg = mxConstants.NONE;
|
||||
}
|
||||
else if (!transparent && (bg == null || bg == mxConstants.NONE))
|
||||
{
|
||||
bg = '#ffffff';
|
||||
}
|
||||
|
||||
var extras = {globalVars: graph.getExportVariables()};
|
||||
|
||||
if (grid)
|
||||
{
|
||||
extras.grid = {
|
||||
size: graph.gridSize,
|
||||
steps: graph.view.gridSteps,
|
||||
color: graph.view.gridColor
|
||||
};
|
||||
}
|
||||
|
||||
return new mxElectronRequest('export', {
|
||||
format: format,
|
||||
xml: data,
|
||||
from: range,
|
||||
bg: (bg != null) ? bg : mxConstants.NONE,
|
||||
filename: (filename != null) ? filename : null,
|
||||
allPages: allPages,
|
||||
base64: base64,
|
||||
embedXml: embed,
|
||||
extras: encodeURIComponent(JSON.stringify(extras)),
|
||||
scale: scale,
|
||||
border: border
|
||||
});
|
||||
return new mxElectronRequest('export', params);
|
||||
};
|
||||
|
||||
var origSetAutosave = Editor.prototype.setAutosave;
|
||||
@@ -2082,6 +2000,11 @@ mxStencilRegistry.allowEval = false;
|
||||
{
|
||||
var extras = {globalVars: graph.getExportVariables()};
|
||||
|
||||
if (Graph.translateDiagram)
|
||||
{
|
||||
extras.diagramLanguage = Graph.diagramLanguage;
|
||||
}
|
||||
|
||||
editorUi.saveRequest(name, format,
|
||||
function(newTitle, base64)
|
||||
{
|
||||
|
||||
@@ -151,6 +151,38 @@ server {
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_pass http://drawio-export:8000/;
|
||||
}
|
||||
|
||||
location /minder/ {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Real-PORT $remote_port;
|
||||
proxy_set_header X-Forwarded-Host $the_host;
|
||||
proxy_set_header X-Forwarded-Proto $the_scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header Scheme $scheme;
|
||||
proxy_set_header Server-Protocol $server_protocol;
|
||||
proxy_set_header Server-Name $server_name;
|
||||
proxy_set_header Server-Addr $server_addr;
|
||||
proxy_set_header Server-Port $server_port;
|
||||
proxy_pass http://minder/;
|
||||
}
|
||||
|
||||
# 审批
|
||||
location /approve/ {
|
||||
proxy_pass http://approve/;
|
||||
}
|
||||
location /approve/api/ {
|
||||
auth_request /approveAuth;
|
||||
proxy_pass http://approve/api/;
|
||||
}
|
||||
location /approveAuth {
|
||||
internal;
|
||||
proxy_set_header Content-Type "application/json";
|
||||
proxy_set_header Content-Length $request_length;
|
||||
proxy_pass http://service/api/approve/verifyToken;
|
||||
}
|
||||
}
|
||||
|
||||
include /etc/nginx/conf.d/conf.d/*.conf;
|
||||
|
||||
1079
docker/office/resources/api/documents/api.js
vendored
1079
docker/office/resources/api/documents/api.js
vendored
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user